1 /* This file is part of GNU bc.
3 Copyright (C) 1991-1994, 1997, 2006 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License , or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to:
17 The Free Software Foundation, Inc.
18 Foundation, Inc. 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301 USA
21 You may contact the author by:
22 e-mail: philnelson@acm.org
23 us-mail: Philip A. Nelson
24 Computer Science Department, 9062
25 Western Washington University
26 Bellingham, WA 98226-9062
28 *************************************************************************/
29 /* execute.c - run a bc program. */
36 /* The SIGINT interrupt handling routine. */
48 /* Get the current byte and advance the PC counter. */
54 return (functions[p->pc_func].f_body[p->pc_addr++]);
58 /* The routine that actually runs the machine. */
63 int label_num, l_gp, l_off;
75 /* Initialize this run... */
78 runtime_error = FALSE;
79 bc_init_num (&temp_num);
81 /* Set up the interrupt mechanism for an interactive session. */
84 signal (SIGINT, stop_execution);
88 while (pc.pc_addr < functions[pc.pc_func].f_code_size
89 && !runtime_error && !had_sigint)
94 { /* Print out address and the stack before each instruction.*/
95 int depth; estack_rec *temp = ex_stack;
97 printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst);
98 if (temp == NULL) printf ("empty stack.\n", inst);
104 printf (" %d = ", depth);
105 bc_out_num (temp->s_num, 10, out_char, std_only);
117 case 'A' : /* increment array variable (Add one). */
118 var_name = byte(&pc);
119 if ((var_name & 0x80) != 0)
120 var_name = ((var_name & 0x7f) << 8) + byte(&pc);
121 incr_array (var_name);
124 case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */
125 case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */
126 c_code = !bc_is_zero (ex_stack->s_num);
128 case 'J' : /* Jump to a label. */
129 label_num = byte(&pc); /* Low order bits first. */
130 label_num += byte(&pc) << 8;
131 if (inst == 'J' || (inst == 'B' && c_code)
132 || (inst == 'Z' && !c_code)) {
133 gp = functions[pc.pc_func].f_label;
134 l_gp = label_num >> BC_LABEL_LOG;
135 l_off = label_num % BC_LABEL_GROUP;
136 while (l_gp-- > 0) gp = gp->l_next;
137 pc.pc_addr = gp->l_adrs[l_off];
141 case 'C' : /* Call a function. */
142 /* Get the function number. */
143 new_func = byte(&pc);
144 if ((new_func & 0x80) != 0)
145 new_func = ((new_func & 0x7f) << 8) + byte(&pc);
147 /* Check to make sure it is defined. */
148 if (!functions[new_func].f_defined)
150 rt_error ("Function %s not defined.", f_names[new_func]);
154 /* Check and push parameters. */
155 process_params (&pc, new_func);
157 /* Push auto variables. */
158 for (auto_list = functions[new_func].f_autos;
160 auto_list = auto_list->next)
161 auto_var (auto_list->av_name);
163 /* Push pc and ibase. */
168 /* Reset pc to start of function. */
169 pc.pc_func = new_func;
173 case 'D' : /* Duplicate top of stack */
174 push_copy (ex_stack->s_num);
177 case 'K' : /* Push a constant */
178 /* Get the input base and convert it to a bc number. */
182 const_base = fn_stack->s_val;
183 if (const_base == 10)
184 push_b10_const (&pc);
186 push_constant (prog_char, const_base);
189 case 'L' : /* load array variable */
190 var_name = byte(&pc);
191 if ((var_name & 0x80) != 0)
192 var_name = ((var_name & 0x7f) << 8) + byte(&pc);
193 load_array (var_name);
196 case 'M' : /* decrement array variable (Minus!) */
197 var_name = byte(&pc);
198 if ((var_name & 0x80) != 0)
199 var_name = ((var_name & 0x7f) << 8) + byte(&pc);
200 decr_array (var_name);
203 case 'O' : /* Write a string to the output with processing. */
204 while ((ch = byte(&pc)) != '"')
210 if (ch == '"') break;
213 case 'a': out_schar (007); break;
214 case 'b': out_schar ('\b'); break;
215 case 'f': out_schar ('\f'); break;
216 case 'n': out_schar ('\n'); break;
217 case 'q': out_schar ('"'); break;
218 case 'r': out_schar ('\r'); break;
219 case 't': out_schar ('\t'); break;
220 case '\\': out_schar ('\\'); break;
227 case 'R' : /* Return from function */
230 /* "Pop" autos and parameters. */
231 pop_vars(functions[pc.pc_func].f_autos);
232 pop_vars(functions[pc.pc_func].f_params);
235 pc.pc_addr = fpop ();
236 pc.pc_func = fpop ();
239 rt_error ("Return from main program.");
242 case 'S' : /* store array variable */
243 var_name = byte(&pc);
244 if ((var_name & 0x80) != 0)
245 var_name = ((var_name & 0x7f ) << 8) + byte(&pc);
246 store_array (var_name);
249 case 'T' : /* Test tos for zero */
250 c_code = bc_is_zero (ex_stack->s_num);
254 case 'W' : /* Write the value on the top of the stack. */
255 case 'P' : /* Write the value on the top of the stack. No newline. */
256 bc_out_num (ex_stack->s_num, o_base, out_char, std_only);
257 if (inst == 'W') out_char ('\n');
258 store_var (4); /* Special variable "last". */
263 case 'c' : /* Call special function. */
264 new_func = byte(&pc);
268 case 'L': /* Length function. */
269 /* For the number 0.xxxx, 0 is not significant. */
270 if (ex_stack->s_num->n_len == 1 &&
271 ex_stack->s_num->n_scale != 0 &&
272 ex_stack->s_num->n_value[0] == 0 )
273 bc_int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
275 bc_int2num (&ex_stack->s_num, ex_stack->s_num->n_len
276 + ex_stack->s_num->n_scale);
279 case 'S': /* Scale function. */
280 bc_int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
283 case 'R': /* Square Root function. */
284 if (!bc_sqrt (&ex_stack->s_num, scale))
285 rt_error ("Square root of a negative number");
288 case 'I': /* Read function. */
289 push_constant (input_char, i_base);
292 case 'X': /* Random function. */
294 bc_int2num (&ex_stack->s_num, random());
299 case 'd' : /* Decrement number */
300 var_name = byte(&pc);
301 if ((var_name & 0x80) != 0)
302 var_name = ((var_name & 0x7f) << 8) + byte(&pc);
306 case 'h' : /* Halt the machine. */
309 case 'i' : /* increment number */
310 var_name = byte(&pc);
311 if ((var_name & 0x80) != 0)
312 var_name = ((var_name & 0x7f) << 8) + byte(&pc);
316 case 'l' : /* load variable */
317 var_name = byte(&pc);
318 if ((var_name & 0x80) != 0)
319 var_name = ((var_name & 0x7f) << 8) + byte(&pc);
323 case 'n' : /* Negate top of stack. */
324 bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num, 0);
327 case 'p' : /* Pop the execution stack. */
331 case 's' : /* store variable */
332 var_name = byte(&pc);
333 if ((var_name & 0x80) != 0)
334 var_name = ((var_name & 0x7f) << 8) + byte(&pc);
335 store_var (var_name);
338 case 'w' : /* Write a string to the output. */
339 while ((ch = byte(&pc)) != '"') out_schar (ch);
343 case 'x' : /* Exchange Top of Stack with the one under the tos. */
344 if (check_stack(2)) {
345 bc_num temp = ex_stack->s_num;
346 ex_stack->s_num = ex_stack->s_next->s_num;
347 ex_stack->s_next->s_num = temp;
351 case '0' : /* Load Constant 0. */
355 case '1' : /* Load Constant 1. */
359 case '!' : /* Negate the boolean value on top of the stack. */
360 c_code = bc_is_zero (ex_stack->s_num);
364 case '&' : /* compare greater than */
367 c_code = !bc_is_zero (ex_stack->s_next->s_num)
368 && !bc_is_zero (ex_stack->s_num);
374 case '|' : /* compare greater than */
377 c_code = !bc_is_zero (ex_stack->s_next->s_num)
378 || !bc_is_zero (ex_stack->s_num);
387 bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0);
391 bc_init_num (&temp_num);
395 case '-' : /* subtract */
398 bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0);
402 bc_init_num (&temp_num);
406 case '*' : /* multiply */
409 bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num,
414 bc_init_num (&temp_num);
418 case '/' : /* divide */
421 if (bc_divide (ex_stack->s_next->s_num,
422 ex_stack->s_num, &temp_num, scale) == 0)
427 bc_init_num (&temp_num);
430 rt_error ("Divide by zero");
434 case '%' : /* remainder */
437 if (bc_is_zero (ex_stack->s_num))
438 rt_error ("Modulo by zero");
441 bc_modulo (ex_stack->s_next->s_num,
442 ex_stack->s_num, &temp_num, scale);
446 bc_init_num (&temp_num);
451 case '^' : /* raise */
454 bc_raise (ex_stack->s_next->s_num,
455 ex_stack->s_num, &temp_num, scale);
456 if (bc_is_zero (ex_stack->s_next->s_num) && bc_is_neg (ex_stack->s_num))
457 rt_error ("divide by zero");
461 bc_init_num (&temp_num);
465 case '=' : /* compare equal */
468 c_code = bc_compare (ex_stack->s_next->s_num,
469 ex_stack->s_num) == 0;
475 case '#' : /* compare not equal */
478 c_code = bc_compare (ex_stack->s_next->s_num,
479 ex_stack->s_num) != 0;
485 case '<' : /* compare less than */
488 c_code = bc_compare (ex_stack->s_next->s_num,
489 ex_stack->s_num) == -1;
495 case '{' : /* compare less than or equal */
498 c_code = bc_compare (ex_stack->s_next->s_num,
499 ex_stack->s_num) <= 0;
505 case '>' : /* compare greater than */
508 c_code = bc_compare (ex_stack->s_next->s_num,
509 ex_stack->s_num) == 1;
515 case '}' : /* compare greater than or equal */
518 c_code = bc_compare (ex_stack->s_next->s_num,
519 ex_stack->s_num) >= 0;
525 default : /* error! */
526 rt_error ("bad instruction: inst=%c", inst);
530 /* Clean up the function stack and pop all autos/parameters. */
531 while (pc.pc_func != 0)
533 pop_vars(functions[pc.pc_func].f_autos);
534 pop_vars(functions[pc.pc_func].f_params);
536 pc.pc_addr = fpop ();
537 pc.pc_func = fpop ();
540 /* Clean up the execution stack. */
541 while (ex_stack != NULL) pop();
543 /* Clean up the interrupt stuff. */
546 signal (SIGINT, use_quit);
548 printf ("\ninterrupted execution.\n");
553 /* Prog_char gets another byte from the program. It is used for
554 conversion of text constants in the code to numbers. */
559 return (int) byte(&pc);
563 /* Read a character from the standard input. This function is used
564 by the "read" function. */
571 /* Get a character from the standard input for the read function. */
574 /* Check for a \ quoted newline. */
580 out_col = 0; /* Saw a new line */
584 /* Classify and preprocess the input character. */
586 return (in_ch - '0');
587 if (in_ch >= 'A' && in_ch <= 'F')
588 return (in_ch + 10 - 'A');
589 if (in_ch >= 'a' && in_ch <= 'f')
590 return (in_ch + 10 - 'a');
591 if (in_ch == '.' || in_ch == '+' || in_ch == '-')
600 /* Push_constant converts a sequence of input characters as returned
601 by IN_CHAR into a number. The number is pushed onto the execution
602 stack. The number is converted as a number in base CONV_BASE. */
605 push_constant (in_char, conv_base)
606 int (*in_char)(VOID);
610 bc_num build, temp, result, mult, divisor;
614 /* Initialize all bc numbers */
616 bc_init_num (&result);
618 build = bc_copy_num (_zero_);
621 /* The conversion base. */
622 bc_int2num (&mult, conv_base);
624 /* Get things ready. */
638 /* Check for the special case of a single digit. */
643 if (in_ch < 16 && first_ch >= conv_base)
644 first_ch = conv_base - 1;
645 bc_int2num (&build, (int) first_ch);
648 /* Convert the integer part. */
651 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
652 bc_multiply (build, mult, &result, 0);
653 bc_int2num (&temp, (int) in_ch);
654 bc_add (result, temp, &build, 0);
660 if (in_ch >= conv_base) in_ch = conv_base-1;
661 bc_free_num (&result);
663 divisor = bc_copy_num (_one_);
664 result = bc_copy_num (_zero_);
668 bc_multiply (result, mult, &result, 0);
669 bc_int2num (&temp, (int) in_ch);
670 bc_add (result, temp, &result, 0);
671 bc_multiply (divisor, mult, &divisor, 0);
674 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
676 bc_divide (result, divisor, &result, digits);
677 bc_add (build, result, &build, 0);
682 bc_sub (_zero_, build, &build, 0);
686 bc_free_num (&result);
691 /* When converting base 10 constants from the program, we use this
692 more efficient way to convert them to numbers. PC tells where
693 the constant starts and is expected to be advanced to after
697 push_b10_const (progctr)
698 program_counter *progctr;
701 program_counter look_pc;
703 unsigned char inchar;
706 /* Count the digits and get things ready. */
710 inchar = byte (&look_pc);
711 while (inchar != '.' && inchar != ':')
714 inchar = byte(&look_pc);
718 inchar = byte(&look_pc);
719 while (inchar != ':')
722 inchar = byte(&look_pc);
726 /* Get the first character again and move the progctr. */
727 inchar = byte(progctr);
729 /* Secial cases of 0, 1, and A-F single inputs. */
730 if (kdigits == 1 && kscale == 0)
735 inchar = byte(progctr);
740 inchar = byte(progctr);
745 bc_init_num (&build);
746 bc_int2num (&build, inchar);
748 inchar = byte(progctr);
753 /* Build the new number. */
756 build = bc_new_num (1,kscale);
757 ptr = build->n_value;
762 build = bc_new_num (kdigits,kscale);
763 ptr = build->n_value;
766 while (inchar != ':')
775 inchar = byte(progctr);
781 /* Put the correct value on the stack for C_CODE. Frees TOS num. */
787 bc_free_num (&ex_stack->s_num);
789 ex_stack->s_num = bc_copy_num (_one_);
791 ex_stack->s_num = bc_copy_num (_zero_);