386BSD 0.1 development
[unix-history] / usr / othersrc / public / bc-1.01 / execute.c
CommitLineData
f87489ac
WJ
1/* execute.c - run a bc program. */
2
3/* This file is part of bc written for MINIX.
4 Copyright (C) 1991 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 <setjmp.h>
32#include "global.h"
33#include "proto.h"
34
35/* Local variable for SIGINT interrupt of an execution. */
36static jmp_buf env;
37
38
39/* The SIGINT interrupt handling routine. */
40
41void
42stop_execution (sig)
43 int sig;
44{
45 printf ("\n");
46 rt_error ("interrupted execution");
47 longjmp (env, 1); /* Jump to the main code. */
48}
49
50
51/* Get the current byte and advance the PC counter. */
52
53unsigned char
54byte (pc)
55 program_counter *pc;
56{
57 int seg, offset;
58
59 seg = pc->pc_addr >> BC_SEG_LOG;
60 offset = pc->pc_addr++ % BC_SEG_SIZE;
61 return (functions[pc->pc_func].f_body[seg][offset]);
62}
63
64
65/* The routine that actually runs the machine. */
66
67void
68execute ()
69{
70 int label_num, l_gp, l_off;
71 bc_label_group *gp;
72
73 char inst, ch;
74 int new_func;
75 int var_name;
76
77 int const_base;
78
79 bc_num temp_num;
80 arg_list *auto_list;
81
82 /* Initialize this run... */
83 pc.pc_func = 0;
84 pc.pc_addr = 0;
85 runtime_error = FALSE;
86 init_num (&temp_num);
87
88 /* Set up the interrupt mechanism for an interactive session. */
89 if (interactive)
90 if (setjmp (env) == 0)
91 signal (SIGINT, stop_execution);
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_char (ch);
210 else
211 {
212 ch = byte(&pc);
213 if (ch == '"') break;
214 switch (ch)
215 {
216 case 'n': out_char ('\n'); break;
217 case 't': out_char ('\t'); break;
218 case 'r': out_char ('\r'); break;
219 case 'b': out_char (007); break;
220 case 'f': out_char ('\f'); break;
221 case '\\': out_char ('\\'); break;
222 default: break;
223 }
224 }
225 if (interactive) fflush (stdout);
226 break;
227
228 case 'R' : /* Return from function */
229 if (pc.pc_func != 0)
230 {
231 /* "Pop" autos and parameters. */
232 pop_vars(functions[pc.pc_func].f_autos);
233 pop_vars(functions[pc.pc_func].f_params);
234 /* reset the pc. */
235 fpop ();
236 pc.pc_addr = fpop ();
237 pc.pc_func = fpop ();
238 }
239 else
240 rt_error ("Return from main program.");
241 break;
242
243 case 'S' : /* store array variable */
244 var_name = byte(&pc);
245 if ((var_name & 0x80) != 0)
246 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
247 store_array (var_name);
248 break;
249
250 case 'T' : /* Test tos for zero */
251 c_code = is_zero (ex_stack->s_num);
252 assign (c_code);
253 break;
254
255 case 'W' : /* Write the value on the top of the stack. */
256 case 'P' : /* Write the value on the top of the stack. No newline. */
257 out_num (ex_stack->s_num, o_base, out_char);
258 if (inst == 'W') out_char ('\n');
259 store_var (3); /* Special variable "last". */
260 if (interactive) fflush (stdout);
261 break;
262
263 case 'c' : /* Call special function. */
264 new_func = byte(&pc);
265
266 switch (new_func)
267 {
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 int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
274 else
275 int2num (&ex_stack->s_num, ex_stack->s_num->n_len
276 + ex_stack->s_num->n_scale);
277 break;
278
279 case 'S': /* Scale function. */
280 int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
281 break;
282
283 case 'R': /* Square Root function. */
284 if (!bc_sqrt (&ex_stack->s_num, scale))
285 rt_error ("Square root of a negative number");
286 break;
287
288 case 'I': /* Read function. */
289 push_constant (input_char, i_base);
290 break;
291 }
292 break;
293
294 case 'd' : /* Decrement number */
295 var_name = byte(&pc);
296 if ((var_name & 0x80) != 0)
297 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
298 decr_var (var_name);
299 break;
300
301 case 'h' : /* Halt the machine. */
302 exit (0);
303
304 case 'i' : /* increment number */
305 var_name = byte(&pc);
306 if ((var_name & 0x80) != 0)
307 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
308 incr_var (var_name);
309 break;
310
311 case 'l' : /* load variable */
312 var_name = byte(&pc);
313 if ((var_name & 0x80) != 0)
314 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
315 load_var (var_name);
316 break;
317
318 case 'n' : /* Negate top of stack. */
319 bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num);
320 break;
321
322 case 'p' : /* Pop the execution stack. */
323 pop ();
324 break;
325
326 case 's' : /* store variable */
327 var_name = byte(&pc);
328 if ((var_name & 0x80) != 0)
329 var_name = ((var_name << 8) & 0x7f) + byte(&pc);
330 store_var (var_name);
331 break;
332
333 case 'w' : /* Write a string to the output. */
334 while ((ch = byte(&pc)) != '"') out_char (ch);
335 if (interactive) fflush (stdout);
336 break;
337
338 case 'x' : /* Exchange Top of Stack with the one under the tos. */
339 if (check_stack(2)) {
340 bc_num temp = ex_stack->s_num;
341 ex_stack->s_num = ex_stack->s_next->s_num;
342 ex_stack->s_next->s_num = temp;
343 }
344 break;
345
346 case '0' : /* Load Constant 0. */
347 push_copy (_zero_);
348 break;
349
350 case '1' : /* Load Constant 0. */
351 push_copy (_one_);
352 break;
353
354 case '!' : /* Negate the boolean value on top of the stack. */
355 c_code = is_zero (ex_stack->s_num);
356 assign (c_code);
357 break;
358
359 case '&' : /* compare greater than */
360 if (check_stack(2))
361 {
362 c_code = !is_zero (ex_stack->s_next->s_num)
363 && !is_zero (ex_stack->s_num);
364 pop ();
365 assign (c_code);
366 }
367 break;
368
369 case '|' : /* compare greater than */
370 if (check_stack(2))
371 {
372 c_code = !is_zero (ex_stack->s_next->s_num)
373 || !is_zero (ex_stack->s_num);
374 pop ();
375 assign (c_code);
376 }
377 break;
378
379 case '+' : /* add */
380 if (check_stack(2))
381 {
382 bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
383 pop();
384 pop();
385 push_num (temp_num);
386 init_num (&temp_num);
387 }
388 break;
389
390 case '-' : /* subtract */
391 if (check_stack(2))
392 {
393 bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num);
394 pop();
395 pop();
396 push_num (temp_num);
397 init_num (&temp_num);
398 }
399 break;
400
401 case '*' : /* multiply */
402 if (check_stack(2))
403 {
404 bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num,
405 &temp_num, scale);
406 pop();
407 pop();
408 push_num (temp_num);
409 init_num (&temp_num);
410 }
411 break;
412
413 case '/' : /* divide */
414 if (check_stack(2))
415 {
416 if (bc_divide (ex_stack->s_next->s_num,
417 ex_stack->s_num, &temp_num, scale) == 0)
418 {
419 pop();
420 pop();
421 push_num (temp_num);
422 init_num (&temp_num);
423 }
424 else
425 rt_error ("Divide by zero");
426 }
427 break;
428
429 case '%' : /* remainder */
430 if (check_stack(2))
431 {
432 if (is_zero (ex_stack->s_num))
433 rt_error ("Modulo by zero");
434 else
435 {
436 bc_modulo (ex_stack->s_next->s_num,
437 ex_stack->s_num, &temp_num, scale);
438 pop();
439 pop();
440 push_num (temp_num);
441 init_num (&temp_num);
442 }
443 }
444 break;
445
446 case '^' : /* raise */
447 if (check_stack(2))
448 {
449 bc_raise (ex_stack->s_next->s_num,
450 ex_stack->s_num, &temp_num, scale);
451 if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num))
452 rt_error ("divide by zero");
453 pop();
454 pop();
455 push_num (temp_num);
456 init_num (&temp_num);
457 }
458 break;
459
460 case '=' : /* compare equal */
461 if (check_stack(2))
462 {
463 c_code = bc_compare (ex_stack->s_next->s_num,
464 ex_stack->s_num) == 0;
465 pop ();
466 assign (c_code);
467 }
468 break;
469
470 case '#' : /* compare not equal */
471 if (check_stack(2))
472 {
473 c_code = bc_compare (ex_stack->s_next->s_num,
474 ex_stack->s_num) != 0;
475 pop ();
476 assign (c_code);
477 }
478 break;
479
480 case '<' : /* compare less than */
481 if (check_stack(2))
482 {
483 c_code = bc_compare (ex_stack->s_next->s_num,
484 ex_stack->s_num) == -1;
485 pop ();
486 assign (c_code);
487 }
488 break;
489
490 case '{' : /* compare less than or equal */
491 if (check_stack(2))
492 {
493 c_code = bc_compare (ex_stack->s_next->s_num,
494 ex_stack->s_num) <= 0;
495 pop ();
496 assign (c_code);
497 }
498 break;
499
500 case '>' : /* compare greater than */
501 if (check_stack(2))
502 {
503 c_code = bc_compare (ex_stack->s_next->s_num,
504 ex_stack->s_num) == 1;
505 pop ();
506 assign (c_code);
507 }
508 break;
509
510 case '}' : /* compare greater than or equal */
511 if (check_stack(2))
512 {
513 c_code = bc_compare (ex_stack->s_next->s_num,
514 ex_stack->s_num) >= 0;
515 pop ();
516 assign (c_code);
517 }
518 break;
519
520 default : /* error! */
521 rt_error ("bad instruction: inst=%c", inst);
522 }
523 }
524
525 /* Clean up the interrupt stuff. */
526 if (interactive)
527 signal (SIGINT, use_quit);
528
529 /* Clean up the function stack and pop all autos/parameters. */
530 while (pc.pc_func != 0)
531 {
532 pop_vars(functions[pc.pc_func].f_autos);
533 pop_vars(functions[pc.pc_func].f_params);
534 fpop ();
535 pc.pc_addr = fpop ();
536 pc.pc_func = fpop ();
537 }
538
539 /* Clean up the execution stack. */
540 while (ex_stack != NULL) pop();
541
542}
543
544
545/* Prog_char gets another byte from the program. It is used for
546 conversion of text constants in the code to numbers. */
547
548char
549prog_char ()
550{
551 return byte(&pc);
552}
553
554
555/* Read a character from the standard input. This function is used
556 by the "read" function. */
557
558char
559input_char ()
560{
561 char in_ch;
562
563 /* Get a character from the standard input for the read function. */
564 in_ch = getchar();
565
566 /* Check for a \ quoted newline. */
567 if (in_ch == '\\')
568 {
569 in_ch = getchar();
570 if (in_ch == '\n')
571 in_ch = getchar();
572 }
573
574 /* Classify and preprocess the input character. */
575 if (isdigit(in_ch))
576 return (in_ch - '0');
577 if (in_ch >= 'A' && in_ch <= 'F')
578 return (in_ch + 10 - 'A');
579 if (in_ch >= 'a' && in_ch <= 'f')
580 return (in_ch + 10 - 'a');
581 if (in_ch == '.' || in_ch == '+' || in_ch == '-')
582 return (in_ch);
583 if (in_ch <= ' ')
584 return (' ');
585
586 return (':');
587}
588
589
590/* Push_constant converts a sequence of input characters as returned
591 by IN_CHAR into a number. The number is pushed onto the execution
592 stack. The number is converted as a number in base CONV_BASE. */
593
594void
595push_constant (in_char, conv_base)
596 char (*in_char)(VOID);
597 int conv_base;
598{
599 int digits;
600 bc_num build, temp, result, mult, divisor;
601 char in_ch, first_ch;
602 char negative;
603
604 /* Initialize all bc numbers */
605 init_num (&temp);
606 init_num (&result);
607 init_num (&mult);
608 build = copy_num (_zero_);
609 negative = FALSE;
610
611 /* The conversion base. */
612 int2num (&mult, conv_base);
613
614 /* Get things ready. */
615 in_ch = in_char();
616 while (in_ch == ' ')
617 in_ch = in_char();
618
619 if (in_ch == '+')
620 in_ch = in_char();
621 else
622 if (in_ch == '-')
623 {
624 negative = TRUE;
625 in_ch = in_char();
626 }
627
628 /* Check for the special case of a single digit. */
629 if (in_ch < 16)
630 {
631 first_ch = in_ch;
632 in_ch = in_char();
633 if (in_ch < 16 && first_ch >= conv_base)
634 first_ch = conv_base - 1;
635 int2num (&build, (int) first_ch);
636 }
637
638 /* Convert the integer part. */
639 while (in_ch < 16)
640 {
641 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
642 bc_multiply (build, mult, &result, 0);
643 int2num (&temp, (int) in_ch);
644 bc_add (result, temp, &build);
645 in_ch = in_char();
646 }
647 if (in_ch == '.')
648 {
649 in_ch = in_char();
650 if (in_ch >= conv_base) in_ch = conv_base-1;
651 free_num (&result);
652 free_num (&temp);
653 divisor = copy_num (_one_);
654 result = copy_num (_zero_);
655 digits = 0;
656 while (in_ch < 16)
657 {
658 bc_multiply (result, mult, &result, 0);
659 int2num (&temp, (int) in_ch);
660 bc_add (result, temp, &result);
661 bc_multiply (divisor, mult, &divisor, 0);
662 digits++;
663 in_ch = in_char();
664 if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
665 }
666 bc_divide (result, divisor, &result, digits);
667 bc_add (build, result, &build);
668 }
669
670 /* Final work. */
671 if (negative)
672 bc_sub (_zero_, build, &build);
673
674 push_num (build);
675 free_num (&temp);
676 free_num (&result);
677 free_num (&mult);
678}
679
680
681/* When converting base 10 constants from the program, we use this
682 more efficient way to convert them to numbers. PC tells where
683 the constant starts and is expected to be advanced to after
684 the constant. */
685
686void
687push_b10_const (pc)
688 program_counter *pc;
689{
690 bc_num build;
691 program_counter look_pc;
692 int kdigits, kscale;
693 char inchar;
694 char *ptr;
695
696 /* Count the digits and get things ready. */
697 look_pc = *pc;
698 kdigits = 0;
699 kscale = 0;
700 inchar = byte (&look_pc);
701 while (inchar != '.' && inchar != ':')
702 {
703 kdigits++;
704 inchar = byte(&look_pc);
705 }
706 if (inchar == '.' )
707 {
708 inchar = byte(&look_pc);
709 while (inchar != ':')
710 {
711 kscale++;
712 inchar = byte(&look_pc);
713 }
714 }
715
716 /* Get the first character again and move the pc. */
717 inchar = byte(pc);
718
719 /* Secial cases of 0, 1, and A-F single inputs. */
720 if (kdigits == 1 && kscale == 0)
721 {
722 if (inchar == 0)
723 {
724 push_copy (_zero_);
725 inchar = byte(pc);
726 return;
727 }
728 if (inchar == 1) {
729 push_copy (_one_);
730 inchar = byte(pc);
731 return;
732 }
733 if (inchar > 9)
734 {
735 init_num (&build);
736 int2num (&build, inchar);
737 push_num (build);
738 inchar = byte(pc);
739 return;
740 }
741 }
742
743 /* Build the new number. */
744 if (kdigits == 0)
745 {
746 build = new_num (1,kscale);
747 ptr = build->n_value;
748 *ptr++ = 0;
749 }
750 else
751 {
752 build = new_num (kdigits,kscale);
753 ptr = build->n_value;
754 }
755
756 while (inchar != ':')
757 {
758 if (inchar != '.')
759 if (inchar > 9)
760 *ptr++ = 9;
761 else
762 *ptr++ = inchar;
763 inchar = byte(pc);
764 }
765 push_num (build);
766}
767
768
769/* Put the correct value on the stack for C_CODE. Frees TOS num. */
770
771void
772assign (c_code)
773 char c_code;
774{
775 free_num (&ex_stack->s_num);
776 if (c_code)
777 ex_stack->s_num = copy_num (_one_);
778 else
779 ex_stack->s_num = copy_num (_zero_);
780}