diff options
| author | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 | 
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 | 
| commit | a16f65c7d117419bd266c28a1901ef129a337569 (patch) | |
| tree | 2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /gnu/usr.bin/bc/execute.c | |
| parent | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff) | |
Diffstat (limited to 'gnu/usr.bin/bc/execute.c')
| -rw-r--r-- | gnu/usr.bin/bc/execute.c | 783 | 
1 files changed, 783 insertions, 0 deletions
diff --git a/gnu/usr.bin/bc/execute.c b/gnu/usr.bin/bc/execute.c new file mode 100644 index 000000000000..a7bc9c733520 --- /dev/null +++ b/gnu/usr.bin/bc/execute.c @@ -0,0 +1,783 @@ +/* execute.c - run a bc program. */ + +/*  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" +#include <signal.h> +#include "global.h" +#include "proto.h" + + +/* The SIGINT interrupt handling routine. */ + +int had_sigint; + +void +stop_execution (sig) +     int sig; +{ +  had_sigint = TRUE; +  printf ("\n"); +  rt_error ("interrupted execution"); +} + + +/* Get the current byte and advance the PC counter. */ + +unsigned char +byte (pc) +     program_counter *pc; +{ +  int seg, offset; +     +  seg = pc->pc_addr >> BC_SEG_LOG; +  offset = pc->pc_addr++ % BC_SEG_SIZE; +  return (functions[pc->pc_func].f_body[seg][offset]); +} + + +/* The routine that actually runs the machine. */ + +void +execute () +{ +  int label_num, l_gp, l_off; +  bc_label_group *gp; +   +  char inst, ch; +  int  new_func; +  int  var_name; + +  int const_base; + +  bc_num temp_num; +  arg_list *auto_list; + +  /* Initialize this run... */ +  pc.pc_func = 0; +  pc.pc_addr = 0; +  runtime_error = FALSE; +  init_num (&temp_num); + +  /* Set up the interrupt mechanism for an interactive session. */ +  if (interactive) +    { +      signal (SIGINT, stop_execution); +      had_sigint = FALSE; +    } +    +  while (pc.pc_addr < functions[pc.pc_func].f_code_size && !runtime_error) +    { +      inst = byte(&pc); + +#if DEBUG > 3 +      { /* Print out address and the stack before each instruction.*/ +	int depth; estack_rec *temp = ex_stack; +	 +	printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst); +	if (temp == NULL) printf ("empty stack.\n", inst); +	else +	  { +	    depth = 1; +	    while (temp != NULL) +	      { +		printf ("   %d = ", depth); +		out_num (temp->s_num, 10, out_char); +		depth++; +		temp = temp->s_next; +	      } +	  } +      } +#endif + +    switch ( inst ) +      { + +      case 'A' : /* increment array variable (Add one). */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	incr_array (var_name); +	break; + +      case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */ +      case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */ +	c_code = !is_zero (ex_stack->s_num); +	pop (); +      case 'J' : /* Jump to a label. */ +	label_num = byte(&pc);  /* Low order bits first. */ +	label_num += byte(&pc) << 8; +	if (inst == 'J' || (inst == 'B' && c_code) +	    || (inst == 'Z' && !c_code)) { +	  gp = functions[pc.pc_func].f_label; +	  l_gp  = label_num >> BC_LABEL_LOG; +	  l_off = label_num % BC_LABEL_GROUP; +	  while (l_gp-- > 0) gp = gp->l_next; +	  pc.pc_addr = gp->l_adrs[l_off]; +	} +	break; + +      case 'C' : /* Call a function. */ +	/* Get the function number. */ +	new_func = byte(&pc); +	if ((new_func & 0x80) != 0)  +	  new_func = ((new_func << 8) & 0x7f) + byte(&pc); + +	/* Check to make sure it is defined. */ +	if (!functions[new_func].f_defined) +	  { +	    rt_error ("Function %s not defined.", f_names[new_func]); +	    break; +	  } + +	/* Check and push parameters. */ +	process_params (&pc, new_func); + +	/* Push auto variables. */ +	for (auto_list = functions[new_func].f_autos; +	     auto_list != NULL; +	     auto_list = auto_list->next) +	  auto_var (auto_list->av_name); + +	/* Push pc and ibase. */ +	fpush (pc.pc_func); +	fpush (pc.pc_addr); +	fpush (i_base); + +	/* Reset pc to start of function. */ +	pc.pc_func = new_func; +	pc.pc_addr = 0; +	break; + +      case 'D' : /* Duplicate top of stack */ +	push_copy (ex_stack->s_num); +	break; + +      case 'K' : /* Push a constant */ +	/* Get the input base and convert it to a bc number. */ +	if (pc.pc_func == 0)  +	  const_base = i_base; +	else +	  const_base = fn_stack->s_val; +	if (const_base == 10) +	  push_b10_const (&pc); +	else +	  push_constant (prog_char, const_base); +	break; + +      case 'L' : /* load array variable */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	load_array (var_name); +	break; + +      case 'M' : /* decrement array variable (Minus!) */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	decr_array (var_name); +	break; + +      case 'O' : /* Write a string to the output with processing. */ +	while ((ch = byte(&pc)) != '"') +	  if (ch != '\\') +	    out_char (ch); +	  else +	    { +	      ch = byte(&pc); +	      if (ch == '"') break; +	      switch (ch) +		{ +		case 'n':  out_char ('\n'); break; +		case 't':  out_char ('\t'); break; +		case 'r':  out_char ('\r'); break; +		case 'b':  out_char (007); break; +		case 'f':  out_char ('\f'); break; +		case '\\': out_char ('\\'); break; +		default:  break; +		} +	    } +	if (interactive) fflush (stdout); +	break; + +      case 'R' : /* Return from function */ +	if (pc.pc_func != 0) +	  { +	    /* "Pop" autos and parameters. */ +	    pop_vars(functions[pc.pc_func].f_autos); +	    pop_vars(functions[pc.pc_func].f_params); +	    /* reset the pc. */ +	    fpop (); +	    pc.pc_addr = fpop (); +	    pc.pc_func = fpop (); +	  } +	else +	  rt_error ("Return from main program."); +	break; + +      case 'S' : /* store array variable */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	store_array (var_name); +	break; + +      case 'T' : /* Test tos for zero */ +	c_code = is_zero (ex_stack->s_num); +	assign (c_code); +	break; + +      case 'W' : /* Write the value on the top of the stack. */ +      case 'P' : /* Write the value on the top of the stack.  No newline. */ +	out_num (ex_stack->s_num, o_base, out_char); +	if (inst == 'W') out_char ('\n'); +	store_var (3);  /* Special variable "last". */ +	if (interactive) fflush (stdout); +	break; + +      case 'c' : /* Call special function. */ +	new_func = byte(&pc); + +      switch (new_func) +	{ +	case 'L':  /* Length function. */ +	  /* For the number 0.xxxx,  0 is not significant. */ +	  if (ex_stack->s_num->n_len == 1 && +	      ex_stack->s_num->n_scale != 0 && +	      ex_stack->s_num->n_value[0] == 0 ) +	    int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); +	  else +	    int2num (&ex_stack->s_num, ex_stack->s_num->n_len +		     + ex_stack->s_num->n_scale); +	  break; +		 +	case 'S':  /* Scale function. */  +	  int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); +	  break; + +	case 'R':  /* Square Root function. */ +	  if (!bc_sqrt (&ex_stack->s_num, scale)) +	    rt_error ("Square root of a negative number"); +	  break; + +	case 'I': /* Read function. */ +	  push_constant (input_char, i_base); +	  break; +	} +	break; + +      case 'd' : /* Decrement number */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	decr_var (var_name); +	break; +       +      case 'h' : /* Halt the machine. */ +	exit (0); + +      case 'i' : /* increment number */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	incr_var (var_name); +	break; + +      case 'l' : /* load variable */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	load_var (var_name); +	break; + +      case 'n' : /* Negate top of stack. */ +	bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num); +	break; + +      case 'p' : /* Pop the execution stack. */ +	pop (); +	break; + +      case 's' : /* store variable */ +	var_name = byte(&pc); +	if ((var_name & 0x80) != 0) +	  var_name = ((var_name << 8) & 0x7f) + byte(&pc); +	store_var (var_name); +	break; + +      case 'w' : /* Write a string to the output. */ +	while ((ch = byte(&pc)) != '"') out_char (ch); +	if (interactive) fflush (stdout); +	break; +		    +      case 'x' : /* Exchange Top of Stack with the one under the tos. */ +	if (check_stack(2)) { +	  bc_num temp = ex_stack->s_num; +	  ex_stack->s_num = ex_stack->s_next->s_num; +	  ex_stack->s_next->s_num = temp; +	} +	break; + +      case '0' : /* Load Constant 0. */ +	push_copy (_zero_); +	break; + +      case '1' : /* Load Constant 0. */ +	push_copy (_one_); +	break; + +      case '!' : /* Negate the boolean value on top of the stack. */ +	c_code = is_zero (ex_stack->s_num); +	assign (c_code); +	break; + +      case '&' : /* compare greater than */ +	if (check_stack(2)) +	  { +	    c_code = !is_zero (ex_stack->s_next->s_num) +	      && !is_zero (ex_stack->s_num); +	    pop (); +	    assign (c_code); +	  } +	break; + +      case '|' : /* compare greater than */ +	if (check_stack(2)) +	  { +	    c_code = !is_zero (ex_stack->s_next->s_num) +	      || !is_zero (ex_stack->s_num); +	    pop (); +	    assign (c_code); +	  } +	break; + +      case '+' : /* add */ +	if (check_stack(2)) +	  { +	    bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num); +	    pop(); +	    pop(); +	    push_num (temp_num); +	    init_num (&temp_num); +	  } +	break; + +      case '-' : /* subtract */ +	if (check_stack(2)) +	  { +	    bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num); +	    pop(); +	    pop(); +	    push_num (temp_num); +	    init_num (&temp_num); +	  } +	break; + +      case '*' : /* multiply */ +	if (check_stack(2)) +	  { +	    bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num, +			 &temp_num, scale); +	    pop(); +	    pop(); +	    push_num (temp_num); +	    init_num (&temp_num); +	  } +	break; + +      case '/' : /* divide */ +	if (check_stack(2)) +	  { +	    if (bc_divide (ex_stack->s_next->s_num, +			   ex_stack->s_num, &temp_num, scale) == 0) +	      { +		pop(); +		pop(); +		push_num (temp_num); +		init_num (&temp_num); +	      } +	    else +	      rt_error ("Divide by zero"); +	  } +	break; + +      case '%' : /* remainder */ +	if (check_stack(2)) +	  { +	    if (is_zero (ex_stack->s_num)) +	      rt_error ("Modulo by zero"); +	    else +	      { +		bc_modulo (ex_stack->s_next->s_num, +			   ex_stack->s_num, &temp_num, scale); +		pop(); +		pop(); +		push_num (temp_num); +		init_num (&temp_num); +	      } +	  } +	break; + +      case '^' : /* raise */ +	if (check_stack(2)) +	  { +	    bc_raise (ex_stack->s_next->s_num, +		      ex_stack->s_num, &temp_num, scale); +	    if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num)) +	      rt_error ("divide by zero"); +	    pop(); +	    pop(); +	    push_num (temp_num); +	    init_num (&temp_num); +	  } +	break; + +      case '=' : /* compare equal */ +	if (check_stack(2)) +	  { +	    c_code = bc_compare (ex_stack->s_next->s_num, +				 ex_stack->s_num) == 0; +	    pop (); +	    assign (c_code); +	  } +	break; + +      case '#' : /* compare not equal */ +	if (check_stack(2)) +	  { +	    c_code = bc_compare (ex_stack->s_next->s_num, +				 ex_stack->s_num) != 0; +	    pop (); +	    assign (c_code); +	  } +	break; + +      case '<' : /* compare less than */ +	if (check_stack(2)) +	  { +	    c_code = bc_compare (ex_stack->s_next->s_num, +				 ex_stack->s_num) == -1; +	    pop (); +	    assign (c_code); +	  } +	break; + +      case '{' : /* compare less than or equal */ +	if (check_stack(2)) +	  { +	    c_code = bc_compare (ex_stack->s_next->s_num, +				 ex_stack->s_num) <= 0; +	    pop (); +	    assign (c_code); +	  } +	break; + +      case '>' : /* compare greater than */ +	if (check_stack(2)) +	  { +	    c_code = bc_compare (ex_stack->s_next->s_num, +				 ex_stack->s_num) == 1; +	    pop (); +	    assign (c_code); +	  } +	break; + +      case '}' : /* compare greater than or equal */ +	if (check_stack(2)) +	  { +	    c_code = bc_compare (ex_stack->s_next->s_num, +				 ex_stack->s_num) >= 0; +	    pop (); +	    assign (c_code); +	  } +	break; + +	default  : /* error! */ +	  rt_error ("bad instruction: inst=%c", inst); +      } +    } + +  /* Clean up the function stack and pop all autos/parameters. */ +  while (pc.pc_func != 0) +    { +      pop_vars(functions[pc.pc_func].f_autos); +      pop_vars(functions[pc.pc_func].f_params); +      fpop (); +      pc.pc_addr = fpop (); +      pc.pc_func = fpop (); +    } + +  /* Clean up the execution stack. */  +  while (ex_stack != NULL) pop(); + +  /* Clean up the interrupt stuff. */ +  if (interactive) +    { +      signal (SIGINT, use_quit); +      if (had_sigint) +	printf ("Interruption completed.\n"); +    } +} + + +/* Prog_char gets another byte from the program.  It is used for +   conversion of text constants in the code to numbers. */ + +char +prog_char () +{ +  return byte(&pc); +} + + +/* Read a character from the standard input.  This function is used +   by the "read" function. */ + +char +input_char () +{ +  char in_ch; +   +  /* Get a character from the standard input for the read function. */ +  in_ch = getchar(); + +  /* Check for a \ quoted newline. */ +  if (in_ch == '\\') +    { +      in_ch = getchar(); +      if (in_ch == '\n') +	in_ch = getchar(); +    } + +  /* Classify and preprocess the input character. */ +  if (isdigit(in_ch)) +    return (in_ch - '0'); +  if (in_ch >= 'A' && in_ch <= 'F') +    return (in_ch + 10 - 'A'); +  if (in_ch >= 'a' && in_ch <= 'f') +    return (in_ch + 10 - 'a'); +  if (in_ch == '.' || in_ch == '+' || in_ch == '-') +    return (in_ch); +  if (in_ch <= ' ') +    return (' '); +   +  return (':'); +} + + +/* Push_constant converts a sequence of input characters as returned +   by IN_CHAR into a number.  The number is pushed onto the execution +   stack.  The number is converted as a number in base CONV_BASE. */ + +void +push_constant (in_char, conv_base) +   char (*in_char)(VOID); +   int conv_base; +{ +  int digits; +  bc_num build, temp, result, mult, divisor; +  char  in_ch, first_ch; +  char  negative; + +  /* Initialize all bc numbers */ +  init_num (&temp); +  init_num (&result); +  init_num (&mult); +  build = copy_num (_zero_); +  negative = FALSE; + +  /* The conversion base. */ +  int2num (&mult, conv_base); +   +  /* Get things ready. */ +  in_ch = in_char(); +  while (in_ch == ' ') +    in_ch = in_char(); + +  if (in_ch == '+') +    in_ch = in_char(); +  else +    if (in_ch == '-') +      { +	negative = TRUE; +	in_ch = in_char(); +      } + +  /* Check for the special case of a single digit. */ +  if (in_ch < 16) +    { +      first_ch = in_ch; +      in_ch = in_char(); +      if (in_ch < 16 && first_ch >= conv_base) +	first_ch = conv_base - 1; +      int2num (&build, (int) first_ch); +    } + +  /* Convert the integer part. */ +  while (in_ch < 16) +    { +      if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; +      bc_multiply (build, mult, &result, 0); +      int2num (&temp, (int) in_ch); +      bc_add (result, temp, &build); +      in_ch = in_char(); +    } +  if (in_ch == '.') +    { +      in_ch = in_char(); +      if (in_ch >= conv_base) in_ch = conv_base-1; +      free_num (&result); +      free_num (&temp); +      divisor = copy_num (_one_); +      result = copy_num (_zero_); +      digits = 0; +      while (in_ch < 16) +	{ +	  bc_multiply (result, mult, &result, 0); +	  int2num (&temp, (int) in_ch); +	  bc_add (result, temp, &result); +	  bc_multiply (divisor, mult, &divisor, 0); +	  digits++; +	  in_ch = in_char(); +	  if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; +	} +      bc_divide (result, divisor, &result, digits); +      bc_add (build, result, &build); +    } +   +  /* Final work.  */ +  if (negative) +    bc_sub (_zero_, build, &build); + +  push_num (build); +  free_num (&temp); +  free_num (&result); +  free_num (&mult); +} + + +/* When converting base 10 constants from the program, we use this +   more efficient way to convert them to numbers.  PC tells where +   the constant starts and is expected to be advanced to after +   the constant. */ + +void +push_b10_const (pc) +     program_counter *pc; +{ +  bc_num build; +  program_counter look_pc; +  int kdigits, kscale; +  char inchar; +  char *ptr; +   +  /* Count the digits and get things ready. */ +  look_pc = *pc; +  kdigits = 0; +  kscale  = 0; +  inchar = byte (&look_pc); +  while (inchar != '.' && inchar != ':') +    { +      kdigits++; +      inchar = byte(&look_pc); +    } +  if (inchar == '.' ) +    { +      inchar = byte(&look_pc); +      while (inchar != ':') +	{ +	  kscale++; +	  inchar = byte(&look_pc); +	} +    } + +  /* Get the first character again and move the pc. */ +  inchar = byte(pc); +   +  /* Secial cases of 0, 1, and A-F single inputs. */ +  if (kdigits == 1 && kscale == 0) +    { +      if (inchar == 0) +	{ +	  push_copy (_zero_); +	  inchar = byte(pc); +	  return; +	} +      if (inchar == 1) { +      push_copy (_one_); +      inchar = byte(pc); +      return; +    } +    if (inchar > 9) +      { +	init_num (&build); +	int2num (&build, inchar); +	push_num (build); +	inchar = byte(pc); +	return; +      } +    } + +  /* Build the new number. */ +  if (kdigits == 0) +    { +      build = new_num (1,kscale); +      ptr = build->n_value; +      *ptr++ = 0; +    } +  else +    { +      build = new_num (kdigits,kscale); +      ptr = build->n_value; +    } + +  while (inchar != ':') +    { +      if (inchar != '.') +	if (inchar > 9) +	  *ptr++ = 9; +	else +	  *ptr++ = inchar; +      inchar = byte(pc); +    } +  push_num (build); +} + + +/* Put the correct value on the stack for C_CODE.  Frees TOS num. */ + +void +assign (c_code) +     char c_code; +{ +  free_num (&ex_stack->s_num); +  if (c_code) +    ex_stack->s_num = copy_num (_one_); +  else +    ex_stack->s_num = copy_num (_zero_); +}  | 
