%{
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include "consthash.h"
#include "types.h"
#include "arithfuncs.h"
#include "boolfuncs.h"
#include "funchash.h"
#include "callhash.h"


extern int yyparse( void );
extern int yylex (void);
extern int yylineno;
extern BOOL verbose;
extern BOOL expand_constants;


int yyerrors   = 0;


static char *curr_func  = NULL;
static char *curr_param = NULL;

expr prog_expr   = NULL;


void yyerror(const char *str)
{
        fprintf(stderr,"\tline %d: error: %s\n", yylineno, str);
	yyerrors++;
}
 
int yywrap( void )
{
        return 1;
} 

static expr check_id( char *name )
{
	/* if name is the current function's name, build an id */
	if( curr_param != NULL && strcmp( name, curr_param ) == 0 )
	{
		return expr_id( name );
	}
	/* otherwise look it up in the constant hash */
	int n = lookup_const( name );
	if( expand_constants )
	{
		return expr_num( n );
	} else
	{
		return expr_id( name );
	}
}
%}

%union 
{
        int       n;
        char     *s;
	expr      e;
	bexpr     b;
	guard     g;
	guardlist gl;
	fdefn     f;
}

%token COLONCOLON IMPLIES EQ GT NE LT LE GE TRUEV PLUS MINUS MUL
       DIV MOD OPEN CLOSE GUARD IS INTTYPE OTHERWISE
       TOKERR TOKEOF
%token <n> NUMBER
%token <s> IDENT

%type <e> factor term expr;
%type <b> bexpr;
%type <g> guard guardotherwise;
%type <gl> guardrules guardlist;
%type <f> fdefinition;


%start program

%%
program      :  defns expr
		{
			prog_expr = $2;
		}
	     |  defns
	        {
			yyerror( "expression expected after functions, eg f(1)" );
		}
             ;

defns        :  /* empty */
             | defns ftypedefn    /* ignore type defns */
             | defns fdefinition
             ;

ftypedefn    : IDENT COLONCOLON type IMPLIES type
	       {
			if( verbose )
			{
				fprintf( stderr,
					"\t- type of function %s : "
					"Int->Int\n", $1 );
			}
	       }
	     ;

type         : INTTYPE
             ;

fdefinition : IDENT IDENT {curr_func=$1; curr_param=$2;}
	      IS expr     {curr_func=curr_param=NULL;}
	       {
			define_function( $1, fdefn_onerule( $1, $2, $5 ) );
	       }
	     | IDENT IDENT {curr_func=$1; curr_param=$2;}
	       guardrules  {curr_func=curr_param=NULL;}
	       {
			define_function( $1,
			     fdefn_manyrules( $1, $2, reverse_guardlist($4) ) );
	       }
	     ;

guardrules   : guardlist		/* built in reverse order */
	     {
	     	$$ = $1;
	     }
             | guardlist guardotherwise
	     {
	     	$$ = guardlist_cons( $2, $1 );
	     } 
	     ;

guardlist   : guard			/* built in reverse order */
		{
			$$ = guardlist_cons( $1, guardlist_nil() );
		}
             | guardlist guard
		{
			$$ = guardlist_cons( $2, $1 );
		}
	     ;

guard        : GUARD bexpr IS expr
		{
			$$ = guard_pair( $2, $4 );
		}
	     ;

guardotherwise	: GUARD OTHERWISE IS expr
		{
			$$ = guard_pair( bexpr_truev(), $4 );
		}
		| GUARD TRUEV IS expr
		{
			$$ = guard_pair( bexpr_truev(), $4 );
		}
	     ;

bexpr        : expr EQ expr	{ $$ = equals( $1, $3 ); }
	     | expr NE expr     { $$ = notequals( $1, $3 ); }
	     | expr GT expr     { $$ = greaterthan( $1, $3 ); }
	     | expr GE expr     { $$ = greaterequal( $1, $3 ); }
	     | expr LT expr     { $$ = lessthan( $1, $3 ); }
	     | expr LE expr     { $$ = lessequal( $1, $3 ); }
	     | TRUEV            { $$ = bexpr_truev(); }
	     ;

expr         : expr PLUS term   { $$ = plus( $1, $3 ); }
             | expr MINUS term  { $$ = minus( $1, $3 ); }
	     | term
	     ;

term         : term MUL factor { $$ = times( $1, $3 ); }
             | term DIV factor { $$ = divide( $1, $3 ); }
             | term MOD factor { $$ = mod( $1, $3 ); }
	     | factor
	     ;

factor       : NUMBER		     { $$ = expr_num( $1 ); }
	     | OPEN expr CLOSE       { $$ = $2; }
	     | IDENT OPEN expr CLOSE { $$ = expr_call($1,$3); mark_called($1); }
	     | IDENT                 { $$ = check_id( $1 ); }
	     ;
