#ifndef lint
static char    *RCSid = "$Id: parse.c%v 3.38.2.121 1993/04/30 00:02:32 woo Exp woo $";
#endif


/* GNUPLOT - parse.c */
/*
 * Copyright (C) 1986 - 1993   Thomas Williams, Colin Kelley 
 *
 * Permission to use, copy, and distribute this software and its documentation
 * for any purpose with or without fee is hereby granted, provided that the
 * above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation. 
 *
 * Permission to modify the software is granted, but not the right to distribute
 * the modified code.  Modifications are to be distributed as patches to
 * released version. 
 *
 * This software is provided "as is" without express or implied warranty. 
 *
 *
 * AUTHORS 
 *
 * Original Software: Thomas Williams,  Colin Kelley. 
 *
 * Gnuplot 2.0 additions: Russell Lang, Dave Kotz, John Campbell. 
 *
 * Gnuplot 3.0 additions: Gershon Elber and many others. 
 *
 */

#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
#include <math.h>
#include "plot.h"

#ifndef vms
#if !defined(__ZTC__) && !defined(__MSC__)
extern int      errno;
#endif
#endif

extern int      num_tokens, c_token;
extern struct lexical_unit token[];
extern char     c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN + 1];	/* name of current dummy
								 * vars */
extern struct udft_entry *dummy_func;	/* pointer to dummy variable's func */

struct value   *
pop(), *Ginteger(), *Gcomplex();
struct at_type *
temp_at(), *perm_at();
struct udft_entry *add_udf();
struct udvt_entry *add_udv();
union argument *add_action();

struct at_type  at;
#ifdef _Windows
static jmp_buf far fpe_env;
#else
static jmp_buf  fpe_env;
#endif

#define dummy (struct value *) 0

#if defined(__TURBOC__) || defined(DJGPP)
void
fpe()
#else
#if defined( __ZTC__ ) || defined( _CRAY ) || defined( sgi )|| defined( OS2 ) || defined(__EMX__)
void
fpe(an_int)
	int             an_int;
#else
#if defined( NEXT ) || defined( VMS)
void
fpe(int an_int)
#else
#ifdef sgi
void
fpe(int sig, int code, struct sigcontext * sc)
/*
 * void fpe(an_int) int an_int; 
 */
#else
fpe()
#endif
#endif
#endif				/* __ZTC__ || _CRAY */
#endif				/* __TURBOC__ */
{
#if defined(MSDOS) && !defined(__EMX__) && !defined(DJGPP) && !defined(_Windows) || defined(DOS386)
	/* thanks to lotto@wjh12.UUCP for telling us about this  */
	_fpreset();
#endif
#if defined(MSDOS) && defined(__EMX__)
	(void)signal(SIGFPE, (void *)fpe);
#else
#ifdef DJGPP
	(void)signal(SIGFPE, (SignalHandler)fpe);
#else
#ifdef OS2
	(void) signal(an_int, SIG_ACK);
#else
	(void)signal(SIGFPE, fpe);
#endif
#endif
#endif
#ifdef ATARI
	/* do we need this ? (AL) */
	fprintf(stderr, "floating point exception!\n");
#endif
	undefined = TRUE;
	longjmp(fpe_env, TRUE);
}


#ifdef apollo
#include <apollo/base.h>
#include <apollo/pfm.h>
#include <apollo/fault.h>

/*
 * On an Apollo, the OS can signal a couple errors that are not mapped into
 * SIGFPE, namely signalling NaN and branch on an unordered comparison.  I
 * suppose there are others, but none of these are documented, so I handle
 * them as they arise. 
 *
 * Anyway, we need to catch these faults and signal SIGFPE. 
 */

pfm_$fh_func_val_t apollo_sigfpe(pfm_$fault_rec_t & fault_rec)
{
	kill(getpid(), SIGFPE);
	return pfm_$continue_fault_handling;
}

apollo_pfm_catch()
{
	status_$t status;
	pfm_$establish_fault_handler(fault_$fp_bsun, pfm_$fh_backstop,
				       apollo_sigfpe, &status);
	pfm_$establish_fault_handler(fault_$fp_sig_nan, pfm_$fh_backstop,
				       apollo_sigfpe, &status);
}
#endif


evaluate_at(at_ptr, val_ptr)
	struct at_type *at_ptr;
	struct value   *val_ptr;
{
	double          temp, real();

	undefined = FALSE;
	errno = 0;
	reset_stack();

#ifndef DOSX286
	if (setjmp(fpe_env))
		return(0);		/* just bail out */
#if defined(MSDOS) && defined(__EMX__)
	(void)signal(SIGFPE, (void *)fpe);
#else
#if DJGPP
	(void)signal(SIGFPE, (SignalHandler)fpe);
#else
	(void)signal(SIGFPE, fpe);	/* catch core dumps on FPEs */
#endif
#endif
#endif

	execute_at(at_ptr);

#ifndef DOSX286
	(void)signal(SIGFPE, SIG_DFL);
#endif

	if (errno == EDOM || errno == ERANGE) {
		undefined = TRUE;
	} else {
		(void)pop(val_ptr);
		check_stack();
	}
	/* At least one machine (ATT 3b1) computes Inf without a SIGFPE */
	temp = real(val_ptr);
	if (temp > VERYLARGE || temp < -VERYLARGE) {
		undefined = TRUE;
	}
}


struct value   *
const_express(valptr)
	struct value   *valptr;
{
	register int    tkn = c_token;
	if (END_OF_COMMAND)
		int_error("constant expression required", c_token);
	evaluate_at(temp_at(), valptr);	/* run it and send answer back */
	if (undefined) {
		int_error("undefined value", tkn);
	}
	return (valptr);
}


struct at_type *
temp_at()
{				/* build a static action table and return its
				 * pointer */
	at.a_count = 0;		/* reset action table !!! */
	express();
	return (&at);
}


/* build an action table, put it in dynamic memory, and return its pointer */

struct at_type *
perm_at()
{
	register struct at_type *at_ptr;
	register unsigned int len;

	(void)temp_at();
	len = sizeof(struct at_type) -
		(MAX_AT_LEN - at.a_count) * sizeof(struct at_entry);
	at_ptr = (struct at_type *) alloc((unsigned long)len, "action table");
	(void)memcpy(at_ptr, &at, len);
	return (at_ptr);
}


#ifdef NOCOPY
/*
 * cheap and slow version of memcpy() in case you don't have one 
 */
memcpy(dest, src, len)
	char           *dest, *src;
	unsigned int    len;
{
	while (len--)
		*dest++ = *src++;
}
#endif				/* NOCOPY */


express()
{				/* full expressions */
	xterm();
	xterms();
}

xterm()
{				/* ? : expressions */
	aterm();
	aterms();
}


aterm()
{
	bterm();
	bterms();
}


bterm()
{
	cterm();
	cterms();
}


cterm()
{
	dterm();
	dterms();
}


dterm()
{
	eterm();
	eterms();
}


eterm()
{
	fterm();
	fterms();
}


fterm()
{
	gterm();
	gterms();
}


gterm()
{
	hterm();
	hterms();
}


hterm()
{
	unary();		/* - things */
	iterms();		/* * / % */
}


factor()
{
	register int    value;

	if (equals(c_token, "(")) {
		c_token++;
		express();
		if (!equals(c_token, ")"))
			int_error("')' expected", c_token);
		c_token++;
	} else if (isnumber(c_token)) {
		convert(&(add_action(PUSHC)->v_arg), c_token);
		c_token++;
	} else if (isletter(c_token)) {
		if ((c_token + 1 < num_tokens) && equals(c_token + 1, "(")) {
			value = standard(c_token);
			if (value) {	/* it's a standard function */
				c_token += 2;
				express();
				if (equals(c_token, ",")) {
					while (equals(c_token, ",")) {
						c_token += 1;
						express();
					}
				}
				if (!equals(c_token, ")"))
					int_error("')' expected", c_token);
				c_token++;
				(void)add_action(value);
			} else {
				int             call_type = (int)CALL;
				value = c_token;
				c_token += 2;
				express();
				if (equals(c_token, ",")) {
					struct value    num_params;
					num_params.type = INTGR;
					num_params.v.int_val = 1;
					while (equals(c_token, ",")) {
						num_params.v.int_val += 1;
						c_token += 1;
						express();
					}
					add_action(PUSHC)->v_arg = num_params;
					call_type = (int)CALLN;
				}
				if (!equals(c_token, ")"))
					int_error("')' expected", c_token);
				c_token++;
				add_action(call_type)->udf_arg = add_udf(value);
			}
		} else {
			if (equals(c_token, c_dummy_var[0])) {
				c_token++;
				add_action(PUSHD1)->udf_arg = dummy_func;
			} else if (equals(c_token, c_dummy_var[1])) {
				c_token++;
				add_action(PUSHD2)->udf_arg = dummy_func;
			} else {
				int             i, param = 0;
				for (i = 2; i < MAX_NUM_VAR; i++) {
					if (equals(c_token, c_dummy_var[i])) {
						struct value    num_params;
						num_params.type = INTGR;
						num_params.v.int_val = i;
						param = 1;
						c_token++;
						add_action(PUSHC)->v_arg = num_params;
						add_action(PUSHD)->udf_arg = dummy_func;
						break;
					}
				}
				if (!param) {	/* defined variable */
					add_action(PUSH)->udv_arg = add_udv(c_token);
					c_token++;
				}
			}
		}
	}
	/* end if letter */
	else
		int_error("invalid expression ", c_token);

	/* add action code for ! (factorial) operator */
	while (equals(c_token, "!")) {
		c_token++;
		(void)add_action(FACTORIAL);
	}
	/* add action code for ** operator */
	if (equals(c_token, "**")) {
		c_token++;
		unary();
		(void)add_action(POWER);
	}
}



xterms()
{				/* create action code for ? : expressions */

	if (equals(c_token, "?")) {
		register int    savepc1, savepc2;
		register union argument *argptr1, *argptr2;
		c_token++;
		savepc1 = at.a_count;
		argptr1 = add_action(JTERN);
		express();
		if (!equals(c_token, ":"))
			int_error("expecting ':'", c_token);
		c_token++;
		savepc2 = at.a_count;
		argptr2 = add_action(JUMP);
		argptr1->j_arg = at.a_count - savepc1;
		express();
		argptr2->j_arg = at.a_count - savepc2;
	}
}


aterms()
{				/* create action codes for || operator */

	while (equals(c_token, "||")) {
		register int    savepc;
		register union argument *argptr;
		c_token++;
		savepc = at.a_count;
		argptr = add_action(JUMPNZ);	/* short-circuit if already
						 * TRUE */
		aterm();
		argptr->j_arg = at.a_count - savepc;	/* offset for jump */
		(void)add_action(BOOLE);
	}
}


bterms()
{				/* create action code for && operator */

	while (equals(c_token, "&&")) {
		register int    savepc;
		register union argument *argptr;
		c_token++;
		savepc = at.a_count;
		argptr = add_action(JUMPZ);	/* short-circuit if already
						 * FALSE */
		bterm();
		argptr->j_arg = at.a_count - savepc;	/* offset for jump */
		(void)add_action(BOOLE);
	}
}


cterms()
{				/* create action code for | operator */

	while (equals(c_token, "|")) {
		c_token++;
		cterm();
		(void)add_action(BOR);
	}
}


dterms()
{				/* create action code for ^ operator */

	while (equals(c_token, "^")) {
		c_token++;
		dterm();
		(void)add_action(XOR);
	}
}


eterms()
{				/* create action code for & operator */

	while (equals(c_token, "&")) {
		c_token++;
		eterm();
		(void)add_action(BAND);
	}
}


fterms()
{				/* create action codes for == and !=
				 * operators */

	while (TRUE) {
		if (equals(c_token, "==")) {
			c_token++;
			fterm();
			(void)add_action(EQ);
		} else if (equals(c_token, "!=")) {
			c_token++;
			fterm();
			(void)add_action(NE);
		} else
			break;
	}
}


gterms()
{				/* create action code for < > >= or <=
				 * operators */

	while (TRUE) {
		/* I hate "else if" statements */
		if (equals(c_token, ">")) {
			c_token++;
			gterm();
			(void)add_action(GT);
		} else if (equals(c_token, "<")) {
			c_token++;
			gterm();
			(void)add_action(LT);
		} else if (equals(c_token, ">=")) {
			c_token++;
			gterm();
			(void)add_action(GE);
		} else if (equals(c_token, "<=")) {
			c_token++;
			gterm();
			(void)add_action(LE);
		} else
			break;
	}

}



hterms()
{				/* create action codes for + and - operators */

	while (TRUE) {
		if (equals(c_token, "+")) {
			c_token++;
			hterm();
			(void)add_action(PLUS);
		} else if (equals(c_token, "-")) {
			c_token++;
			hterm();
			(void)add_action(MINUS);
		} else
			break;
	}
}


iterms()
{				/* add action code for * / and % operators */

	while (TRUE) {
		if (equals(c_token, "*")) {
			c_token++;
			unary();
			(void)add_action(MULT);
		} else if (equals(c_token, "/")) {
			c_token++;
			unary();
			(void)add_action(DIV);
		} else if (equals(c_token, "%")) {
			c_token++;
			unary();
			(void)add_action(MOD);
		} else
			break;
	}
}


unary()
{				/* add code for unary operators */
	if (equals(c_token, "!")) {
		c_token++;
		unary();
		(void)add_action(LNOT);
	} else if (equals(c_token, "~")) {
		c_token++;
		unary();
		(void)add_action(BNOT);
	} else if (equals(c_token, "-")) {
		c_token++;
		unary();
		(void)add_action(UMINUS);
	} else
		factor();
}
