1 /* util.c: Utility routines for bc. */
2
3 /* This file is part of bc written for MINIX.
4 Copyright (C) 1991, 1992 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
30 #include "bcdefs.h"
31 #ifndef VARARGS
32 #include <stdarg.h>
33 #else
34 #include <varargs.h>
35 #endif
36 #include "global.h"
37 #include "proto.h"
38
39
40 /* strcopyof mallocs new memory and copies a string to to the new
41 memory. */
42
43 char *
strcopyof(str)44 strcopyof (str)
45 char *str;
46 {
47 char *temp;
48
49 temp = (char *) bc_malloc (strlen (str)+1);
50 return (strcpy (temp,str));
51 }
52
53
54 /* nextarg adds another value to the list of arguments. */
55
56 arg_list *
nextarg(args,val)57 nextarg (args, val)
58 arg_list *args;
59 char val;
60 { arg_list *temp;
61
62 temp = (arg_list *) bc_malloc (sizeof (arg_list));
63 temp->av_name = val;
64 temp->next = args;
65
66 return (temp);
67 }
68
69
70 /* For generate, we must produce a string in the form
71 "val,val,...,val". We also need a couple of static variables
72 for retaining old generated strings. It also uses a recursive
73 function that builds the string. */
74
75 static char *arglist1 = NULL, *arglist2 = NULL;
76
77
78 /* make_arg_str does the actual construction of the argument string.
79 ARGS is the pointer to the list and LEN is the maximum number of
80 characters needed. 1 char is the minimum needed. COMMAS tells
81 if each number should be seperated by commas.*/
82
83 _PROTOTYPE (static char *make_arg_str, (arg_list *args, int len, int commas));
84
85 static char *
make_arg_str(args,len,commas)86 make_arg_str (args, len, commas)
87 arg_list *args;
88 int len;
89 int commas;
90 {
91 char *temp;
92 char sval[20];
93
94 /* Recursive call. */
95 if (args != NULL)
96 temp = make_arg_str (args->next, len+11, commas);
97 else
98 {
99 temp = (char *) bc_malloc (len);
100 *temp = 0;
101 return temp;
102 }
103
104 /* Add the current number to the end of the string. */
105 if (len != 1 && commas)
106 sprintf (sval, "%d,", args->av_name);
107 else
108 sprintf (sval, "%d", args->av_name);
109 temp = strcat (temp, sval);
110 return (temp);
111 }
112
113 char *
arg_str(args,commas)114 arg_str (args, commas)
115 arg_list *args;
116 int commas;
117 {
118 if (arglist2 != NULL)
119 free (arglist2);
120 arglist2 = arglist1;
121 arglist1 = make_arg_str (args, 1, commas);
122 return (arglist1);
123 }
124
125
126 /* free_args frees an argument list ARGS. */
127
128 void
free_args(args)129 free_args (args)
130 arg_list *args;
131 {
132 arg_list *temp;
133
134 temp = args;
135 while (temp != NULL)
136 {
137 args = args->next;
138 free (temp);
139 temp = args;
140 }
141 }
142
143
144 /* Check for valid parameter (PARAMS) and auto (AUTOS) lists.
145 There must be no duplicates any where. Also, this is where
146 warnings are generated for array parameters. */
147
148 void
check_params(params,autos)149 check_params ( params, autos )
150 arg_list *params, *autos;
151 {
152 arg_list *tmp1, *tmp2;
153
154 /* Check for duplicate parameters. */
155 if (params != NULL)
156 {
157 tmp1 = params;
158 while (tmp1 != NULL)
159 {
160 tmp2 = tmp1->next;
161 while (tmp2 != NULL)
162 {
163 if (tmp2->av_name == tmp1->av_name)
164 yyerror ("duplicate parameter names");
165 tmp2 = tmp2->next;
166 }
167 if (tmp1->av_name < 0)
168 warn ("Array parameter");
169 tmp1 = tmp1->next;
170 }
171 }
172
173 /* Check for duplicate autos. */
174 if (autos != NULL)
175 {
176 tmp1 = autos;
177 while (tmp1 != NULL)
178 {
179 tmp2 = tmp1->next;
180 while (tmp2 != NULL)
181 {
182 if (tmp2->av_name == tmp1->av_name)
183 yyerror ("duplicate auto variable names");
184 tmp2 = tmp2->next;
185 }
186 tmp1 = tmp1->next;
187 }
188 }
189
190 /* Check for duplicate between parameters and autos. */
191 if ((params != NULL) && (autos != NULL))
192 {
193 tmp1 = params;
194 while (tmp1 != NULL)
195 {
196 tmp2 = autos;
197 while (tmp2 != NULL)
198 {
199 if (tmp2->av_name == tmp1->av_name)
200 yyerror ("variable in both parameter and auto lists");
201 tmp2 = tmp2->next;
202 }
203 tmp1 = tmp1->next;
204 }
205 }
206 }
207
208
209 /* Initialize the code generator the parser. */
210
211 void
init_gen()212 init_gen ()
213 {
214 /* Get things ready. */
215 break_label = 0;
216 continue_label = 0;
217 next_label = 1;
218 out_count = 2;
219 if (compile_only)
220 printf ("@i");
221 else
222 init_load ();
223 had_error = FALSE;
224 did_gen = FALSE;
225 }
226
227
228 /* generate code STR for the machine. */
229
230 void
generate(str)231 generate (str)
232 char *str;
233 {
234 did_gen = TRUE;
235 if (compile_only)
236 {
237 printf ("%s",str);
238 out_count += strlen(str);
239 if (out_count > 60)
240 {
241 printf ("\n");
242 out_count = 0;
243 }
244 }
245 else
246 load_code (str);
247 }
248
249
250 /* Execute the current code as loaded. */
251
252 void
run_code()253 run_code()
254 {
255 /* If no compile errors run the current code. */
256 if (!had_error && did_gen)
257 {
258 if (compile_only)
259 {
260 printf ("@r\n");
261 out_count = 0;
262 }
263 else
264 execute ();
265 }
266
267 /* Reinitialize the code generation and machine. */
268 if (did_gen)
269 init_gen();
270 else
271 had_error = FALSE;
272 }
273
274
275 /* Output routines: Write a character CH to the standard output.
276 It keeps track of the number of characters output and may
277 break the output with a "\<cr>". */
278
279 void
out_char(ch)280 out_char (ch)
281 char ch;
282 {
283 if (ch == '\n')
284 {
285 out_col = 0;
286 putchar ('\n');
287 }
288 else
289 {
290 out_col++;
291 if (out_col == 70)
292 {
293 putchar ('\\');
294 putchar ('\n');
295 out_col = 1;
296 }
297 putchar (ch);
298 }
299 }
300
301
302 /* The following are "Symbol Table" routines for the parser. */
303
304 /* find_id returns a pointer to node in TREE that has the correct
305 ID. If there is no node in TREE with ID, NULL is returned. */
306
307 id_rec *
find_id(tree,id)308 find_id (tree, id)
309 id_rec *tree;
310 char *id;
311 {
312 int cmp_result;
313
314 /* Check for an empty tree. */
315 if (tree == NULL)
316 return NULL;
317
318 /* Recursively search the tree. */
319 cmp_result = strcmp (id, tree->id);
320 if (cmp_result == 0)
321 return tree; /* This is the item. */
322 else if (cmp_result < 0)
323 return find_id (tree->left, id);
324 else
325 return find_id (tree->right, id);
326 }
327
328
329 /* insert_id_rec inserts a NEW_ID rec into the tree whose ROOT is
330 provided. insert_id_rec returns TRUE if the tree height from
331 ROOT down is increased otherwise it returns FALSE. This is a
332 recursive balanced binary tree insertion algorithm. */
333
insert_id_rec(root,new_id)334 int insert_id_rec (root, new_id)
335 id_rec **root;
336 id_rec *new_id;
337 {
338 id_rec *A, *B;
339
340 /* If root is NULL, this where it is to be inserted. */
341 if (*root == NULL)
342 {
343 *root = new_id;
344 new_id->left = NULL;
345 new_id->right = NULL;
346 new_id->balance = 0;
347 return (TRUE);
348 }
349
350 /* We need to search for a leaf. */
351 if (strcmp (new_id->id, (*root)->id) < 0)
352 {
353 /* Insert it on the left. */
354 if (insert_id_rec (&((*root)->left), new_id))
355 {
356 /* The height increased. */
357 (*root)->balance --;
358
359 switch ((*root)->balance)
360 {
361 case 0: /* no height increase. */
362 return (FALSE);
363 case -1: /* height increase. */
364 return (FALSE);
365 case -2: /* we need to do a rebalancing act. */
366 A = *root;
367 B = (*root)->left;
368 if (B->balance <= 0)
369 {
370 /* Single Rotate. */
371 A->left = B->right;
372 B->right = A;
373 *root = B;
374 A->balance = 0;
375 B->balance = 0;
376 }
377 else
378 {
379 /* Double Rotate. */
380 *root = B->right;
381 B->right = (*root)->left;
382 A->left = (*root)->right;
383 (*root)->left = B;
384 (*root)->right = A;
385 switch ((*root)->balance)
386 {
387 case -1:
388 A->balance = 1;
389 B->balance = 0;
390 break;
391 case 0:
392 A->balance = 0;
393 B->balance = 0;
394 break;
395 case 1:
396 A->balance = 0;
397 B->balance = -1;
398 break;
399 }
400 (*root)->balance = 0;
401 }
402 }
403 }
404 }
405 else
406 {
407 /* Insert it on the right. */
408 if (insert_id_rec (&((*root)->right), new_id))
409 {
410 /* The height increased. */
411 (*root)->balance ++;
412 switch ((*root)->balance)
413 {
414 case 0: /* no height increase. */
415 return (FALSE);
416 case 1: /* height increase. */
417 return (FALSE);
418 case 2: /* we need to do a rebalancing act. */
419 A = *root;
420 B = (*root)->right;
421 if (B->balance >= 0)
422 {
423 /* Single Rotate. */
424 A->right = B->left;
425 B->left = A;
426 *root = B;
427 A->balance = 0;
428 B->balance = 0;
429 }
430 else
431 {
432 /* Double Rotate. */
433 *root = B->left;
434 B->left = (*root)->right;
435 A->right = (*root)->left;
436 (*root)->left = A;
437 (*root)->right = B;
438 switch ((*root)->balance)
439 {
440 case -1:
441 A->balance = 0;
442 B->balance = 1;
443 break;
444 case 0:
445 A->balance = 0;
446 B->balance = 0;
447 break;
448 case 1:
449 A->balance = -1;
450 B->balance = 0;
451 break;
452 }
453 (*root)->balance = 0;
454 }
455 }
456 }
457 }
458
459 /* If we fall through to here, the tree did not grow in height. */
460 return (FALSE);
461 }
462
463
464 /* Initialize variables for the symbol table tree. */
465
466 void
init_tree()467 init_tree()
468 {
469 name_tree = NULL;
470 next_array = 1;
471 next_func = 1;
472 next_var = 4; /* 0 => ibase, 1 => obase, 2 => scale, 3 => last. */
473 }
474
475
476 /* Lookup routines for symbol table names. */
477
478 int
lookup(name,namekind)479 lookup (name, namekind)
480 char *name;
481 int namekind;
482 {
483 id_rec *id;
484
485 /* Warn about non-standard name. */
486 if (strlen(name) != 1)
487 warn ("multiple letter name - %s", name);
488
489 /* Look for the id. */
490 id = find_id (name_tree, name);
491 if (id == NULL)
492 {
493 /* We need to make a new item. */
494 id = (id_rec *) bc_malloc (sizeof (id_rec));
495 id->id = strcopyof (name);
496 id->a_name = 0;
497 id->f_name = 0;
498 id->v_name = 0;
499 insert_id_rec (&name_tree, id);
500 }
501
502 /* Return the correct value. */
503 switch (namekind)
504 {
505
506 case ARRAY:
507 /* ARRAY variable numbers are returned as negative numbers. */
508 if (id->a_name != 0)
509 {
510 free (name);
511 return (-id->a_name);
512 }
513 id->a_name = next_array++;
514 a_names[id->a_name] = name;
515 if (id->a_name < MAX_STORE)
516 {
517 if (id->a_name >= a_count)
518 more_arrays ();
519 return (-id->a_name);
520 }
521 yyerror ("Too many array variables");
522 exit (1);
523
524 case FUNCT:
525 if (id->f_name != 0)
526 {
527 free(name);
528 return (id->f_name);
529 }
530 id->f_name = next_func++;
531 f_names[id->f_name] = name;
532 if (id->f_name < MAX_STORE)
533 {
534 if (id->f_name >= f_count)
535 more_functions ();
536 return (id->f_name);
537 }
538 yyerror ("Too many functions");
539 exit (1);
540
541 case SIMPLE:
542 if (id->v_name != 0)
543 {
544 free(name);
545 return (id->v_name);
546 }
547 id->v_name = next_var++;
548 v_names[id->v_name - 1] = name;
549 if (id->v_name <= MAX_STORE)
550 {
551 if (id->v_name >= v_count)
552 more_variables ();
553 return (id->v_name);
554 }
555 yyerror ("Too many variables");
556 exit (1);
557 }
558 }
559
560
561 /* Print the welcome banner. */
562
563 void
welcome()564 welcome()
565 {
566 printf ("This is free software with ABSOLUTELY NO WARRANTY.\n");
567 printf ("For details type `warranty'. \n");
568 }
569
570
571 /* Print out the warranty information. */
572
573 void
warranty(prefix)574 warranty(prefix)
575 char *prefix;
576 {
577 printf ("\n%s%s\n\n", prefix, BC_VERSION);
578 printf ("%s%s%s%s%s%s%s%s%s%s%s",
579 " This program is free software; you can redistribute it and/or modify\n",
580 " it under the terms of the GNU General Public License as published by\n",
581 " the Free Software Foundation; either version 2 of the License , or\n",
582 " (at your option) any later version.\n\n",
583 " This program is distributed in the hope that it will be useful,\n",
584 " but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
585 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n",
586 " GNU General Public License for more details.\n\n",
587 " You should have received a copy of the GNU General Public License\n",
588 " along with this program. If not, write to the Free Software\n",
589 " Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
590 }
591
592 /* Print out the limits of this program. */
593
594 void
limits()595 limits()
596 {
597 printf ("BC_BASE_MAX = %d\n", BC_BASE_MAX);
598 printf ("BC_DIM_MAX = %ld\n", (long) BC_DIM_MAX);
599 printf ("BC_SCALE_MAX = %d\n", BC_SCALE_MAX);
600 printf ("BC_STRING_MAX = %d\n", BC_STRING_MAX);
601 printf ("MAX Exponent = %ld\n", (long) LONG_MAX);
602 printf ("MAX code = %ld\n", (long) BC_MAX_SEGS * (long) BC_SEG_SIZE);
603 printf ("multiply digits = %ld\n", (long) LONG_MAX / (long) 90);
604 printf ("Number of vars = %ld\n", (long) MAX_STORE);
605 #ifdef OLD_EQ_OP
606 printf ("Old assignment operatiors are valid. (=-, =+, ...)\n");
607 #endif
608 }
609
610 /* bc_malloc will check the return value so all other places do not
611 have to do it! SIZE is the number of types to allocate. */
612
613 char *
bc_malloc(size)614 bc_malloc (size)
615 int size;
616 {
617 char *ptr;
618
619 ptr = (char *) malloc (size);
620 if (ptr == NULL)
621 out_of_memory ();
622
623 return ptr;
624 }
625
626
627 /* The following routines are error routines for various problems. */
628
629 /* Malloc could not get enought memory. */
630
631 void
out_of_memory()632 out_of_memory()
633 {
634 fprintf (stderr, "Fatal error: Out of memory for malloc.\n");
635 exit (1);
636 }
637
638
639
640 /* The standard yyerror routine. Built with variable number of argumnets. */
641
642 #ifndef VARARGS
643 #ifdef __STDC__
644 void
yyerror(char * str,...)645 yyerror (char *str, ...)
646 #else
647 void
648 yyerror (str)
649 char *str;
650 #endif
651 #else
652 void
653 yyerror (str, va_alist)
654 char *str;
655 #endif
656 {
657 char *name;
658 va_list args;
659
660 #ifndef VARARGS
661 va_start (args, str);
662 #else
663 va_start (args);
664 #endif
665 if (is_std_in)
666 name = "(standard_in)";
667 else
668 name = g_argv[optind-1];
669 fprintf (stderr,"%s %d: ",name,line_no);
670 vfprintf (stderr, str, args);
671 fprintf (stderr, "\n");
672 had_error = TRUE;
673 va_end (args);
674 }
675
676
677 /* The routine to produce warnings about non-standard features
678 found during parsing. */
679
680 #ifndef VARARGS
681 #ifdef __STDC__
682 void
warn(char * mesg,...)683 warn (char *mesg, ...)
684 #else
685 void
686 warn (mesg)
687 char *mesg;
688 #endif
689 #else
690 void
691 warn (mesg, va_alist)
692 char *mesg;
693 #endif
694 {
695 char *name;
696 va_list args;
697
698 #ifndef VARARGS
699 va_start (args, mesg);
700 #else
701 va_start (args);
702 #endif
703 if (std_only)
704 {
705 if (is_std_in)
706 name = "(standard_in)";
707 else
708 name = g_argv[optind-1];
709 fprintf (stderr,"%s %d: ",name,line_no);
710 vfprintf (stderr, mesg, args);
711 fprintf (stderr, "\n");
712 had_error = TRUE;
713 }
714 else
715 if (warn_not_std)
716 {
717 if (is_std_in)
718 name = "(standard_in)";
719 else
720 name = g_argv[optind-1];
721 fprintf (stderr,"%s %d: (Warning) ",name,line_no);
722 vfprintf (stderr, mesg, args);
723 fprintf (stderr, "\n");
724 }
725 va_end (args);
726 }
727
728 /* Runtime error will print a message and stop the machine. */
729
730 #ifndef VARARGS
731 #ifdef __STDC__
732 void
rt_error(char * mesg,...)733 rt_error (char *mesg, ...)
734 #else
735 void
736 rt_error (mesg)
737 char *mesg;
738 #endif
739 #else
740 void
741 rt_error (mesg, va_alist)
742 char *mesg;
743 #endif
744 {
745 va_list args;
746 char error_mesg [255];
747
748 #ifndef VARARGS
749 va_start (args, mesg);
750 #else
751 va_start (args);
752 #endif
753 vsprintf (error_mesg, mesg, args);
754 va_end (args);
755
756 fprintf (stderr, "Runtime error (func=%s, adr=%d): %s\n",
757 f_names[pc.pc_func], pc.pc_addr, error_mesg);
758 runtime_error = TRUE;
759 }
760
761
762 /* A runtime warning tells of some action taken by the processor that
763 may change the program execution but was not enough of a problem
764 to stop the execution. */
765
766 #ifndef VARARGS
767 #ifdef __STDC__
768 void
rt_warn(char * mesg,...)769 rt_warn (char *mesg, ...)
770 #else
771 void
772 rt_warn (mesg)
773 char *mesg;
774 #endif
775 #else
776 void
777 rt_warn (mesg, va_alist)
778 char *mesg;
779 #endif
780 {
781 va_list args;
782 char error_mesg [255];
783
784 #ifndef VARARGS
785 va_start (args, mesg);
786 #else
787 va_start (args);
788 #endif
789 vsprintf (error_mesg, mesg, args);
790 va_end (args);
791
792 fprintf (stderr, "Runtime warning (func=%s, adr=%d): %s\n",
793 f_names[pc.pc_func], pc.pc_addr, error_mesg);
794 }
795