/*	EXPR_SEM.C
	Copyright (C) 1992	Keith L. Robertson	All Rights Reserved

	Semantic actions used in the EXPR expression evaluator.
*/
#include "expr_lex.h"		// <types.h>
#include <math.h>
#include "pg_parse.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define		MAX_TABLE	20
typedef struct  {
	STRING	name;
	FLOAT	value;
}	IDENTIFIER_ENTRY;

IDENTIFIER_ENTRY	ident_table [MAX_TABLE]	= {
	"pi",	3.14159265358979323846,
	"e",	2.7182818284590452354,
};


/****************************/

VOID	expr_print ()
{   printf ("\t%f\n", attr [0].val.f);
}


VOID	finish_kill ()
{   printf ("\tDone.\n");
}

VOID	kill_ident ()
{   SHORT	id_index;
    STRING	id_name = attr [0].name;
    CHAR	msg [80];

    for  (id_index = MAX_TABLE-1;  0 <= id_index;  --id_index)  {
	if  (strcmp (ident_table [id_index].name, id_name) == 0)  {
	    free (ident_table [id_index].name);
	    ident_table [id_index].name = 0;
	    break;
	}
    }
    if  (id_index < 0)  {
	/* Report that it wasn't found. */
	sprintf (msg, "  Identifier '%s' not found.", id_name);
	log_error (SEMANTIC, msg);
    }
    free (id_name);
}


VOID	list ()
{   SHORT	id_index;
    BOOL	is_ident = 0;
    for  (id_index = MAX_TABLE-1;  0 <= id_index;  --id_index)  {
	if  (ident_table [id_index].name != 0)  {
	    printf ("\t%s\t%f\n", ident_table [id_index].name, ident_table [id_index].value);
	    is_ident = 1;
	}
    }
    if  (! is_ident)  { printf ("\tNo identifiers are defined.\n"); }
}


VOID	lisp ()
{   printf ("\tTh'ufferin' th'uccotash!\n\
\tThum people get their kick'th from the th'rangetht thing'th.\n");
    if  (! was_jumped (attr [1]))  {
	printf ("\tI gueth I lithp and you th'tutter!\n");
    }
}


VOID	session_help ()
{   printf ("\n\
Enter a command to have it evaluated and its result printed:\n\
  All commands must end with a semicolon (;).  Available commands:\n\
  An expression with NUMBERs, IDENTifiers, symbols:  ( ) ^ * / + -\n\
  and functions '<fn>(<expr>)':  arc/cos sin tan, exp log, abs sqrt ceil floor.\n\
  Use:\t\"IDENT = <expr> ;\"\tto define an identifier for use in expressions.\n\
\t\"list ;\"\t\tto list all currently defined identifiers.\n\
\t\"kill IDENT... ;\"\tto undefine a list of identifiers.\n\
\t\"lisp [lisp];\"\t\tfor a silly message.\n\
\t\"help ;\" or \"? ;\"\tto repeat this message.\n\
\t\"exit ;\"  or enter end-of-file (F6,Return)  to exit.\n");
}


VOID	session_error ()
{   log_error (SYNTAX, "error in expression");
	/* Read to end of command. */
    skip_to_token (SEMICOLON);
}


#if  0
/********
    Example usage of skip_to_bittok().
    Let's say these are all the high-level uses of Expr:
	Assign_statement	--> IDENT ':=' Expr ';'
	While_statement		--> WHILE Expr DO Statement
	If_statement		--> IF Expr THEN Statement
    There are NUM_TOKENS tokens, ';' is token 1 (right after EOS, token 0),
    DO is token 22, and THEN is token 23.
    If there's a syntax error in an Expr, we want to print a message and
    skip to the end of the Expr.
    'log_error' prints the position where the error was detected,
    so we rarely need much description as to what token was expected.
********/

VOID	expr_error ()
{   static USHORT	expr_ends [(NUM_TOKENS + 8 - 1) / 8]
	= { 0x0002, 0x00C0, };
    log_error (SYNTAX, "Error in expression.");
    skip_to_bittok (expr_ends);
}
#endif


VOID	expr_assign ()
{   SHORT	id_index;
    STRING	id_name = attr [0].name;

	/* Try and find ident name in table. */
    id_index = MAX_TABLE-1;
    while  ((0 <= id_index)  &&
	    (strcmp (ident_table [id_index].name, id_name) != 0))  {
	--id_index;
    }

    if  (id_index < 0)  {
	/* Ident being assigned isn't in the table.  Create it. */

	/* First try and find an empty spot. */
	id_index = MAX_TABLE-1;
	while  ((0 <= id_index)  &&  (ident_table [id_index].name != 0))  {
	    --id_index;
	}

	if  (0 <= id_index)  {
	    /* Found a spot for it; add ident in table. */
	    ident_table [id_index].name = id_name;
	}
    }

    if  (0 <= id_index)  {
	/* Set ident value. */
	ident_table [id_index].value = attr [2].val.f;
    }
    else   {
	log_error (SEMANTIC, "could not add identifier.  Table is full.");
    }

    /* Value of assignment is value of expression. */
    attr [0].val.f = attr [2].val.f;
}


VOID	expr_paren ()
{   attr [0].val.f = attr [1].val.f;
}


#define		NUM_FUNCTIONS	12
static CSTRING	ftable [] = {
	"arccos",	"arcsin",	"arctan",
	"cos",		"sin",		"tan",
	"exp",		"log",		"abs",
	"sqrt",		"ceil",		"floor",
};

VOID	function_call ()
{   SHORT	fn_index = NUM_FUNCTIONS-1;
    STRING	fn_name = strlwr (attr [0].name);

    while  (fn_index >= 0  &&  strcmp (ftable[fn_index], fn_name) != 0)  {
	--fn_index;
    }

    if  (fn_index < 0)  {
	CHAR	msg [80];
	sprintf (msg, "  Function '%s' is not defined.", fn_name);
	log_error (SEMANTIC, msg);
	attr [0].val.f = 0;		// Return result of 0.
    }
    else   {
	FLOAT	res,  arg = attr [2].val.f;
	switch  (fn_index)  {
	case  0:	res = acos (arg);	break;
	case  1:	res = asin (arg);	break;
	case  2:	res = atan (arg);	break;
	case  3:	res = cos (arg);	break;
	case  4:	res = sin (arg);	break;
	case  5:	res = tan (arg);	break;
	case  6:	res = exp (arg);	break;
	case  7:	res = log (arg);	break;
	case  8:	res = fabs (arg);	break;
	case  9:	res = sqrt (arg);	break;
	case 10:	res = ceil (arg);	break;
	case 11:	res = floor (arg);	break;
	}
	attr [0].val.f = res;	// Return result.
    }
}


VOID	unary ()
{   if  (attr [0].token == MINUS)  {
	attr [0].val.f = - attr [1].val.f;
    }
    else  { attr [0].val.f = attr [1].val.f; }
}


VOID	power ()
{   attr [0].val.f = pow (attr [0].val.f, attr [2].val.f);
}


VOID	binary2 ()
{   if  (attr [1].token == TIMES)  {
	attr [0].val.f *= attr [2].val.f;
    }
    else   {	/* '/', DIVIDE */
	attr [0].val.f /= attr [2].val.f;
    }
}


VOID	binary1 ()
{   if  (attr [1].token == PLUS)  {
	attr [0].val.f += attr [2].val.f;
    }
    else   {	/* '-', MINUS */
	attr [0].val.f -= attr [2].val.f;
    }
}


VOID	look_up_ident ()
{   FLOAT	val;
    CHAR	msg [80];
    SHORT	id_index;
    STRING	id_name = attr [0].name;

	/* Try and find ident name in table. */
    id_index = MAX_TABLE-1;
    while  ((0 <= id_index)  &&
	    (strcmp (ident_table [id_index].name, id_name) != 0))  {
	--id_index;
    }

    if  (0 <= id_index)  {
	/* Found it!  Get its value. */
	val = ident_table [id_index].value;
    }
    else  {
	/* Report that it wasn't found. */
	sprintf (msg, "  Identifier '%s' not found.", id_name);
	log_error (SEMANTIC, msg);
	val = 0;
    }
    attr [0].val.f = val;
    free (id_name);
}


/****************************/

INT	main ()
{   INT		id_index;

    /* Duplicate any predefined identifiers so */
    /* that kill() doesn't free static memory. */
    for  (id_index = MAX_TABLE-1;  0 <= id_index;  --id_index)  {
	if  (ident_table [id_index].name != 0)  {
	    ident_table [id_index].name = strdup (ident_table [id_index].name);
	}
    }

    session_help ();
    init_lexer ();
    ERROR_THRESHOLD = 0;

    parse ();
    return  0;
}
