/* $Header: /afs/athena.mit.edu/astaff/project/atdev/src/fmax/RCS/grammar.y,v 1.6 91/01/24 14:27:13 dot Exp Locker: dot $ */
%{
#include "datatypes.h"
#include "fmax.h"

static Value result, tmpval1, tmpval2, tmpval3;

extern Tree *MakeTreeNode();
extern Tree *MakeBinaryOp();
extern Tree *MakeTernaryOp();
extern Tree *MakeCommaOp();
extern Tree *MakeUnaryOp();
extern Tree *MakeIntegerNode();
extern Tree *MakeRealNode();
extern Tree *MakeComplexNode();
extern Tree *MakeArrayNode();
extern Tree *MakeVariableNode();
extern Tree *MakeFunctionNode();
extern void ChangeToLValue();
extern TreeList *EmptyTreeList();
extern TreeList *AddToTreeList();
extern void DefineFunction();

%}

%start program

%union { Tree *tree;
	 int i;
	 double d;
	 char *id;
	 TreeList *list; }

%token <i> INTEGER DISPLAY ON OFF
%token <d> REAL 
%token <id> IDENT
%token <id> STRING
%token LPAREN RPAREN LBRACKET RBRACKET LCURLY RCURLY
%token COMMA SEMICOLON EOL
%token DEFINE CLEAR REPLOT QUIT PLOT SET NUMSAMPLES
%token TITLE XLABEL YLABEL Y2LABEL


%right <op> ASSIGN PLUSEQ MINUSEQ TIMESEQ DIVIDEEQ MODEQ POWEREQ LSHIFTEQ RSHIFTEQ LOREQ BOREQ XOREQ LANDEQ BANDEQ 
%right <op> QUESTION COLON
%left <op> LOR
%left <op> LAND
%left <op> BOR
%left <op> XOR
%left <op> BAND
%left <op> EQ NE
%left <op> LT GT LE GE
%left <op> LSHIFT RSHIFT
%left <op> PLUS MINUS
%left <op> TIMES DIVIDE MODULO
%right <op> POWER
%left <op> CONCAT    
%right <op> UNARY LNOT BNOT PLUSPLUS MINUSMINUS PLUSOVER MINUSOVER MULTOVER DIVOVER MODOVER POWEROVER LOROVER BOROVER XOROVER LANDOVER BANDOVER SIZEOF

%type <tree> expr sexpr uexpr primary 
%type <list> exprlist nonempty_exprlist 
%type <i> onoff setvar

%% 

program:
	statement
	| program  statement

statement:
        eos
	| command eos
    	| expr eos
		{ EvalTree($1,&result,NULL);
                  fputs("==>\t", stdout); 
		  dispval(stdout,&result);
	          fputs("\n", stdout);
	          fflush(stdout);}
	;

eos:
	SEMICOLON
	| EOL
	;

/* expressions */
expr:
	sexpr
	| sexpr COMMA nonempty_exprlist
		{ $$ = MakeCommaOp($1,$3); }
	; 

/* single expression, i.e. no commas */
sexpr:
	uexpr
	| sexpr PLUS sexpr
		{ $$ = MakeBinaryOp(Plus,$1,$3); }
	| sexpr MINUS sexpr
		{ $$ = MakeBinaryOp(Minus,$1,$3); }
	| sexpr TIMES sexpr
		{ $$ = MakeBinaryOp(Mult,$1,$3); }
	| sexpr DIVIDE sexpr
		{ $$ = MakeBinaryOp(Div,$1,$3); }
	| sexpr MODULO sexpr
		{ $$ = MakeBinaryOp(Mod,$1,$3); }
	| sexpr POWER sexpr
		{ $$ = MakeBinaryOp(Power,$1,$3); }
	| sexpr LSHIFT sexpr
		{ $$ = MakeBinaryOp(Lshift,$1,$3); }
	| sexpr RSHIFT sexpr
		{ $$ = MakeBinaryOp(Rshift,$1,$3); }
	| sexpr EQ sexpr
		{ $$ = MakeBinaryOp(Eq,$1,$3); }
	| sexpr NE sexpr
		{ $$ = MakeBinaryOp(Ne,$1,$3); }
	| sexpr LT sexpr
		{ $$ = MakeBinaryOp(Lt,$1,$3); }
	| sexpr GT sexpr
		{ $$ = MakeBinaryOp(Gt,$1,$3); }
	| sexpr LE sexpr
		{ $$ = MakeBinaryOp(Le,$1,$3); }
	| sexpr GE sexpr
		{ $$ = MakeBinaryOp(Ge,$1,$3); }
	| sexpr LAND sexpr
		{ $$ = MakeBinaryOp(Land,$1,$3); }
	| sexpr LOR sexpr
		{ $$ = MakeBinaryOp(Lor,$1,$3); }
	| sexpr XOR sexpr
		{ $$ = MakeBinaryOp(Xor,$1,$3); }
	| sexpr BAND sexpr
		{ $$ = MakeBinaryOp(Band,$1,$3); }
	| sexpr BOR sexpr
		{ $$ = MakeBinaryOp(Bor,$1,$3); }
	| sexpr CONCAT sexpr
		{ $$ = MakeBinaryOp(Concat,$1,$3); }
	| sexpr QUESTION sexpr COLON sexpr
		{ $$ = MakeTernaryOp($1,$3,$5); }
	| sexpr ASSIGN 
		{ ChangeToLValue($1); }
	  sexpr
		{ $$ = MakeBinaryOp(Assign,$1,$4); }
	;

/* unary expressions */
uexpr:
	primary
	| MINUS uexpr  %prec UNARY
		{ $$ = MakeUnaryOp(Uminus,$2); }
	| PLUS uexpr   %prec UNARY
		{ $$ = MakeUnaryOp(Uplus,$2); }
	| PLUSPLUS uexpr
		{ $$ = MakeUnaryOp(Preinc,$2); }
	| MINUSMINUS uexpr
		{ $$ = MakeUnaryOp(Predec,$2); }
	| BNOT uexpr
		{ $$ = MakeUnaryOp(Bnot,$2); }
	| LNOT uexpr
		{ $$ = MakeUnaryOp(Lnot,$2); }
	| PLUSOVER uexpr
		{ $$ = MakeUnaryOp(Plusover,$2); }
	| MINUSOVER uexpr
		{ $$ = MakeUnaryOp(Minusover,$2); }
	| MULTOVER uexpr
		{ $$ = MakeUnaryOp(Multover,$2); }
	| DIVOVER uexpr
		{ $$ = MakeUnaryOp(Divover,$2); }
	| MODOVER uexpr
		{ $$ = MakeUnaryOp(Modover,$2); }
	| POWEROVER uexpr
		{ $$ = MakeUnaryOp(Powerover,$2); }
	| LOROVER uexpr
		{ $$ = MakeUnaryOp(Lorover,$2); }
	| BOROVER uexpr
		{ $$ = MakeUnaryOp(Borover,$2); }
	| XOROVER uexpr
		{ $$ = MakeUnaryOp(Xorover,$2); }
	| LANDOVER uexpr
		{ $$ = MakeUnaryOp(Landover,$2); }
	| BANDOVER uexpr
		{ $$ = MakeUnaryOp(Bandover,$2); }
	| SIZEOF uexpr
		{ $$ = MakeUnaryOp(Sizeof,$2); }
	;

/* primary (non-compound) expressions */
primary:
	IDENT
		{ $$ = MakeVariableNode($1); }
	| INTEGER
		{ $$ = MakeIntegerNode($1); }
	| REAL
		{ $$ = MakeRealNode($1); }
	| LCURLY sexpr COMMA sexpr RCURLY    /* complex number */
		{ $$ = MakeComplexNode(EvalTree($2,&tmpval1,NULL),
				       EvalTree($4,&tmpval2,NULL)); }
	| LPAREN expr RPAREN          
		{ $$ = $2; }
	| LBRACKET exprlist RBRACKET         /* an array */
		{ $$ = MakeArrayNode($2); } 
	| LBRACKET sexpr COLON sexpr COLON sexpr RBRACKET
		{ $$ = MakePartition(EvalTree($2,&tmpval1,NULL),
				     EvalTree($4,&tmpval2,NULL),
				     EvalTree($6,&tmpval3,NULL)); }
	| LBRACKET sexpr COLON sexpr RBRACKET
		{ tmpval2.type = integer;
		  tmpval2.i.i = 1;
		  $$ = MakePartition(EvalTree($2,&tmpval1,NULL),
				     &tmpval2,
				     EvalTree($4,&tmpval3,NULL)); }
	| primary LPAREN exprlist RPAREN     /* function call */
		{ $$ = MakeFunctionNode($1,$3); } 
	| primary LBRACKET sexpr RBRACKET    /* array index */
		{ $$ = MakeBinaryOp(Index,$1,$3); }
	| primary PLUSPLUS
		{ $$ = MakeUnaryOp(Postinc,$1); }
	| primary MINUSMINUS
		{ $$ = MakeUnaryOp(Postdec,$1); }
	| primary LNOT   %prec UNARY         /* factorial */
		{ $$ = MakeUnaryOp(Factorial,$1); }
	;

exprlist:
	/* empty */
		{ $$ = EmptyTreeList(); }
	| nonempty_exprlist
	;
nonempty_exprlist:
	sexpr
		{ $$ = AddToTreeList(EmptyTreeList(),$1); } 
	| nonempty_exprlist COMMA sexpr
		{ $$ = AddToTreeList($1,$3); }
	;



identlist:
	/* empty */
	| nonempty_identlist
	; 
nonempty_identlist:
	IDENT 
		{ MakeLocalSym($1); }
	| nonempty_identlist COMMA IDENT
		{ MakeLocalSym($3); } 
	;

command:
	DEFINE IDENT LPAREN
		{ MarkLocalSyms(); }
	identlist RPAREN ASSIGN sexpr
		{ ForgetLocalSyms();
		  DefineFunction($2,CountLocalSyms(),$8); 
	        }
	| CLEAR { ClearAllPlots(); }
    	| QUIT
		{ exit(0); }
	| PLOT LBRACKET sexpr RBRACKET
		{ PlotArrays(NULL, EvalTree($3, &tmpval1, NULL)); }
	| PLOT LBRACKET sexpr COMMA sexpr RBRACKET
		{ PlotArrays(EvalTree($3, &tmpval1, NULL), 
                             EvalTree($5, &tmpval2, NULL));}
	| PLOT 
		{ MarkLocalSyms();
		  MakeLocalSym("x"); }
          sexpr
		{ ForgetLocalSyms();
		  PlotExpression($3); }
	| PLOT STRING
		{ PlotFile($2); }
	| TITLE STRING
		{ SetTitle($2); }
	| XLABEL STRING
		{ SetXLabel($2); }
	| YLABEL STRING
		{ SetYLabel($2); }
	| Y2LABEL STRING
		{ SetY2Label($2); }
        | SET NUMSAMPLES INTEGER
		{ SetSamples ($3); }
	| SET setvar onoff
                { SetOption ($2, $3);}
	; 
	/* etc */

setvar:
     	  DISPLAY
		{ $$ = DISPLAYVAL; }
     	;

onoff:
     	  ON 
		{ $$ = 1; }
	| OFF
		{ $$ = 0; }
     	;


%%
    
#include "lex.yy.c"

double valueof(v)
Value *v;
{
    if (v->type == integer) return v->i.i;
    else if (v->type == real) return v->r.r;
    else internalerr("valueof: integer or real expected.");
}
    
Tree *MakeTreeNode(n)
int n;
{
    register Tree *t;

    t = (Tree *) malloc(sizeof(Tree) + (n-1)*sizeof(Tree *));
    if (t == NULL) internalerr("Out of memory in MakeTreeNode()");
    return t;
}
    
Tree *MakeBinaryOp(op,t1,t2)
operator op;
Tree *t1,*t2;
{
    register Tree *t;

    t = MakeTreeNode(2);
    t->op = op;
    t->args[0] = t1;
    t->args[1] = t2;
    return t;
}
    
Tree *MakeTernaryOp(t1,t2,t3)
Tree *t1, *t2, *t3;
{
    register Tree *t;

    t = MakeTreeNode(3);
    t->args[0] = t1;
    t->args[1] = t2;
    t->args[2] = t3;
    return t;
}

Tree *MakeCommaOp(first,rest)
Tree *first;
TreeList *rest;
{
    register Tree *t;
    register int i;

    t = MakeTreeNode(rest->n + 1);
    t->op = Comma;
    t->v.i.i = rest->n + 1;
    t->args[0] = first;
    for(i=0; i < rest->n; i++) {
	t->args[i+1] = rest->subtrees[i];
    }
    return t;
}


Tree *MakeUnaryOp(op,t1)
operator op;
Tree *t1;
{
    register Tree *t;

    t = MakeTreeNode(1);
    t->op = op;
    t->args[0] = t1;
    return t;
}

Tree *MakeIntegerNode(i)
int i;
{
    register Tree *t;

    t = MakeTreeNode(0);
    t->op = Pushc;
    t->v.type = integer;
    t->v.i.i = i;
    return t;
}

Tree *MakeRealNode(r)
double r;
{
    register Tree *t;

    t = MakeTreeNode(0);
    t->op = Pushc;
    t->v.type = real;
    t->v.r.r = r;
    return t;
}
    
Tree *MakeComplexNode(a,b)
Value *a,*b;
{
    register Tree *t;

    t = MakeTreeNode(0);
    t->op = Pushc;
    t->v.type = complex;
    if (a->type == integer)
	t->v.c.c.r = (double) a->i.i;
    else if (a->type == real)
	t->v.c.c.r = a->r.r;
    else
	runerr("components of complex number must be integer or real.");
    if (b->type == integer)
	t->v.c.c.i = (double) b->i.i;
    else if (b->type == real)
	t->v.c.c.i = b->r.r;
    else
	runerr("components of complex number must be integer or real.");

    return t;
}

Tree *MakeArrayNode(tl)
TreeList *tl;
{
    register Tree *t;
    register int i;

    t = MakeTreeNode(0);
    t->op = Pushc;
    t->v.type = array;
    t->v.a.maxsize = t->v.a.N = tl->n;
    t->v.a.vals = (Value *) malloc(tl->n * sizeof(Value));
    for(i=0; i < tl->n; i++) {
	t->v.a.vals[i] = *EvalTree(tl->subtrees[i],&tmpval1,NULL);
    }
    return t;
}

Tree *MakeVariableNode(id)
char *id;
{
/* Look up the ident in the symbol table.  If it is not a local var.,
 * then code a Push operation with that symbol as the argument.  If
 * it is a local variable, then do a Pushl operation with the localvar
 * as the argument.
 */

    register Tree *t;
    register Symbol *s;

    s = LookupIdent(id);
    if (s == NULL) s = NewUndefSymbol(id);
    t = MakeTreeNode(0);

    if (s->value.type != localvar) {
	t->op = Push;
	t->v.type = variable;
	t->v.v.s = s;
    }
    else {
	t->op = Pushl;
	t->v = s->value;
    }

    free(id);   /* fix the symbol handing */
    return t;
}

Tree *MakeFunctionNode(func,args)
Tree *func;
TreeList *args;
{
    register Tree *t;
    register int i;

    t = MakeTreeNode(args->n + 1);
    t->op = Call;
    t->v.i.i = args->n;
    t->args[0] = func;
    for(i=0; i<args->n; i++)
	t->args[i+1] = args->subtrees[i];
    return t;
}

void ChangeToLValue(t)
Tree *t;
{
    if (t->op == Push) {
	t->op = Pushlval;
    }
    else if (t->op == Index) {
	t->op = Indexlval;
	ChangeToLValue(t->args[0]);
    }
    else
	yyerror("Bad left-hand side of assignment.");
}

TreeList *EmptyTreeList()
{
    register TreeList *tl;

    tl = (TreeList *)malloc(sizeof(TreeList) + 7*sizeof(Tree *));
    tl->n = 0;
    tl->maxn = 8;
    return tl;
}

TreeList *AddToTreeList(tl,t)
TreeList *tl;
Tree *t;
{
    if (tl->n == tl->maxn) {
	realloc(tl,sizeof(TreeList) + (tl->maxn+tl->maxn-1)*sizeof(Tree *));
	tl->maxn *= 2;
    }

    tl->subtrees[tl->n++] = t;
    return tl;
}

void DefineFunction(name,numprms,body)
char *name;
int numprms;
Tree *body;
{
    Symbol *s;

    s = LookupIdent(name);
    if (s == NULL) s = NewUndefSymbol(name);

    s->value.type = deffunc;
    s->value.df.numprms = numprms;
    s->value.df.body = body;
    free(name);  /* fix up the symbol handling so I don't have to do this */
}

Tree *MakePartition(v1,v2,v3)
Value *v1, *v2, *v3;
{
    double start, step, stop;
    Tree *t;
    int i;

    if (!(isarray(v1) || isarray(v2) || isarray(v3))) {
	if (!(isintorreal(v1) && isintorreal(v2) && isintorreal(v3)))
         runerr("Integers, reals, or arrays expected for partition operator.");

	start = valueof(v1);
	step = valueof(v2);
	stop = valueof(v3);
	if (step == 0.0) runerr("Cannot partition with a step of zero.");
	
	t = MakeTreeNode(0);
	t->op = Pushc;
	t->v.type = array;
	t->v.a.N =  (stop > start)?(int)floor((stop-start)/step + 1):0;
	t->v.a.maxsize = t->v.a.N;
	t->v.a.vals = (Value *)malloc(sizeof(Value) * t->v.a.N);
	
	/* Work around a vax  bug to ensure that it */
	/* goes through the loop the last time (when start = stop */
	for (i=0; start < stop+step; start += step, i++) {
	    t->v.a.vals[i].type =  real;
	    t->v.a.vals[i].r.r = start;
       }

	return t;
    }
    else {
	internalerr("arrays not supported yet.");
    }
}
       
