1 /* execute.c - run a bc program. */ 2 3 /* This file is part of GNU bc. 4 Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc. 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License , or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; see the file COPYING. If not, write to 18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 19 20 You may contact the author by: 21 e-mail: phil@cs.wwu.edu 22 us-mail: Philip A. Nelson 23 Computer Science Department, 9062 24 Western Washington University 25 Bellingham, WA 98226-9062 26 27 *************************************************************************/ 28 29 #include "bcdefs.h" 30 #include <signal.h> 31 #include "global.h" 32 #include "proto.h" 33 34 35 /* The SIGINT interrupt handling routine. */ 36 37 int had_sigint; 38 39 void 40 stop_execution (sig) 41 int sig; 42 { 43 had_sigint = TRUE; 44 printf ("\n"); 45 rt_error ("interrupted execution"); 46 } 47 48 49 /* Get the current byte and advance the PC counter. */ 50 51 unsigned char 52 byte (pc) 53 program_counter *pc; 54 { 55 int seg, offset; 56 57 seg = pc->pc_addr >> BC_SEG_LOG; 58 offset = pc->pc_addr++ % BC_SEG_SIZE; 59 return (functions[pc->pc_func].f_body[seg][offset]); 60 } 61 62 63 /* The routine that actually runs the machine. */ 64 65 void 66 execute () 67 { 68 int label_num, l_gp, l_off; 69 bc_label_group *gp; 70 71 char inst, ch; 72 int new_func; 73 int var_name; 74 75 int const_base; 76 77 bc_num temp_num; 78 arg_list *auto_list; 79 80 /* Initialize this run... */ 81 pc.pc_func = 0; 82 pc.pc_addr = 0; 83 runtime_error = FALSE; 84 init_num (&temp_num); 85 86 /* Set up the interrupt mechanism for an interactive session. */ 87 if (interactive) 88 { 89 signal (SIGINT, stop_execution); 90 had_sigint = FALSE; 91 } 92 93 while (pc.pc_addr < functions[pc.pc_func].f_code_size && !runtime_error) 94 { 95 inst = byte(&pc); 96 97 #if DEBUG > 3 98 { /* Print out address and the stack before each instruction.*/ 99 int depth; estack_rec *temp = ex_stack; 100 101 printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst); 102 if (temp == NULL) printf ("empty stack.\n", inst); 103 else 104 { 105 depth = 1; 106 while (temp != NULL) 107 { 108 printf (" %d = ", depth); 109 out_num (temp->s_num, 10, out_char); 110 depth++; 111 temp = temp->s_next; 112 } 113 } 114 } 115 #endif 116 117 switch ( inst ) 118 { 119 120 case 'A' : /* increment array variable (Add one). */ 121 var_name = byte(&pc); 122 if ((var_name & 0x80) != 0) 123 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 124 incr_array (var_name); 125 break; 126 127 case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */ 128 case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */ 129 c_code = !is_zero (ex_stack->s_num); 130 pop (); 131 case 'J' : /* Jump to a label. */ 132 label_num = byte(&pc); /* Low order bits first. */ 133 label_num += byte(&pc) << 8; 134 if (inst == 'J' || (inst == 'B' && c_code) 135 || (inst == 'Z' && !c_code)) { 136 gp = functions[pc.pc_func].f_label; 137 l_gp = label_num >> BC_LABEL_LOG; 138 l_off = label_num % BC_LABEL_GROUP; 139 while (l_gp-- > 0) gp = gp->l_next; 140 pc.pc_addr = gp->l_adrs[l_off]; 141 } 142 break; 143 144 case 'C' : /* Call a function. */ 145 /* Get the function number. */ 146 new_func = byte(&pc); 147 if ((new_func & 0x80) != 0) 148 new_func = ((new_func << 8) & 0x7f) + byte(&pc); 149 150 /* Check to make sure it is defined. */ 151 if (!functions[new_func].f_defined) 152 { 153 rt_error ("Function %s not defined.", f_names[new_func]); 154 break; 155 } 156 157 /* Check and push parameters. */ 158 process_params (&pc, new_func); 159 160 /* Push auto variables. */ 161 for (auto_list = functions[new_func].f_autos; 162 auto_list != NULL; 163 auto_list = auto_list->next) 164 auto_var (auto_list->av_name); 165 166 /* Push pc and ibase. */ 167 fpush (pc.pc_func); 168 fpush (pc.pc_addr); 169 fpush (i_base); 170 171 /* Reset pc to start of function. */ 172 pc.pc_func = new_func; 173 pc.pc_addr = 0; 174 break; 175 176 case 'D' : /* Duplicate top of stack */ 177 push_copy (ex_stack->s_num); 178 break; 179 180 case 'K' : /* Push a constant */ 181 /* Get the input base and convert it to a bc number. */ 182 if (pc.pc_func == 0) 183 const_base = i_base; 184 else 185 const_base = fn_stack->s_val; 186 if (const_base == 10) 187 push_b10_const (&pc); 188 else 189 push_constant (prog_char, const_base); 190 break; 191 192 case 'L' : /* load array variable */ 193 var_name = byte(&pc); 194 if ((var_name & 0x80) != 0) 195 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 196 load_array (var_name); 197 break; 198 199 case 'M' : /* decrement array variable (Minus!) */ 200 var_name = byte(&pc); 201 if ((var_name & 0x80) != 0) 202 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 203 decr_array (var_name); 204 break; 205 206 case 'O' : /* Write a string to the output with processing. */ 207 while ((ch = byte(&pc)) != '"') 208 if (ch != '\\') 209 out_schar (ch); 210 else 211 { 212 ch = byte(&pc); 213 if (ch == '"') break; 214 switch (ch) 215 { 216 case 'a': out_schar (007); break; 217 case 'b': out_schar ('\b'); break; 218 case 'f': out_schar ('\f'); break; 219 case 'n': out_schar ('\n'); break; 220 case 'q': out_schar ('"'); break; 221 case 'r': out_schar ('\r'); break; 222 case 't': out_schar ('\t'); break; 223 case '\\': out_schar ('\\'); break; 224 default: break; 225 } 226 } 227 fflush (stdout); 228 break; 229 230 case 'R' : /* Return from function */ 231 if (pc.pc_func != 0) 232 { 233 /* "Pop" autos and parameters. */ 234 pop_vars(functions[pc.pc_func].f_autos); 235 pop_vars(functions[pc.pc_func].f_params); 236 /* reset the pc. */ 237 fpop (); 238 pc.pc_addr = fpop (); 239 pc.pc_func = fpop (); 240 } 241 else 242 rt_error ("Return from main program."); 243 break; 244 245 case 'S' : /* store array variable */ 246 var_name = byte(&pc); 247 if ((var_name & 0x80) != 0) 248 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 249 store_array (var_name); 250 break; 251 252 case 'T' : /* Test tos for zero */ 253 c_code = is_zero (ex_stack->s_num); 254 assign (c_code); 255 break; 256 257 case 'W' : /* Write the value on the top of the stack. */ 258 case 'P' : /* Write the value on the top of the stack. No newline. */ 259 out_num (ex_stack->s_num, o_base, out_char); 260 if (inst == 'W') out_char ('\n'); 261 store_var (4); /* Special variable "last". */ 262 fflush (stdout); 263 pop (); 264 break; 265 266 case 'c' : /* Call special function. */ 267 new_func = byte(&pc); 268 269 switch (new_func) 270 { 271 case 'L': /* Length function. */ 272 /* For the number 0.xxxx, 0 is not significant. */ 273 if (ex_stack->s_num->n_len == 1 && 274 ex_stack->s_num->n_scale != 0 && 275 ex_stack->s_num->n_value[0] == 0 ) 276 int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); 277 else 278 int2num (&ex_stack->s_num, ex_stack->s_num->n_len 279 + ex_stack->s_num->n_scale); 280 break; 281 282 case 'S': /* Scale function. */ 283 int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); 284 break; 285 286 case 'R': /* Square Root function. */ 287 if (!bc_sqrt (&ex_stack->s_num, scale)) 288 rt_error ("Square root of a negative number"); 289 break; 290 291 case 'I': /* Read function. */ 292 push_constant (input_char, i_base); 293 break; 294 } 295 break; 296 297 case 'd' : /* Decrement number */ 298 var_name = byte(&pc); 299 if ((var_name & 0x80) != 0) 300 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 301 decr_var (var_name); 302 break; 303 304 case 'h' : /* Halt the machine. */ 305 exit (0); 306 307 case 'i' : /* increment number */ 308 var_name = byte(&pc); 309 if ((var_name & 0x80) != 0) 310 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 311 incr_var (var_name); 312 break; 313 314 case 'l' : /* load variable */ 315 var_name = byte(&pc); 316 if ((var_name & 0x80) != 0) 317 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 318 load_var (var_name); 319 break; 320 321 case 'n' : /* Negate top of stack. */ 322 bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num, 0); 323 break; 324 325 case 'p' : /* Pop the execution stack. */ 326 pop (); 327 break; 328 329 case 's' : /* store variable */ 330 var_name = byte(&pc); 331 if ((var_name & 0x80) != 0) 332 var_name = ((var_name << 8) & 0x7f) + byte(&pc); 333 store_var (var_name); 334 break; 335 336 case 'w' : /* Write a string to the output. */ 337 while ((ch = byte(&pc)) != '"') out_schar (ch); 338 fflush (stdout); 339 break; 340 341 case 'x' : /* Exchange Top of Stack with the one under the tos. */ 342 if (check_stack(2)) { 343 bc_num temp = ex_stack->s_num; 344 ex_stack->s_num = ex_stack->s_next->s_num; 345 ex_stack->s_next->s_num = temp; 346 } 347 break; 348 349 case '0' : /* Load Constant 0. */ 350 push_copy (_zero_); 351 break; 352 353 case '1' : /* Load Constant 0. */ 354 push_copy (_one_); 355 break; 356 357 case '!' : /* Negate the boolean value on top of the stack. */ 358 c_code = is_zero (ex_stack->s_num); 359 assign (c_code); 360 break; 361 362 case '&' : /* compare greater than */ 363 if (check_stack(2)) 364 { 365 c_code = !is_zero (ex_stack->s_next->s_num) 366 && !is_zero (ex_stack->s_num); 367 pop (); 368 assign (c_code); 369 } 370 break; 371 372 case '|' : /* compare greater than */ 373 if (check_stack(2)) 374 { 375 c_code = !is_zero (ex_stack->s_next->s_num) 376 || !is_zero (ex_stack->s_num); 377 pop (); 378 assign (c_code); 379 } 380 break; 381 382 case '+' : /* add */ 383 if (check_stack(2)) 384 { 385 bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0); 386 pop(); 387 pop(); 388 push_num (temp_num); 389 init_num (&temp_num); 390 } 391 break; 392 393 case '-' : /* subtract */ 394 if (check_stack(2)) 395 { 396 bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0); 397 pop(); 398 pop(); 399 push_num (temp_num); 400 init_num (&temp_num); 401 } 402 break; 403 404 case '*' : /* multiply */ 405 if (check_stack(2)) 406 { 407 bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num, 408 &temp_num, scale); 409 pop(); 410 pop(); 411 push_num (temp_num); 412 init_num (&temp_num); 413 } 414 break; 415 416 case '/' : /* divide */ 417 if (check_stack(2)) 418 { 419 if (bc_divide (ex_stack->s_next->s_num, 420 ex_stack->s_num, &temp_num, scale) == 0) 421 { 422 pop(); 423 pop(); 424 push_num (temp_num); 425 init_num (&temp_num); 426 } 427 else 428 rt_error ("Divide by zero"); 429 } 430 break; 431 432 case '%' : /* remainder */ 433 if (check_stack(2)) 434 { 435 if (is_zero (ex_stack->s_num)) 436 rt_error ("Modulo by zero"); 437 else 438 { 439 bc_modulo (ex_stack->s_next->s_num, 440 ex_stack->s_num, &temp_num, scale); 441 pop(); 442 pop(); 443 push_num (temp_num); 444 init_num (&temp_num); 445 } 446 } 447 break; 448 449 case '^' : /* raise */ 450 if (check_stack(2)) 451 { 452 bc_raise (ex_stack->s_next->s_num, 453 ex_stack->s_num, &temp_num, scale); 454 if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num)) 455 rt_error ("divide by zero"); 456 pop(); 457 pop(); 458 push_num (temp_num); 459 init_num (&temp_num); 460 } 461 break; 462 463 case '=' : /* compare equal */ 464 if (check_stack(2)) 465 { 466 c_code = bc_compare (ex_stack->s_next->s_num, 467 ex_stack->s_num) == 0; 468 pop (); 469 assign (c_code); 470 } 471 break; 472 473 case '#' : /* compare not equal */ 474 if (check_stack(2)) 475 { 476 c_code = bc_compare (ex_stack->s_next->s_num, 477 ex_stack->s_num) != 0; 478 pop (); 479 assign (c_code); 480 } 481 break; 482 483 case '<' : /* compare less than */ 484 if (check_stack(2)) 485 { 486 c_code = bc_compare (ex_stack->s_next->s_num, 487 ex_stack->s_num) == -1; 488 pop (); 489 assign (c_code); 490 } 491 break; 492 493 case '{' : /* compare less than or equal */ 494 if (check_stack(2)) 495 { 496 c_code = bc_compare (ex_stack->s_next->s_num, 497 ex_stack->s_num) <= 0; 498 pop (); 499 assign (c_code); 500 } 501 break; 502 503 case '>' : /* compare greater than */ 504 if (check_stack(2)) 505 { 506 c_code = bc_compare (ex_stack->s_next->s_num, 507 ex_stack->s_num) == 1; 508 pop (); 509 assign (c_code); 510 } 511 break; 512 513 case '}' : /* compare greater than or equal */ 514 if (check_stack(2)) 515 { 516 c_code = bc_compare (ex_stack->s_next->s_num, 517 ex_stack->s_num) >= 0; 518 pop (); 519 assign (c_code); 520 } 521 break; 522 523 default : /* error! */ 524 rt_error ("bad instruction: inst=%c", inst); 525 } 526 } 527 528 /* Clean up the function stack and pop all autos/parameters. */ 529 while (pc.pc_func != 0) 530 { 531 pop_vars(functions[pc.pc_func].f_autos); 532 pop_vars(functions[pc.pc_func].f_params); 533 fpop (); 534 pc.pc_addr = fpop (); 535 pc.pc_func = fpop (); 536 } 537 538 /* Clean up the execution stack. */ 539 while (ex_stack != NULL) pop(); 540 541 /* Clean up the interrupt stuff. */ 542 if (interactive) 543 { 544 signal (SIGINT, use_quit); 545 if (had_sigint) 546 printf ("Interruption completed.\n"); 547 } 548 } 549 550 551 /* Prog_char gets another byte from the program. It is used for 552 conversion of text constants in the code to numbers. */ 553 554 char 555 prog_char () 556 { 557 return byte(&pc); 558 } 559 560 561 /* Read a character from the standard input. This function is used 562 by the "read" function. */ 563 564 char 565 input_char () 566 { 567 char in_ch; 568 569 /* Get a character from the standard input for the read function. */ 570 in_ch = getchar(); 571 572 /* Check for a \ quoted newline. */ 573 if (in_ch == '\\') 574 { 575 in_ch = getchar(); 576 if (in_ch == '\n') 577 in_ch = getchar(); 578 } 579 580 /* Classify and preprocess the input character. */ 581 if (isdigit(in_ch)) 582 return (in_ch - '0'); 583 if (in_ch >= 'A' && in_ch <= 'F') 584 return (in_ch + 10 - 'A'); 585 if (in_ch >= 'a' && in_ch <= 'f') 586 return (in_ch + 10 - 'a'); 587 if (in_ch == '.' || in_ch == '+' || in_ch == '-') 588 return (in_ch); 589 if (in_ch <= ' ') 590 return (' '); 591 592 return (':'); 593 } 594 595 596 /* Push_constant converts a sequence of input characters as returned 597 by IN_CHAR into a number. The number is pushed onto the execution 598 stack. The number is converted as a number in base CONV_BASE. */ 599 600 void 601 push_constant (in_char, conv_base) 602 char (*in_char)(VOID); 603 int conv_base; 604 { 605 int digits; 606 bc_num build, temp, result, mult, divisor; 607 char in_ch, first_ch; 608 char negative; 609 610 /* Initialize all bc numbers */ 611 init_num (&temp); 612 init_num (&result); 613 init_num (&mult); 614 build = copy_num (_zero_); 615 negative = FALSE; 616 617 /* The conversion base. */ 618 int2num (&mult, conv_base); 619 620 /* Get things ready. */ 621 in_ch = in_char(); 622 while (in_ch == ' ') 623 in_ch = in_char(); 624 625 if (in_ch == '+') 626 in_ch = in_char(); 627 else 628 if (in_ch == '-') 629 { 630 negative = TRUE; 631 in_ch = in_char(); 632 } 633 634 /* Check for the special case of a single digit. */ 635 if (in_ch < 16) 636 { 637 first_ch = in_ch; 638 in_ch = in_char(); 639 if (in_ch < 16 && first_ch >= conv_base) 640 first_ch = conv_base - 1; 641 int2num (&build, (int) first_ch); 642 } 643 644 /* Convert the integer part. */ 645 while (in_ch < 16) 646 { 647 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; 648 bc_multiply (build, mult, &result, 0); 649 int2num (&temp, (int) in_ch); 650 bc_add (result, temp, &build, 0); 651 in_ch = in_char(); 652 } 653 if (in_ch == '.') 654 { 655 in_ch = in_char(); 656 if (in_ch >= conv_base) in_ch = conv_base-1; 657 free_num (&result); 658 free_num (&temp); 659 divisor = copy_num (_one_); 660 result = copy_num (_zero_); 661 digits = 0; 662 while (in_ch < 16) 663 { 664 bc_multiply (result, mult, &result, 0); 665 int2num (&temp, (int) in_ch); 666 bc_add (result, temp, &result, 0); 667 bc_multiply (divisor, mult, &divisor, 0); 668 digits++; 669 in_ch = in_char(); 670 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; 671 } 672 bc_divide (result, divisor, &result, digits); 673 bc_add (build, result, &build, 0); 674 } 675 676 /* Final work. */ 677 if (negative) 678 bc_sub (_zero_, build, &build, 0); 679 680 push_num (build); 681 free_num (&temp); 682 free_num (&result); 683 free_num (&mult); 684 } 685 686 687 /* When converting base 10 constants from the program, we use this 688 more efficient way to convert them to numbers. PC tells where 689 the constant starts and is expected to be advanced to after 690 the constant. */ 691 692 void 693 push_b10_const (pc) 694 program_counter *pc; 695 { 696 bc_num build; 697 program_counter look_pc; 698 int kdigits, kscale; 699 char inchar; 700 char *ptr; 701 702 /* Count the digits and get things ready. */ 703 look_pc = *pc; 704 kdigits = 0; 705 kscale = 0; 706 inchar = byte (&look_pc); 707 while (inchar != '.' && inchar != ':') 708 { 709 kdigits++; 710 inchar = byte(&look_pc); 711 } 712 if (inchar == '.' ) 713 { 714 inchar = byte(&look_pc); 715 while (inchar != ':') 716 { 717 kscale++; 718 inchar = byte(&look_pc); 719 } 720 } 721 722 /* Get the first character again and move the pc. */ 723 inchar = byte(pc); 724 725 /* Secial cases of 0, 1, and A-F single inputs. */ 726 if (kdigits == 1 && kscale == 0) 727 { 728 if (inchar == 0) 729 { 730 push_copy (_zero_); 731 inchar = byte(pc); 732 return; 733 } 734 if (inchar == 1) { 735 push_copy (_one_); 736 inchar = byte(pc); 737 return; 738 } 739 if (inchar > 9) 740 { 741 init_num (&build); 742 int2num (&build, inchar); 743 push_num (build); 744 inchar = byte(pc); 745 return; 746 } 747 } 748 749 /* Build the new number. */ 750 if (kdigits == 0) 751 { 752 build = new_num (1,kscale); 753 ptr = build->n_value; 754 *ptr++ = 0; 755 } 756 else 757 { 758 build = new_num (kdigits,kscale); 759 ptr = build->n_value; 760 } 761 762 while (inchar != ':') 763 { 764 if (inchar != '.') 765 if (inchar > 9) 766 *ptr++ = 9; 767 else 768 *ptr++ = inchar; 769 inchar = byte(pc); 770 } 771 push_num (build); 772 } 773 774 775 /* Put the correct value on the stack for C_CODE. Frees TOS num. */ 776 777 void 778 assign (c_code) 779 char c_code; 780 { 781 free_num (&ex_stack->s_num); 782 if (c_code) 783 ex_stack->s_num = copy_num (_one_); 784 else 785 ex_stack->s_num = copy_num (_zero_); 786 } |