/*
	parse.c	-	Bill Nickerson, 1988

	Implements routines to produce code for formula evaluation.

	Export: parse
	Static: emit, match, expr, term, exponent, factor
*/

#include <stdio.h>
#include "kwords.h"

/*
	Vars imported from lexer.
*/
extern int lex();
extern char *inptr;
extern double num;

/*
	Forward declarations.
*/
extern void emit(), expr(), term(), exponent(), factor();

/* Set to 1 when formula is not correct, 0 otherwise. "fnvarudes" indicates
   whether or not a user-variable ("#") was encountered.
*/
int iswrong;
int fnvarused;

/* Pointer to p-code array when emitting code. */
double *codeptr;

/* Next token for parser. */
int lookahead;

/*
	formula	- string containing formula to evaluate.
	code	- array to store emitted p-code in or NULL.

	Parse the given formula and put p-code in code array. If pcode is NULL
	then no code is produced and only parsing is done.
	If parsing is successful, iswrong will be 0, else it will be 1.
	Returns nothing.
*/
void parse( formula, code )
char *formula;
double code[];
{
	inptr = formula;
	codeptr = code;
	iswrong = 0;
	fnvarused = 0;
	lookahead = lex();

	expr();
	emit((double)STOPt);

	if (lookahead != EOF)
		iswrong = 1;
}

/*---------------------------------------------------------------------------
	Parsing routines.
*/
void emit( tok )
double tok;
{
	if (codeptr != NULL)
		*codeptr++ = tok;
}

void match( tok )
int tok;
{
	if (lookahead == tok)
		lookahead = lex();
	else	iswrong = 1;
}

void expr()
{
	int tok;

	term();
	while ((tok = lookahead) == PLUSt || tok == MINUSt || tok == SUMt)
	{
		match(tok);
		term();
		emit((double)tok);
	}
}

void term()
{
	int tok;

	exponent();
	while ((tok = lookahead) == TIMESt || tok == DIVt || tok == MODt)
	{
		match(tok);
		exponent();
		emit((double)tok);
	}
}

void exponent()
{
	int tok, exponents = 0;

	/*
		Exponent are right associative, so we have to gather them
		all up and emit them later.
	*/
	factor();
	while ((tok = lookahead) == POWERt)
	{
		exponents++;
		match(tok);
		factor();
	}
	while (exponents--)
		emit((double)POWERt);
}

void factor()
{
	int tok, minuses;

	switch (tok = lookahead)
	{
	/*
		Monadic operators.
	*/
	case ACOSt:
	case COSHt:
	case COSt:
	case ASINt:
	case SINHt:
	case SINt:
	case ATANt:
	case TANHt:
	case TANt:
	case EXPt:
	case LNt:
	case LOGt:
	case CEILt:
	case FLOORt:
	case ABSt:
	case FACTt:	
	case SQRTt:	match(tok);
	/*
		Bracketed expressions.
	*/
	case LPt:	match(LPt);
			expr();
			match(RPt);
			if (tok != LPt)
				emit((double)tok);
			break;
	/*
		Only one id per function. A special code is emitted for it.
	*/
	case FNVARt:	match(tok);
			emit((double)FNVARt);
			fnvarused = 1;
			break;
	/*
		Push constant numbers.
	*/
	case NUMt:	match(tok);
			emit((double)PUSHt);
			emit(num);
			break;
	/*
		Unary minus. Emit a negation instruction only if there is
		an odd number of minus signs in front to reduce code.
	*/
	case MINUSt:	for (minuses = 0; lookahead == MINUSt; minuses++)
				match(MINUSt);
			factor();
			if (minuses & 1)
				emit((double)NEGt);
			break;
	/*
		Error detected.
	*/
	default:	iswrong = 1;
			break;
	}
}
