/*=-=-=-=-=-=-=-=-=-=-  RCCL/RCI Notice of Copyright  -=-=-=-=-=-=-=-=-=-=-=*
 *									    *
 *	Copyright (C) 1987 by all authors described below. Permission to    *
 *	use, distribute and copy is granted, but only for non-commercial    *
 *	purposes and providing the authors are acknowledged and this 	    *
 *	copyright notice is displayed prominently.			    *
 *									    *
 *	The authors provide this software in hope that it will be useful,   *
 *	but do not assume responsibility for its use.			    *
 *									    *
 *=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
/* The yacc grammer for the transform calculator */

%{
#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include "cdefs.h"
#include "types.h"
#include "symbols.h"
#include "error.h"

#define YYSTYPE YYMYTYPE	/* get around bug in yacc .... */

# define ANSWER_BUFF_SIZE 100

typedef struct mystype {
	int count;
	GTYPE g;
	} YYMYTYPE;

extern GTYPE valueStack[];
#define pushValueStack(g)	if (push_value_stack(g) == -1) YYERROR;
#define popValueStack(i)	if (pop_value_stack(i) == -1) YYERROR;

int answerIndex = 0;
int lastIndex = 0;

extern matrix fullrange;
matrix fullrange = { -1, -1, 0 };
#define FULLRANGE (&fullrange)

char combuf[1024];

char *strcpy();

char *grepcopy (s1, s2)
char *s1, *s2;
{
	int c;

	while (c = *s2++)
	 { if (isdigit(c) || isalpha(c) || c == '_')
	    { *s1++ = c;
	    }
	   else
	    { *s1++ = '\\';
	      *s1++ = c;
	    }
	 }
 	*s1 = '\0';
}

static errorPoint = UNDEF;

yyerror (s)
char *s;
{
	errorPoint = getInputColumn();
}

char *helpFileName();

%}

%token DBLE STR ERR ENTER INT IDENTIFIER INDEX HELP KEY
%token CVTOCT CVTHEX CVTDEC CVTBIN CONVERT
%right '=' 12
%left  '+' 1 '-' 2
%left  '*' 3 '/' 4 '%' 15 '\\' 24
%left  UMINUS UPLUS
%nonassoc '[' 21
%right '^' 14
%right '\'' 20
%token '(' 5 ')' 6 '$' 7
%token ',' 13 ';' 16
%token '<' 17 '>' 18 '.' 19
%token ']' 22 ':' 23
%%
session		: 
		| session statement
		;
statement	: convert expr ENTER
		 { SYMBOL_TABLE_ENTRY *entry;
		   char aname[8];

		   printValue (&($2.g), answerIndex, $1.count);

		   /* ---- store results in answer buffer  ---- */

		   sprintf (aname, "$%d", answerIndex);
		   if ((entry = locateSymbol (aname)) == NULL)
		    { if ((entry = insertSymbol (aname)) == NULL)
		       { putOut ('\n'); 
		   	 print_errors();
			 clear_errors();
			 putOut ('\n');
		       }
		      else
		       { entry->protection = 0;
		       }
		    }
		   else
		    { free_gtype (&entry->g);
		    }
		   if (entry)
		    { if (mcopy_gtype (&entry->g, &($2.g)) == NULL)
		       { putOut ('\n'); 
		   	 print_errors();
			 clear_errors();
			 putOut ('\n');
		       }
		      else
		       { lastIndex = answerIndex;
		         answerIndex = ++answerIndex % ANSWER_BUFF_SIZE;
		       }
		    }
		   talloc_reset();
		   clearValueStack();

		   setInputColumn (printPrompt (answerIndex));
		 }
		| ENTER
		 { setInputColumn (printPrompt (answerIndex));
		 }
		| error ENTER
		 { 
		   talloc_reset();
		   clearValueStack();
		   yyerrok;
		   if (num_errors())
		    { putOut ('\n');
		      print_errors();
		      clear_errors();
		      putOut ('\n');
		    }
		   else
		    { int j;

		      if (errorPoint == UNDEF)
		       { putOut ('\n');
			 printOut ("ughhh! ... syntax error\n");
			 putOut ('\n');
		       }
		      else
		       { char *msg = " syntax error ";
			 if (errorPoint >= strlen (msg))
			  { for (j=0; j<errorPoint-strlen(msg); j++)
		             { putOut (' ');
		             }
		            printOut ("%s^\n\n", msg);
			  }
			 else
			  { for (j=0; j<errorPoint; j++)
			     { putOut (' ');
			     }
			    printOut ("^%s\n\n", msg);
			  }
			 errorPoint = UNDEF;
		       }
		    }
		   setInputColumn (printPrompt (answerIndex));
		 }
		| HELP ENTER
		 {
		   char *helpfile;
		   if ((helpfile = helpFileName()) != NULL)
		    { printOut ("\n Accessing help file ...\n\n");
		      sprintf (combuf, "less %s", helpfile);
		      system (combuf);
		      putOut ('\n');
		    }
		   setInputColumn (printPrompt (answerIndex));
		 }
		| HELP keyword ENTER
		 { char *s;
		   char *helpfile;
		   if ((helpfile = helpFileName()) != NULL)
		    { grepcopy(combuf, $2.g.name);
		      s = strcpy ((char *)talloc(strlen(combuf)+1), combuf);
   
		      printOut ("\n Checking help file ...\n\n");
		      sprintf(combuf,"egrep -s '\t.*(%s|e1 %s).*---' %s",
			      s, s, helpfile);
		      if (system(combuf) != 0)
		       { printOut (" nothing appropriate\n");
		       }
		      else
		       { sprintf(combuf,
			 "less '+/\t.*%s.*---' %s",s, helpfile);
			 system (combuf);
		       }
		      putOut ('\n');
		      talloc_reset();
   		    }
		   setInputColumn (printPrompt (answerIndex));
		 }
		| HELP KEY keyword ENTER
		 { char *s;
		   char *helpfile;

		   if ((helpfile = helpFileName()) != NULL)
		    { grepcopy(combuf, $3.g.name);
		      s = strcpy ((char *)talloc(strlen(combuf)+1), combuf);
   
		      printOut ("\n Checking help file ...\n\n");
		      sprintf (combuf, "egrep '(---.*%s|%s.*---)' %s", 
			       s, s, helpfile);
		      if (system (combuf) != 0)
		       { printOut (" nothing appropriate\n\n");
		       }
		      putOut ('\n');
		      talloc_reset();
		    }
		   setInputColumn (printPrompt (answerIndex));
		 }
		;
keyword		: STR
		  { $$.g.name = $1.g.val.s;
		  }
		| IDENTIFIER
		  { $$.g.name = $1.g.name;
		  }
		;
convert		: CONVERT
		  { $$.count = $1.count;
		  }
		| 
		  { $$.count = 0;
		  }
		;
expr		: expr '+' expr
		 { if (binop (&($$.g), "+", &($1.g), &($3.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| expr '-' expr
		 { if (binop (&($$.g), "-", &($1.g), &($3.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| expr '*' expr
		 { if (binop (&($$.g), "*", &($1.g), &($3.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| expr '/' expr
		 { if (binop (&($$.g), "/", &($1.g), &($3.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| expr '\\' expr
		 { if (binop (&($$.g), "\\", &($1.g), &($3.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| expr '^' expr
		 { if (binop (&($$.g), "^", &($1.g), &($3.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| expr '%' expr
		 { if (binop (&($$.g), "%", &($1.g), &($3.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| '-' expr	%prec UMINUS
		 { if (monop (&($$.g), "-", &($2.g)) == NULL)
		    { YYERROR;
		    }
		 }
       		| '+' expr	%prec UPLUS
		 { $$.g = $2.g;
		   $$.g.name = NULL;
		 }
       		| expr '\''
		 { if (monop (&($$.g), "'", &($1.g)) == NULL)
		    { YYERROR;
		    }
		 }
		| symbol '=' expr
		 { GTYPE g;

		   if (assignValue (&g, &$1.g, &$3.g) == NULL)
		    { YYERROR;
		    }
		   $$.g = g;
		 }
		| '(' symbol.list ')' '=' IDENTIFIER '(' expr.list ')'
		 { GTYPE g;
		   int vi, si, ri, i;

		   vi = valueStackIndex() - $7.count;
		   si = vi - $2.count;
		   ri = valueStackIndex();

		   for (i=0; i<$2.count; i++)
		    { pushValueStack(g);	/* space for return values */
		    }
		   valueStack[ri].val.i = $2.count;
		   valueStack[ri].name  = $5.g.name;
		   if (function (&valueStack[ri], $7.count,
				 &valueStack[vi]) == -1)
		    { YYERROR;
		    }
		   for (i=$2.count-1; i>=0; i--)
		    { if (assignValue (&g, &valueStack[si+i],
					   &valueStack[ri+i]) == NULL)
		       { YYERROR;
		       }
		    }
		   popValueStack ($7.count + $2.count);
		   $$.g = g;
		 }
		| IDENTIFIER '(' expr.list ')'
		 { GTYPE g;
		   int vi = valueStackIndex() - $3.count;

		   g.val.i = 1;
		   g.name  = $1.g.name;
		   if (function (&g, $3.count,
		   	         &valueStack[vi]) == -1)
		    { YYERROR;
		    }
		   $$.g = g;
		   $$.g.name = NULL;
		   popValueStack ($3.count);
		 }
		| expr '[' elements.list ']'
		 { GTYPE g;
		   matrix *row_i, *col_i;

		   if (buildSubscriptMatrices(&row_i, &col_i, $3.count) 
		       == -1)
		    { YYERROR;
		    }
		   if (subscriptValue (&g, &$1.g, row_i, col_i) == NULL)
		    { YYERROR;
		    }
		   $$.g = g;
		 }
		| '<' matrix '>'
		 { GTYPE g;

		   if (buildMatrix (&g, $2.count) == NULL)
		    { YYERROR;
		    }
		   $$.g = g;
		 }
		| primary
		 { $$.g = $1.g;
		 }
		| '(' expr ')'
		 { $$.g = $2.g;
		 }
		;
symbol.list	: symbol ',' symbol
		 { $$.count = 2;

		   pushValueStack ($1.g);
		   pushValueStack ($3.g);
		 }
		| symbol.list ',' symbol
		 { $$.count = $1.count + 1;

		   pushValueStack ($3.g);
		 }
		;
symbol		: IDENTIFIER
		 {
		   $$.g.name = $1.g.name;
		   $$.g.row_i = NULL;
		   $$.g.col_i = NULL;
		 }
		| IDENTIFIER '[' elements.list ']'
		 { GTYPE g;

		   g.name = $1.g.name;
		   if (buildSubscriptMatrices(&g.row_i, &g.col_i, $3.count) 
		       == -1)
		    { YYERROR;
		    }
		   $$.g = g;
		 }
		;
primary		: DBLE
		 { $$.g = $1.g;
		 }
		| INT
		 { $$.g = $1.g;
		 }
		| STR
		 { $$.g = $1.g;
		 }
		| symbol
		 { GTYPE gs, gr;
		   SYMBOL_TABLE_ENTRY *entry;
		   char *idname, aname[8];

		   if (strcmp ($1.g.name, "$") == 0)
		    { sprintf (aname, "$%d", lastIndex);
		      idname = aname;
		    }
		   else
		    { idname = $1.g.name;
		    }
		   if ((entry = locateSymbol (idname)) == NULL)
		    { error (0, "symbol %s is not defined\n", $1.g.name);
		      YYERROR;
		    }
		   if (tcopy_gtype (&gs, &entry->g) == NULL)
		    { YYERROR;
		    }
		   if (subscriptValue (&gr, &gs, $1.g.row_i, $1.g.col_i)
		   	== NULL)
		    { YYERROR;
		    }
		   $$.g.type  = gr.type;
		   $$.g.val   = gr.val;

		   $$.g.name  = $1.g.name;
		   $$.g.row_i = $1.g.row_i;
		   $$.g.col_i = $1.g.col_i;
		 }
		;
expr.list 	:
		 { $$.count = 0;
		 }
		| expr
		 { pushValueStack ($1.g);
		   $$.count = 1;
		 }
		| expr.list ',' expr
		 { pushValueStack ($3.g);
		   $$.count = $1.count + 1;
		 }		
		;
matrix 		: elements.list
		 { GTYPE g;

		   if (buildMatrixRow (&g, $1.count) == NULL)
		    { YYERROR;
		    }
		   pushValueStack (g);
		   $$.g = g;		/* for row/column comparison */
		   $$.count = 1;
		 }
		| matrix rowdelimiter elements.list
		 { GTYPE g;

		   if (buildMatrixRow(&g, $3.count) == NULL)
		    { YYERROR;
		    }
		   if ((g.val.m->row != $1.g.val.m->row) ||
		       (g.val.m->col != $1.g.val.m->col))
		    { error (0, "build matrix -- row/column mismatch\n");
		      YYERROR;
		    }
		   pushValueStack (g);
		   $$.g = g;		/* for row/column comparison */
		   $$.count = $1.count + 1;
		 }
		;
rowdelimiter	: ';'
		| ENTER
		;
elements.list	: elements
		 { $$.count = 1;
		 }
		| elements.list ',' elements
		 { $$.count = $1.count + 1;
		 }
		;
elements	: expr
		 { 
		   pushValueStack ($1.g);
		 }
		| ':'
		 { GTYPE g;

		   g.val.m = FULLRANGE;
		   g.type = MATRIX;
		
		   g.name =  NULL;
		   g.row_i = NULL;
		   g.col_i = NULL;

		   pushValueStack (g);
		 }
		| expr ':' expr
		 { GTYPE g;

		   if (buildRangeMatrix	(&g, &$1.g, &$3.g, NULL) == NULL)
		    { YYERROR;
		    }
		   pushValueStack (g);
		 }
		| expr ':' expr ':' expr
		 { GTYPE g;

		   if (buildRangeMatrix (&g, &$1.g, &$3.g, &$5.g) == NULL)
		    { YYERROR;
		    }
		   pushValueStack (g);
		 }
		;
%%

#include "cdefs.h"

main (argc, argv)
int argc;
char **argv;
{
	int function, i;

	argc--; argv++;
	while (argc)
	 { if (ARGUMENT ("-i"))
	    { int base;
	      argc--; argv++;
	      if (sscanf (*argv, "%d", &base) != 1 ||
		  base < 2 || base > 16)
	       { fprintf (stderr, 
		 "ERROR: -i option requires an integer 2 to 16\n");
		 exit (-1);
	       }
	      setInputBase (base);
	    }
	   else if (ARGUMENT ("-o"))
	    { int base;
	      argc--; argv++;
	      if (sscanf (*argv, "%d", &base) != 1 ||
		  base < 2 || base > 16)
	       { fprintf (stderr, 
		 "ERROR: -o option requires an integer 2 to 16\n");
		 exit (-1);
	       }
	      setOutputBase (base);
	    }
	   else
	    { fprintf (stderr, "USAGE: arc [-i <base>] [-o base]\n");
	      exit (-1);
	    }
	   argc--; argv++;
	 }

	initSymbolTable();  /* initialize constants */
	setInputColumn (printPrompt (answerIndex));
	yyparse ();
}

# include "lex.yy.c"

