home *** CD-ROM | disk | FTP | other *** search
- /* util.c: Utility routines for bc. */
-
- /* This file is part of bc written for MINIX.
- Copyright (C) 1991, 1992 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License , or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- You may contact the author by:
- e-mail: phil@cs.wwu.edu
- us-mail: Philip A. Nelson
- Computer Science Department, 9062
- Western Washington University
- Bellingham, WA 98226-9062
-
- *************************************************************************/
-
-
- #include "bcdefs.h"
- #ifndef VARARGS
- #include <stdarg.h>
- #else
- #include <varargs.h>
- #endif
- #include "global.h"
- #include "proto.h"
-
-
- /* strcopyof mallocs new memory and copies a string to to the new
- memory. */
-
- char *
- strcopyof (str)
- char *str;
- {
- char *temp;
-
- temp = (char *) bc_malloc (strlen (str)+1);
- return (strcpy (temp,str));
- }
-
-
- /* nextarg adds another value to the list of arguments. */
-
- arg_list *
- nextarg (args, val)
- arg_list *args;
- char val;
- { arg_list *temp;
-
- temp = (arg_list *) bc_malloc (sizeof (arg_list));
- temp->av_name = val;
- temp->next = args;
-
- return (temp);
- }
-
-
- /* For generate, we must produce a string in the form
- "val,val,...,val". We also need a couple of static variables
- for retaining old generated strings. It also uses a recursive
- function that builds the string. */
-
- static char *arglist1 = NULL, *arglist2 = NULL;
-
-
- /* make_arg_str does the actual construction of the argument string.
- ARGS is the pointer to the list and LEN is the maximum number of
- characters needed. 1 char is the minimum needed. COMMAS tells
- if each number should be seperated by commas.*/
-
- _PROTOTYPE (static char *make_arg_str, (arg_list *args, int len, int commas));
-
- static char *
- make_arg_str (args, len, commas)
- arg_list *args;
- int len;
- int commas;
- {
- char *temp;
- char sval[20];
-
- /* Recursive call. */
- if (args != NULL)
- temp = make_arg_str (args->next, len+11, commas);
- else
- {
- temp = (char *) bc_malloc (len);
- *temp = 0;
- return temp;
- }
-
- /* Add the current number to the end of the string. */
- if (len != 1 && commas)
- sprintf (sval, "%d,", args->av_name);
- else
- sprintf (sval, "%d", args->av_name);
- temp = strcat (temp, sval);
- return (temp);
- }
-
- char *
- arg_str (args, commas)
- arg_list *args;
- int commas;
- {
- if (arglist2 != NULL)
- free (arglist2);
- arglist2 = arglist1;
- arglist1 = make_arg_str (args, 1, commas);
- return (arglist1);
- }
-
-
- /* free_args frees an argument list ARGS. */
-
- void
- free_args (args)
- arg_list *args;
- {
- arg_list *temp;
-
- temp = args;
- while (temp != NULL)
- {
- args = args->next;
- free (temp);
- temp = args;
- }
- }
-
-
- /* Check for valid parameter (PARAMS) and auto (AUTOS) lists.
- There must be no duplicates any where. Also, this is where
- warnings are generated for array parameters. */
-
- void
- check_params ( params, autos )
- arg_list *params, *autos;
- {
- arg_list *tmp1, *tmp2;
-
- /* Check for duplicate parameters. */
- if (params != NULL)
- {
- tmp1 = params;
- while (tmp1 != NULL)
- {
- tmp2 = tmp1->next;
- while (tmp2 != NULL)
- {
- if (tmp2->av_name == tmp1->av_name)
- yyerror ("duplicate parameter names");
- tmp2 = tmp2->next;
- }
- if (tmp1->av_name < 0)
- warn ("Array parameter");
- tmp1 = tmp1->next;
- }
- }
-
- /* Check for duplicate autos. */
- if (autos != NULL)
- {
- tmp1 = autos;
- while (tmp1 != NULL)
- {
- tmp2 = tmp1->next;
- while (tmp2 != NULL)
- {
- if (tmp2->av_name == tmp1->av_name)
- yyerror ("duplicate auto variable names");
- tmp2 = tmp2->next;
- }
- tmp1 = tmp1->next;
- }
- }
-
- /* Check for duplicate between parameters and autos. */
- if ((params != NULL) && (autos != NULL))
- {
- tmp1 = params;
- while (tmp1 != NULL)
- {
- tmp2 = autos;
- while (tmp2 != NULL)
- {
- if (tmp2->av_name == tmp1->av_name)
- yyerror ("variable in both parameter and auto lists");
- tmp2 = tmp2->next;
- }
- tmp1 = tmp1->next;
- }
- }
- }
-
-
- /* Initialize the code generator the parser. */
-
- void
- init_gen ()
- {
- /* Get things ready. */
- break_label = 0;
- continue_label = 0;
- next_label = 1;
- out_count = 2;
- if (compile_only)
- printf ("@i");
- else
- init_load ();
- had_error = FALSE;
- did_gen = FALSE;
- }
-
-
- /* generate code STR for the machine. */
-
- void
- generate (str)
- char *str;
- {
- did_gen = TRUE;
- if (compile_only)
- {
- printf ("%s",str);
- out_count += strlen(str);
- if (out_count > 60)
- {
- printf ("\n");
- out_count = 0;
- }
- }
- else
- load_code (str);
- }
-
-
- /* Execute the current code as loaded. */
-
- void
- run_code()
- {
- /* If no compile errors run the current code. */
- if (!had_error && did_gen)
- {
- if (compile_only)
- {
- printf ("@r\n");
- out_count = 0;
- }
- else
- execute ();
- }
-
- /* Reinitialize the code generation and machine. */
- if (did_gen)
- init_gen();
- else
- had_error = FALSE;
- }
-
-
- /* Output routines: Write a character CH to the standard output.
- It keeps track of the number of characters output and may
- break the output with a "\<cr>". */
-
- void
- out_char (ch)
- char ch;
- {
- if (ch == '\n')
- {
- out_col = 0;
- putchar ('\n');
- }
- else
- {
- out_col++;
- if (out_col == 70)
- {
- putchar ('\\');
- putchar ('\n');
- out_col = 1;
- }
- putchar (ch);
- }
- }
-
-
- /* The following are "Symbol Table" routines for the parser. */
-
- /* find_id returns a pointer to node in TREE that has the correct
- ID. If there is no node in TREE with ID, NULL is returned. */
-
- id_rec *
- find_id (tree, id)
- id_rec *tree;
- char *id;
- {
- int cmp_result;
-
- /* Check for an empty tree. */
- if (tree == NULL)
- return NULL;
-
- /* Recursively search the tree. */
- cmp_result = strcmp (id, tree->id);
- if (cmp_result == 0)
- return tree; /* This is the item. */
- else if (cmp_result < 0)
- return find_id (tree->left, id);
- else
- return find_id (tree->right, id);
- }
-
-
- /* insert_id_rec inserts a NEW_ID rec into the tree whose ROOT is
- provided. insert_id_rec returns TRUE if the tree height from
- ROOT down is increased otherwise it returns FALSE. This is a
- recursive balanced binary tree insertion algorithm. */
-
- int insert_id_rec (root, new_id)
- id_rec **root;
- id_rec *new_id;
- {
- id_rec *A, *B;
-
- /* If root is NULL, this where it is to be inserted. */
- if (*root == NULL)
- {
- *root = new_id;
- new_id->left = NULL;
- new_id->right = NULL;
- new_id->balance = 0;
- return (TRUE);
- }
-
- /* We need to search for a leaf. */
- if (strcmp (new_id->id, (*root)->id) < 0)
- {
- /* Insert it on the left. */
- if (insert_id_rec (&((*root)->left), new_id))
- {
- /* The height increased. */
- (*root)->balance --;
-
- switch ((*root)->balance)
- {
- case 0: /* no height increase. */
- return (FALSE);
- case -1: /* height increase. */
- return (FALSE);
- case -2: /* we need to do a rebalancing act. */
- A = *root;
- B = (*root)->left;
- if (B->balance <= 0)
- {
- /* Single Rotate. */
- A->left = B->right;
- B->right = A;
- *root = B;
- A->balance = 0;
- B->balance = 0;
- }
- else
- {
- /* Double Rotate. */
- *root = B->right;
- B->right = (*root)->left;
- A->left = (*root)->right;
- (*root)->left = B;
- (*root)->right = A;
- switch ((*root)->balance)
- {
- case -1:
- A->balance = 1;
- B->balance = 0;
- break;
- case 0:
- A->balance = 0;
- B->balance = 0;
- break;
- case 1:
- A->balance = 0;
- B->balance = -1;
- break;
- }
- (*root)->balance = 0;
- }
- }
- }
- }
- else
- {
- /* Insert it on the right. */
- if (insert_id_rec (&((*root)->right), new_id))
- {
- /* The height increased. */
- (*root)->balance ++;
- switch ((*root)->balance)
- {
- case 0: /* no height increase. */
- return (FALSE);
- case 1: /* height increase. */
- return (FALSE);
- case 2: /* we need to do a rebalancing act. */
- A = *root;
- B = (*root)->right;
- if (B->balance >= 0)
- {
- /* Single Rotate. */
- A->right = B->left;
- B->left = A;
- *root = B;
- A->balance = 0;
- B->balance = 0;
- }
- else
- {
- /* Double Rotate. */
- *root = B->left;
- B->left = (*root)->right;
- A->right = (*root)->left;
- (*root)->left = A;
- (*root)->right = B;
- switch ((*root)->balance)
- {
- case -1:
- A->balance = 0;
- B->balance = 1;
- break;
- case 0:
- A->balance = 0;
- B->balance = 0;
- break;
- case 1:
- A->balance = -1;
- B->balance = 0;
- break;
- }
- (*root)->balance = 0;
- }
- }
- }
- }
-
- /* If we fall through to here, the tree did not grow in height. */
- return (FALSE);
- }
-
-
- /* Initialize variables for the symbol table tree. */
-
- void
- init_tree()
- {
- name_tree = NULL;
- next_array = 1;
- next_func = 1;
- next_var = 4; /* 0 => ibase, 1 => obase, 2 => scale, 3 => last. */
- }
-
-
- /* Lookup routines for symbol table names. */
-
- int
- lookup (name, namekind)
- char *name;
- int namekind;
- {
- id_rec *id;
-
- /* Warn about non-standard name. */
- if (strlen(name) != 1)
- warn ("multiple letter name - %s", name);
-
- /* Look for the id. */
- id = find_id (name_tree, name);
- if (id == NULL)
- {
- /* We need to make a new item. */
- id = (id_rec *) bc_malloc (sizeof (id_rec));
- id->id = strcopyof (name);
- id->a_name = 0;
- id->f_name = 0;
- id->v_name = 0;
- insert_id_rec (&name_tree, id);
- }
-
- /* Return the correct value. */
- switch (namekind)
- {
-
- case ARRAY:
- /* ARRAY variable numbers are returned as negative numbers. */
- if (id->a_name != 0)
- {
- free (name);
- return (-id->a_name);
- }
- id->a_name = next_array++;
- a_names[id->a_name] = name;
- if (id->a_name < MAX_STORE)
- {
- if (id->a_name >= a_count)
- more_arrays ();
- return (-id->a_name);
- }
- yyerror ("Too many array variables");
- exit (1);
-
- case FUNCT:
- if (id->f_name != 0)
- {
- free(name);
- return (id->f_name);
- }
- id->f_name = next_func++;
- f_names[id->f_name] = name;
- if (id->f_name < MAX_STORE)
- {
- if (id->f_name >= f_count)
- more_functions ();
- return (id->f_name);
- }
- yyerror ("Too many functions");
- exit (1);
-
- case SIMPLE:
- if (id->v_name != 0)
- {
- free(name);
- return (id->v_name);
- }
- id->v_name = next_var++;
- v_names[id->v_name - 1] = name;
- if (id->v_name <= MAX_STORE)
- {
- if (id->v_name >= v_count)
- more_variables ();
- return (id->v_name);
- }
- yyerror ("Too many variables");
- exit (1);
- }
- }
-
-
- /* Print the welcome banner. */
-
- void
- welcome()
- {
- printf ("This is free software with ABSOLUTELY NO WARRANTY.\n");
- printf ("For details type `warranty'. \n");
- }
-
-
- /* Print out the warranty information. */
-
- void
- warranty(prefix)
- char *prefix;
- {
- printf ("\n%s%s\n\n", prefix, BC_VERSION);
- printf ("%s%s%s%s%s%s%s%s%s%s%s",
- " This program is free software; you can redistribute it and/or modify\n",
- " it under the terms of the GNU General Public License as published by\n",
- " the Free Software Foundation; either version 2 of the License , or\n",
- " (at your option) any later version.\n\n",
- " This program is distributed in the hope that it will be useful,\n",
- " but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
- " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n",
- " GNU General Public License for more details.\n\n",
- " You should have received a copy of the GNU General Public License\n",
- " along with this program. If not, write to the Free Software\n",
- " Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n");
- }
-
- /* Print out the limits of this program. */
-
- void
- limits()
- {
- printf ("BC_BASE_MAX = %d\n", BC_BASE_MAX);
- printf ("BC_DIM_MAX = %ld\n", (long) BC_DIM_MAX);
- printf ("BC_SCALE_MAX = %d\n", BC_SCALE_MAX);
- printf ("BC_STRING_MAX = %d\n", BC_STRING_MAX);
- printf ("MAX Exponent = %ld\n", (long) LONG_MAX);
- printf ("MAX code = %ld\n", (long) BC_MAX_SEGS * (long) BC_SEG_SIZE);
- printf ("multiply digits = %ld\n", (long) LONG_MAX / (long) 90);
- printf ("Number of vars = %ld\n", (long) MAX_STORE);
- #ifdef OLD_EQ_OP
- printf ("Old assignment operatiors are valid. (=-, =+, ...)\n");
- #endif
- }
-
- /* bc_malloc will check the return value so all other places do not
- have to do it! SIZE is the number of types to allocate. */
-
- char *
- bc_malloc (size)
- int size;
- {
- char *ptr;
-
- ptr = (char *) malloc (size);
- if (ptr == NULL)
- out_of_memory ();
-
- return ptr;
- }
-
-
- /* The following routines are error routines for various problems. */
-
- /* Malloc could not get enought memory. */
-
- void
- out_of_memory()
- {
- fprintf (stderr, "Fatal error: Out of memory for malloc.\n");
- exit (1);
- }
-
-
-
- /* The standard yyerror routine. Built with variable number of argumnets. */
-
- #ifndef VARARGS
- #ifdef __STDC__
- void
- yyerror (char *str, ...)
- #else
- void
- yyerror (str)
- char *str;
- #endif
- #else
- void
- yyerror (str, va_alist)
- char *str;
- #endif
- {
- char *name;
- va_list args;
-
- #ifndef VARARGS
- va_start (args, str);
- #else
- va_start (args);
- #endif
- if (is_std_in)
- name = "(standard_in)";
- else
- name = g_argv[optind-1];
- fprintf (stderr,"%s %d: ",name,line_no);
- vfprintf (stderr, str, args);
- fprintf (stderr, "\n");
- had_error = TRUE;
- va_end (args);
- }
-
-
- /* The routine to produce warnings about non-standard features
- found during parsing. */
-
- #ifndef VARARGS
- #ifdef __STDC__
- void
- warn (char *mesg, ...)
- #else
- void
- warn (mesg)
- char *mesg;
- #endif
- #else
- void
- warn (mesg, va_alist)
- char *mesg;
- #endif
- {
- char *name;
- va_list args;
-
- #ifndef VARARGS
- va_start (args, mesg);
- #else
- va_start (args);
- #endif
- if (std_only)
- {
- if (is_std_in)
- name = "(standard_in)";
- else
- name = g_argv[optind-1];
- fprintf (stderr,"%s %d: ",name,line_no);
- vfprintf (stderr, mesg, args);
- fprintf (stderr, "\n");
- had_error = TRUE;
- }
- else
- if (warn_not_std)
- {
- if (is_std_in)
- name = "(standard_in)";
- else
- name = g_argv[optind-1];
- fprintf (stderr,"%s %d: (Warning) ",name,line_no);
- vfprintf (stderr, mesg, args);
- fprintf (stderr, "\n");
- }
- va_end (args);
- }
-
- /* Runtime error will print a message and stop the machine. */
-
- #ifndef VARARGS
- #ifdef __STDC__
- void
- rt_error (char *mesg, ...)
- #else
- void
- rt_error (mesg)
- char *mesg;
- #endif
- #else
- void
- rt_error (mesg, va_alist)
- char *mesg;
- #endif
- {
- va_list args;
- char error_mesg [255];
-
- #ifndef VARARGS
- va_start (args, mesg);
- #else
- va_start (args);
- #endif
- vsprintf (error_mesg, mesg, args);
- va_end (args);
-
- fprintf (stderr, "Runtime error (func=%s, adr=%d): %s\n",
- f_names[pc.pc_func], pc.pc_addr, error_mesg);
- runtime_error = TRUE;
- }
-
-
- /* A runtime warning tells of some action taken by the processor that
- may change the program execution but was not enough of a problem
- to stop the execution. */
-
- #ifndef VARARGS
- #ifdef __STDC__
- void
- rt_warn (char *mesg, ...)
- #else
- void
- rt_warn (mesg)
- char *mesg;
- #endif
- #else
- void
- rt_warn (mesg, va_alist)
- char *mesg;
- #endif
- {
- va_list args;
- char error_mesg [255];
-
- #ifndef VARARGS
- va_start (args, mesg);
- #else
- va_start (args);
- #endif
- vsprintf (error_mesg, mesg, args);
- va_end (args);
-
- fprintf (stderr, "Runtime warning (func=%s, adr=%d): %s\n",
- f_names[pc.pc_func], pc.pc_addr, error_mesg);
- }
-