Initial commit
[profile/ivi/bc.git] / bc / execute.c
1 /*  This file is part of GNU bc.
2
3     Copyright (C) 1991-1994, 1997, 2006 Free Software Foundation, Inc.
4
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.
9
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.
14
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
20
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
27        
28 *************************************************************************/
29 /* execute.c - run a bc program. */
30
31 #include "bcdefs.h"
32 #include <signal.h>
33 #include "proto.h"
34
35
36 /* The SIGINT interrupt handling routine. */
37
38 int had_sigint;
39
40 void
41 stop_execution (sig)
42      int sig;
43 {
44   had_sigint = TRUE;
45 }
46
47
48 /* Get the current byte and advance the PC counter. */
49
50 unsigned char
51 byte (p)
52      program_counter *p;
53 {
54   return (functions[p->pc_func].f_body[p->pc_addr++]);
55 }
56
57
58 /* The routine that actually runs the machine. */
59
60 void
61 execute ()
62 {
63   int label_num, l_gp, l_off;
64   bc_label_group *gp;
65   
66   char inst, ch;
67   int  new_func;
68   int  var_name;
69
70   int const_base;
71
72   bc_num temp_num;
73   arg_list *auto_list;
74
75   /* Initialize this run... */
76   pc.pc_func = 0;
77   pc.pc_addr = 0;
78   runtime_error = FALSE;
79   bc_init_num (&temp_num);
80
81   /* Set up the interrupt mechanism for an interactive session. */
82   if (interactive)
83     {
84       signal (SIGINT, stop_execution);
85     }
86    
87   had_sigint = FALSE;
88   while (pc.pc_addr < functions[pc.pc_func].f_code_size
89          && !runtime_error && !had_sigint)
90     {
91       inst = byte(&pc);
92
93 #if DEBUG > 3
94       { /* Print out address and the stack before each instruction.*/
95         int depth; estack_rec *temp = ex_stack;
96         
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);
99         else
100           {
101             depth = 1;
102             while (temp != NULL)
103               {
104                 printf ("  %d = ", depth);
105                 bc_out_num (temp->s_num, 10, out_char, std_only);
106                 depth++;
107                 temp = temp->s_next;
108               }
109             out_char ('\n');
110           }
111       }
112 #endif
113
114     switch ( inst )
115       {
116
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);
122         break;
123
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);
127         pop ();
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];
138         }
139         break;
140
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);
146
147         /* Check to make sure it is defined. */
148         if (!functions[new_func].f_defined)
149           {
150             rt_error ("Function %s not defined.", f_names[new_func]);
151             break;
152           }
153
154         /* Check and push parameters. */
155         process_params (&pc, new_func);
156
157         /* Push auto variables. */
158         for (auto_list = functions[new_func].f_autos;
159              auto_list != NULL;
160              auto_list = auto_list->next)
161           auto_var (auto_list->av_name);
162
163         /* Push pc and ibase. */
164         fpush (pc.pc_func);
165         fpush (pc.pc_addr);
166         fpush (i_base);
167
168         /* Reset pc to start of function. */
169         pc.pc_func = new_func;
170         pc.pc_addr = 0;
171         break;
172
173       case 'D' : /* Duplicate top of stack */
174         push_copy (ex_stack->s_num);
175         break;
176
177       case 'K' : /* Push a constant */
178         /* Get the input base and convert it to a bc number. */
179         if (pc.pc_func == 0) 
180           const_base = i_base;
181         else
182           const_base = fn_stack->s_val;
183         if (const_base == 10)
184           push_b10_const (&pc);
185         else
186           push_constant (prog_char, const_base);
187         break;
188
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);
194         break;
195
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);
201         break;
202
203       case 'O' : /* Write a string to the output with processing. */
204         while ((ch = byte(&pc)) != '"')
205           if (ch != '\\')
206             out_schar (ch);
207           else
208             {
209               ch = byte(&pc);
210               if (ch == '"') break;
211               switch (ch)
212                 {
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;
221                 default:  break;
222                 }
223             }
224         fflush (stdout);
225         break;
226
227       case 'R' : /* Return from function */
228         if (pc.pc_func != 0)
229           {
230             /* "Pop" autos and parameters. */
231             pop_vars(functions[pc.pc_func].f_autos);
232             pop_vars(functions[pc.pc_func].f_params);
233             /* reset the pc. */
234             fpop ();
235             pc.pc_addr = fpop ();
236             pc.pc_func = fpop ();
237           }
238         else
239           rt_error ("Return from main program.");
240         break;
241
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);
247         break;
248
249       case 'T' : /* Test tos for zero */
250         c_code = bc_is_zero (ex_stack->s_num);
251         assign (c_code);
252         break;
253
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". */
259         fflush (stdout);
260         pop ();
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             bc_int2num (&ex_stack->s_num, ex_stack->s_num->n_scale);
274           else
275             bc_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           bc_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         case 'X': /* Random function. */
293           push_copy (_zero_);
294           bc_int2num (&ex_stack->s_num, random());
295           break;
296         }
297         break;
298
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);
303         decr_var (var_name);
304         break;
305       
306       case 'h' : /* Halt the machine. */
307         exit (0);
308
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);
313         incr_var (var_name);
314         break;
315
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);
320         load_var (var_name);
321         break;
322
323       case 'n' : /* Negate top of stack. */
324         bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num, 0);
325         break;
326
327       case 'p' : /* Pop the execution stack. */
328         pop ();
329         break;
330
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);
336         break;
337
338       case 'w' : /* Write a string to the output. */
339         while ((ch = byte(&pc)) != '"') out_schar (ch);
340         fflush (stdout);
341         break;
342                    
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;
348         }
349         break;
350
351       case '0' : /* Load Constant 0. */
352         push_copy (_zero_);
353         break;
354
355       case '1' : /* Load Constant 1. */
356         push_copy (_one_);
357         break;
358
359       case '!' : /* Negate the boolean value on top of the stack. */
360         c_code = bc_is_zero (ex_stack->s_num);
361         assign (c_code);
362         break;
363
364       case '&' : /* compare greater than */
365         if (check_stack(2))
366           {
367             c_code = !bc_is_zero (ex_stack->s_next->s_num)
368               && !bc_is_zero (ex_stack->s_num);
369             pop ();
370             assign (c_code);
371           }
372         break;
373
374       case '|' : /* compare greater than */
375         if (check_stack(2))
376           {
377             c_code = !bc_is_zero (ex_stack->s_next->s_num)
378               || !bc_is_zero (ex_stack->s_num);
379             pop ();
380             assign (c_code);
381           }
382         break;
383
384       case '+' : /* add */
385         if (check_stack(2))
386           {
387             bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0);
388             pop();
389             pop();
390             push_num (temp_num);
391             bc_init_num (&temp_num);
392           }
393         break;
394
395       case '-' : /* subtract */
396         if (check_stack(2))
397           {
398             bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num, 0);
399             pop();
400             pop();
401             push_num (temp_num);
402             bc_init_num (&temp_num);
403           }
404         break;
405
406       case '*' : /* multiply */
407         if (check_stack(2))
408           {
409             bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num,
410                          &temp_num, scale);
411             pop();
412             pop();
413             push_num (temp_num);
414             bc_init_num (&temp_num);
415           }
416         break;
417
418       case '/' : /* divide */
419         if (check_stack(2))
420           {
421             if (bc_divide (ex_stack->s_next->s_num,
422                            ex_stack->s_num, &temp_num, scale) == 0)
423               {
424                 pop();
425                 pop();
426                 push_num (temp_num);
427                 bc_init_num (&temp_num);
428               }
429             else
430               rt_error ("Divide by zero");
431           }
432         break;
433
434       case '%' : /* remainder */
435         if (check_stack(2))
436           {
437             if (bc_is_zero (ex_stack->s_num))
438               rt_error ("Modulo by zero");
439             else
440               {
441                 bc_modulo (ex_stack->s_next->s_num,
442                            ex_stack->s_num, &temp_num, scale);
443                 pop();
444                 pop();
445                 push_num (temp_num);
446                 bc_init_num (&temp_num);
447               }
448           }
449         break;
450
451       case '^' : /* raise */
452         if (check_stack(2))
453           {
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");
458             pop();
459             pop();
460             push_num (temp_num);
461             bc_init_num (&temp_num);
462           }
463         break;
464
465       case '=' : /* compare equal */
466         if (check_stack(2))
467           {
468             c_code = bc_compare (ex_stack->s_next->s_num,
469                                  ex_stack->s_num) == 0;
470             pop ();
471             assign (c_code);
472           }
473         break;
474
475       case '#' : /* compare not equal */
476         if (check_stack(2))
477           {
478             c_code = bc_compare (ex_stack->s_next->s_num,
479                                  ex_stack->s_num) != 0;
480             pop ();
481             assign (c_code);
482           }
483         break;
484
485       case '<' : /* compare less than */
486         if (check_stack(2))
487           {
488             c_code = bc_compare (ex_stack->s_next->s_num,
489                                  ex_stack->s_num) == -1;
490             pop ();
491             assign (c_code);
492           }
493         break;
494
495       case '{' : /* compare less than or equal */
496         if (check_stack(2))
497           {
498             c_code = bc_compare (ex_stack->s_next->s_num,
499                                  ex_stack->s_num) <= 0;
500             pop ();
501             assign (c_code);
502           }
503         break;
504
505       case '>' : /* compare greater than */
506         if (check_stack(2))
507           {
508             c_code = bc_compare (ex_stack->s_next->s_num,
509                                  ex_stack->s_num) == 1;
510             pop ();
511             assign (c_code);
512           }
513         break;
514
515       case '}' : /* compare greater than or equal */
516         if (check_stack(2))
517           {
518             c_code = bc_compare (ex_stack->s_next->s_num,
519                                  ex_stack->s_num) >= 0;
520             pop ();
521             assign (c_code);
522           }
523         break;
524
525         default  : /* error! */
526           rt_error ("bad instruction: inst=%c", inst);
527       }
528     }
529
530   /* Clean up the function stack and pop all autos/parameters. */
531   while (pc.pc_func != 0)
532     {
533       pop_vars(functions[pc.pc_func].f_autos);
534       pop_vars(functions[pc.pc_func].f_params);
535       fpop ();
536       pc.pc_addr = fpop ();
537       pc.pc_func = fpop ();
538     }
539
540   /* Clean up the execution stack. */ 
541   while (ex_stack != NULL) pop();
542
543   /* Clean up the interrupt stuff. */
544   if (interactive)
545     {
546       signal (SIGINT, use_quit);
547       if (had_sigint)
548         printf ("\ninterrupted execution.\n");
549     }
550 }
551
552
553 /* Prog_char gets another byte from the program.  It is used for
554    conversion of text constants in the code to numbers. */
555
556 int
557 prog_char ()
558 {
559   return (int) byte(&pc);
560 }
561
562
563 /* Read a character from the standard input.  This function is used
564    by the "read" function. */
565
566 int
567 input_char ()
568 {
569   int in_ch;
570   
571   /* Get a character from the standard input for the read function. */
572   in_ch = getchar();
573
574   /* Check for a \ quoted newline. */
575   if (in_ch == '\\')
576     {
577       in_ch = getchar();
578       if (in_ch == '\n') {
579           in_ch = getchar();
580           out_col = 0;  /* Saw a new line */
581         }
582     }
583
584   /* Classify and preprocess the input character. */
585   if (isdigit(in_ch))
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 == '-')
592     return (in_ch);
593   if (in_ch <= ' ')
594     return (' ');
595   
596   return (':');
597 }
598
599
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. */
603
604 void
605 push_constant (in_char, conv_base)
606    int (*in_char)(VOID);
607    int conv_base;
608 {
609   int digits;
610   bc_num build, temp, result, mult, divisor;
611   int   in_ch, first_ch;
612   char  negative;
613
614   /* Initialize all bc numbers */
615   bc_init_num (&temp);
616   bc_init_num (&result);
617   bc_init_num (&mult);
618   build = bc_copy_num (_zero_);
619   negative = FALSE;
620
621   /* The conversion base. */
622   bc_int2num (&mult, conv_base);
623   
624   /* Get things ready. */
625   in_ch = in_char();
626   while (in_ch == ' ')
627     in_ch = in_char();
628
629   if (in_ch == '+')
630     in_ch = in_char();
631   else
632     if (in_ch == '-')
633       {
634         negative = TRUE;
635         in_ch = in_char();
636       }
637
638   /* Check for the special case of a single digit. */
639   if (in_ch < 16)
640     {
641       first_ch = in_ch;
642       in_ch = in_char();
643       if (in_ch < 16 && first_ch >= conv_base)
644         first_ch = conv_base - 1;
645       bc_int2num (&build, (int) first_ch);
646     }
647
648   /* Convert the integer part. */
649   while (in_ch < 16)
650     {
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);
655       in_ch = in_char();
656     }
657   if (in_ch == '.')
658     {
659       in_ch = in_char();
660       if (in_ch >= conv_base) in_ch = conv_base-1;
661       bc_free_num (&result);
662       bc_free_num (&temp);
663       divisor = bc_copy_num (_one_);
664       result = bc_copy_num (_zero_);
665       digits = 0;
666       while (in_ch < 16)
667         {
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);
672           digits++;
673           in_ch = in_char();
674           if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1;
675         }
676       bc_divide (result, divisor, &result, digits);
677       bc_add (build, result, &build, 0);
678     }
679   
680   /* Final work.  */
681   if (negative)
682     bc_sub (_zero_, build, &build, 0);
683
684   push_num (build);
685   bc_free_num (&temp);
686   bc_free_num (&result);
687   bc_free_num (&mult);
688 }
689
690
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
694    the constant. */
695
696 void
697 push_b10_const (progctr)
698      program_counter *progctr;
699 {
700   bc_num build;
701   program_counter look_pc;
702   int kdigits, kscale;
703   unsigned char inchar;
704   char *ptr;
705   
706   /* Count the digits and get things ready. */
707   look_pc = *progctr;
708   kdigits = 0;
709   kscale  = 0;
710   inchar = byte (&look_pc);
711   while (inchar != '.' && inchar != ':')
712     {
713       kdigits++;
714       inchar = byte(&look_pc);
715     }
716   if (inchar == '.' )
717     {
718       inchar = byte(&look_pc);
719       while (inchar != ':')
720         {
721           kscale++;
722           inchar = byte(&look_pc);
723         }
724     }
725
726   /* Get the first character again and move the progctr. */
727   inchar = byte(progctr);
728   
729   /* Secial cases of 0, 1, and A-F single inputs. */
730   if (kdigits == 1 && kscale == 0)
731     {
732       if (inchar == 0)
733         {
734           push_copy (_zero_);
735           inchar = byte(progctr);
736           return;
737         }
738       if (inchar == 1) {
739       push_copy (_one_);
740       inchar = byte(progctr);
741       return;
742     }
743     if (inchar > 9)
744       {
745         bc_init_num (&build);
746         bc_int2num (&build, inchar);
747         push_num (build);
748         inchar = byte(progctr);
749         return;
750       }
751     }
752
753   /* Build the new number. */
754   if (kdigits == 0)
755     {
756       build = bc_new_num (1,kscale);
757       ptr = build->n_value;
758       *ptr++ = 0;
759     }
760   else
761     {
762       build = bc_new_num (kdigits,kscale);
763       ptr = build->n_value;
764     }
765
766   while (inchar != ':')
767     {
768       if (inchar != '.')
769         {
770           if (inchar > 9)
771             *ptr++ = 9;
772           else
773             *ptr++ = inchar;
774         }
775       inchar = byte(progctr);
776     }
777   push_num (build);
778 }
779
780
781 /* Put the correct value on the stack for C_CODE.  Frees TOS num. */
782
783 void
784 assign (code)
785      char code;
786 {
787   bc_free_num (&ex_stack->s_num);
788   if (code)
789     ex_stack->s_num = bc_copy_num (_one_);
790   else
791     ex_stack->s_num = bc_copy_num (_zero_);
792 }
793