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