/*	@(#)bs.c	2.1		*/
/*	3.0 SID #	1.3	*/
/* To compile: cc -i -O atof.c bs.c string.c -lm

	BS is a compiler/interpreter
	Source is converted to reverse Polish in comp()
	and executed in execute().

	The Polish string is stored as an array of int's
	(a weakness since all kinds of pointers and doubles are stored in it).

	`Instruction' format is given below in the #define's.
	Definitions:
		S stands for the execute stack (estack)
		op is any operator
		, separates polish syllables in the instruction string
		  Syllables may be ints(operators, short const...),
		  character/function pointers(for jumps...),
		  string constants, or double constants-- all int alligned.
		N the internal array of named variables

*/
#include <ctype.h>
#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <setjmp.h>
#define DIGIT isdigit(*Lp)
#define LETTER isalpha(*Lp)
#define LETNUM isalnum(*Lp)
#define NUMBER (isdigit(*Lp)||*Lp=='.')
#define EOL (*Lp == '\n' || *Lp == '\0' || *Lp == '#')
#define SKIP while(*Lp==' '||*Lp=='\t')++Lp
#define EQL(a,b) !strncmp((a),(b),6)
#define EQS(a,b) !strcmp((a),(b))
/* Table sizes */
#define NAMES		50
#define NAMEGRPS	20
#define INSTRUCTIONS	3000
#define F_STACK		50
#define E_STACK		200
#define LABELS		100
#define STRTEMPS	50
#define J_STACK		5
/* Operators & statement keywords */
#define ASG 1 /* ASG, int subscript in N */
#define LASG 2 /* LASG, int subscript of local var in S */
#define ASGSUBS 3 /* ASGSUBS, int # dimensions, int subscr in N */
/* Binary operators operate as: (S-1) op S -> --S
   arithmetic operators convert both operands to double.
   comparison operators do string compare iff both operands are strings.
   AND and OR treat non-null strings as true.
*/
#define AND 4
#define OR 5
#define EQ 6
#define NEQ 7
#define GT 8
#define LT 9
#define GEQ 10
#define LEQ 11
#define ADD 12
#define SUBT 13
#define MULT 14
#define DIV 15
#define EXPO 16
#define MOD 17
#define CAT 18
/* Unary Operators: op S -> S */
#define NOT 19
#define NEG 20
#define LINE 21 /* LINE, int statement number */
#define DUMP 22
#define TRACE 23
#define ONINTR 24 /* ONINTR, int subscr in Label */
#define EXTR 25 /* Dup top of stack: S -> ++S */
/* Primaries:  primary -> ++S */
#define DBLCONS 26 /* DBLCONS, 64 bits of double constant */
#define INTCONS 27 /* INTCONS, int-sized constant */
#define STRING 28 /* STRING, pointer to null-term string */
#define NAME 29 /* NAME, int subscr in N */
#define SUBSCR 30 /* SUBSCR, dimensions, subscr in N */
#define LNAME 31 /* LNAME, int subscr in S relative to function */
#define FUNCCALL 32 /* FUNCCALL, int subscr in Label, int arg count */
#define FUNCDEF 33 /* FUNCDEF, int decl args, int args+local var */
#define BUILTIN 34 /* BUILTIN, builtin #, # of args */
#define LIBRTN 45 /* LIBRTN, func pointer */
#define BREAK 35 /* BREAK, pointer to pointer to next instr */
#define GOTO 36 /* GOTO, pointer to next instr */
#define JUMP 37 /* JUMP, pointer */
#define IF 38 /* IF, pointer for false */
#define EXIT 39
#define RETURN 40
#define FRETURN 41
#define INTSET 42 /* INTSET, expression..., INTERROGATE */
#define INTERROGATE 43
#define SELECT 44 /* SELECT, maximum -- as in ``(0,1,2)[i]'' */
/* the following do not appear in the execute() switch */
#define IBASE 46 /* IBASE, input base (o,d,x) */
#define OBASE 47 /* OBASE, output base (o,d,x) */
#define INCR 60 /* INCR/DECR change variable values */
#define DECR 58
#define FI 62
#define CONTINUE 63 /* generates a JUMP */
#define COMPILE 48
#define EXECUTE 49
#define FOR 50
#define RUN 51
#define WHILE 52
#define STOP 53 /* uses zero for op */
#define CLEAR 54
#define NEXT 55
#define ELSE 56
#define NUF 57
#define INCLUDE 59
#define ELIF 61
#define EDIT 64
/* Misc */
#define INPUT 80
#define OUTPUT 81
#define ALLOC	127
#define ARRAY	126
#define DOUBLE 0
/* Builtin `builtins' */
#define ARG 1
#define EVAL 2
#define FORMAT 3
#define LAST 4
#define RAND 5
#define SUBSTR 6
#define INDEX 7
#define SIZE 8
#define NARG 9
#define IO 10
#define ACCESS 11
#define FTYPE 12
#define MATCH 13
#define MSTRING 14
#define TRANSLATE 15
#define GRAPH 16
#define TABLE 17
#define ITEM 18
#define KEY 19
#define ISKEY 20

#define MKDOUBLE(x) if((x)->t!=DOUBLE)mkdouble(x)
#define DBLSIZE	sizeof (double)
#define INTSIZE	sizeof (int)
#define INTBITS (INTSIZE*8-2)

char *Resnames[] = {
	"if", "else", "elif", "fi", "goto",
	"exit", "q", "return", "freturn", "for", "next", "while",
	"dump", "trace", "onintr", "run", "clear", "break", "continue",
	"fun", "nuf", "compile", "execute", "stop", "include",
	"ibase", "obase", "edit",
	0
};
char Rescode[] = {
	IF, ELSE, ELIF, FI, GOTO,
	EXIT, EXIT, RETURN, FRETURN, FOR, NEXT, WHILE,
	DUMP, TRACE, ONINTR, RUN, CLEAR, BREAK, CONTINUE,
	FUNCDEF, NUF, COMPILE, EXECUTE, STOP, INCLUDE,
	IBASE, OBASE, EDIT
};
char *Fnames[] = {
	"arg", "narg", "rand", "eval", "format", "last",
	"substr", "index", "trans", "size",
	"open", "close", "access", "ftype", "match", "mstring", "plot",
	"table", "item", "key", "iskey",
	0
};
short Fargct[] = {
	/* >=0 is exact arg count, <0 is -(N+1) minimum args */
	1, 0, 0, 1, 2, 0,
	3, 2, 3, 1,
	3, 1, 2, 1, 2, 1, -2,
	2, 2, 0, 2
};
double intpow();
char Fcode[] = {
	ARG, NARG, RAND, EVAL, FORMAT, LAST,
	SUBSTR, INDEX, TRANSLATE, SIZE,
	IO, IO, ACCESS, FTYPE, MATCH, MSTRING, GRAPH,
	TABLE, ITEM, KEY, ISKEY
};
char *Libnames[] = {
	"atan", "ceil", "cos", "exp", "abs", "floor", "log", "sin", "sqrt", 0
};
double (*Libcode[])() = {
	atan, ceil, cos, exp, fabs, floor, log, sin, sqrt
};

union value {
	double	d;
	char	*sp;
	struct {
		FILE	*file;
		char	ffunc, f_or_p;
	} io;
	char	ch[DBLSIZE];
	int	intg[DBLSIZE/INTSIZE];
	struct {
		struct names	*head,
				*lastref;
	} ar_hdr;
	int	*htabl; /* really a `struct htab' */
};
struct names {
	union value v;
	char	t,
		set;
	union {
		char	sname[6];
		struct {
			int	subscr; /* stored as a complement */
			struct	names *next;
		} ar;
	} u;
} *Np[NAMEGRPS];
struct estack {
	union value v;
	char	t,
		set;
} Estack[E_STACK];
struct htitem {
	char *ht_key;
	struct estack ht_v;
};
struct htab {
	short ht_size;
	struct htitem ht_item[1];
};
struct fstack {
	int	*fip;
	struct	estack *estk, *estkp;
	char	callargs,
		tot_var,
		dclargs,
		fname;
} Fstack[F_STACK], *Fstackp = Fstack, *Ftrace = Fstack;
struct label {
	int	*l_instr;
	char	l_name[6];
} Label[LABELS];
char	Locname[10][6];
int	Lnames;
struct jump {
	int	*fail;
	jmp_buf	save;
	struct fstack *f_stackp;
	struct estack *e_stackp;
} Jmp[J_STACK], *J = Jmp;
struct jump Intr;
jmp_buf	Reset, Afterr, Remain;

short	State;
short	Cerrs = 0;
short	Trace = 0;
short	Expr = 0;
int	Instr[INSTRUCTIONS];
int	Sinstr[256];
int	*Ip = Instr;
int	*Oip = 0;
short	Namegrps = 0, LastN = 0;
short	LastS = 0;
short	Nest = 0;
int	Token;
char	*Lp;
short	Last_op = 0;
struct estack Last;
int	Lnum, Savelnum;
char	*Filename;
short	Cont;
FILE	*Input, *Output, *Sinput;
FILE	*Graphout;
int	Gio = 0;	/* Any output on plotter */
int	Ibase = 10, Obase = 10;
char	Line[512];
char	*Key;
short	Iskey;
char	*Temps[STRTEMPS];
int	Ti = -1;
long	Time;
double	mkdouble();
char	*to_str(), *calloc(), *malloc(), *salloc(), *substr(), *trans();
struct estack *execute();
double	atof();
extern char	*Atof;
struct names 	*getar(), *lookup(), *nextname();
int	Argc;
char	**Argv;
struct 	stat Statb;
extern	char *Mstring[];
extern int nbra;

union gen {
	int i;
	int *ip;
	char *cp;
	struct names *np;
	struct estack *esp;
	struct fstack *fp;
	struct htab *htbl;
	struct htitem *ht;
};

intr()
{
	putchar('\n');
	(void)signal(SIGINT, intr);
	if(Intr.e_stackp)
		longjmp(Intr.save, 1);
	clearstk(Estack+E_STACK-1, Estack);
	fstat(fileno(stdout), &Statb);
	if((Statb.st_mode & S_IFMT) == S_IFIFO)
		exit(2);
	fstat(fileno(stdin), &Statb);
	if((Statb.st_mode & S_IFMT) == S_IFIFO)
		exit(2);
	fprintf(stderr, "\nready\n");
	fclose(Input), fclose(stdin);
	Input = fopen("/dev/tty", "r");
	setbuf(Input, NULL);
	if(Sinput)
		fclose(Sinput), Sinput = 0;
	setup();
}
setup()
{
	LastS = Lnum;
	State = EXECUTE;
	Ftrace = Fstackp;
	Fstackp = Fstack;
	longjmp(Reset, 1);
}
fpe()
{
	error("Floating exception");
}

sigpipe()
{
	error(Gio ? "Bad graph output" : "Pipe closed");
}

main(argc, argv)
char **argv;
{
	register lc;

	(void)setjmp(Remain);
	Ip = Instr;
	Argc = argc;
	Argv = argv;
	time(&Time);
	srand((unsigned)Time);
	Output = stdout;
	if(argc > 1) {
		Input = fopen(argv[1], "r");
		if(Input == NULL) {
			fprintf(stderr, "Cannot open %s\n", argv[1]);
			exit(1);
		}
		State = COMPILE;
	} else
		Input = stdin, State = EXECUTE;

again:
	(void)setjmp(Reset);
	io(3, "get", 0.0, "\0", "r");
	io(3, "put", 1.0, "\0", "w");
	io(3, "puterr", 2.0, "\0", "w");
	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void)signal(SIGINT, intr);
	(void)signal(SIGFPE, fpe);
	(void)signal(SIGPIPE, sigpipe);
	Lnames = 0;
	if(Sinput)
		Lnum = 0;
	else
		Lnum = Savelnum, Savelnum = 0;
	lc = 0;
	(void)setjmp(Afterr);
	while(fgets(&Line[lc], 512-lc, Input) != NULL) {
		lc = strlen(Line) - 2;
		if(Line[lc] == '\\') {
			++Cont;
			continue;
		}
		lc = 0;
		Lp = Line;
		if(State == COMPILE)
			statement();
		else
			singstat();
	}
	Filename = 0;
	if(Input != stdin) {
		fclose(Input);
		if(Sinput) {
			Input = Sinput;
			Sinput = 0;
		}
		else {
			Input = stdin;
			State = EXECUTE;
		}
		goto again;
	}
	exit(0);
}

statement()
{
	register char *lp;
	char tname[16];

	++Lnum;
	/* TEMP check for exec of this file */
	if(Lnum==1 && !strncmp(Line, "bs ", 3) && !strncmp((Line+strlen(Line)-5), "exit", 4))
		return;
	SKIP;
	if(EOL || *Lp == '#')
		return;
	lp = Lp;
	if(aname(tname)) {
		SKIP;
		if(*Lp++ == ':')
			(void)cklabel(tname, Ip);
		else
			Lp = lp;
	}
	if(Ip > (Instr+INSTRUCTIONS-100))
		cerror("Out of code space");
	op(LINE);
	push(Lnum);
	comp();
	if(!EOL)
		cerror("Invalid expression");
	Lnum += Cont, Cont = 0;
}
singstat()
{
	register * saveip = Ip;
	register v;
	register struct estack *ep;

	if(!Oip)
		Oip = Ip;
	if(*Lp == '!') {
		++Lp;
		fexec(Lp);
		puts("!");
		return;
	}
	if(*Lp == '#')
		return;
	Ip = Sinstr;
	Nest = 0;
	*Ip = 0;
	v = comp();
	if(!EOL)
		cerror("Invalid expression");
	push(0);
	ep = execute(Sinstr, Estack);
	if(v && Last_op!=ASG && Last_op!=ASGSUBS && *to_str(ep))
		fputs(to_str(ep), Output), putc('\n', Output), fflush(Output);
	Lnum = 0;
	if(State == EXECUTE)
		grio();
	if(State == COMPILE && saveip >= Sinstr)
		Ip = Instr;
	else
		Ip = saveip;
}

fexec(s)
char * s;
{
	register v;
	static sts;

	if((v = fork()) == 0) { /* child */
		(void)signal(SIGINT, SIG_DFL);
		execl("/bin/sh", "sh", "-c", s, 0);
		exit(0);
	} else if(v == -1) {
		cerror("Cannot fork");
	} else {
		(void)signal(SIGINT, SIG_IGN);
		wait(&sts);
		(void)signal(SIGINT, intr);
	}
}

comp()
{
	int ns;
	char *savelp;
	static *savefun;
	static struct { int tp, *loc; } s[20];
	register i;
	register union gen save1, save2, save3;
	char tname[20];

	savelp = Lp;
	save2.ip = 0;
	if(!aname(tname)) {
		return expr(0);
	}
	if((ns = ckname(tname, Resnames)) == -1) {
		Lp = savelp;
		return expr(0);
	}
	switch(Rescode[ns]) {
	case IF:
elseif:
		s[Nest].tp = IF;
elif:
		expr(1);
		op(IF);
		s[Nest++].loc = Ip++;
		SKIP;
		if(!EOL) {
			comp();
			*(s[--Nest].loc) = (int)Ip;
		}
		break;
	case ELSE:
	case ELIF:
		if(s[--Nest].tp != IF && s[Nest].tp != ELIF)
			cerror("Else: no if");
		save1.ip = s[Nest].loc;
		op(JUMP);
		s[Nest].tp = Rescode[ns];
		s[Nest++].loc = Ip++;
		*save1.ip = (int)Ip;
		if(Rescode[ns] == ELIF) {
			s[Nest].tp = ELIF;
			goto elif;
		}
		SKIP;
		if(*Lp == 'i' && *(Lp+1) == 'f') {
			Lp += 2;
			goto elseif;
		}
		break;
	case FI:
		Ip -= 2; /* get rid of LINE */
fifi:
		if(s[--Nest].tp != IF && s[Nest].tp != ELSE && s[Nest].tp != ELIF)
			cerror("Fi: no if");
		*(s[Nest].loc) = (int)Ip;
		if((s[Nest].tp == ELIF || s[Nest-1].tp == ELIF)
		&& (s[Nest-1].tp == IF || s[Nest-1].tp == ELIF))
			goto fifi;
		SKIP;
		if(*Lp == 'f' && *(Lp+1) == 'i') {
			Lp += 2;
			goto fifi;
		}
		break;
	case GOTO:
		if(aname(tname)) {
			op(GOTO);
			push(cklabel(tname, (int *)0));
			break;
		} else
			cerror("?");
	case EDIT:
		sprintf(tname, "ed %s", Argv[1]);
		fexec(tname);
		longjmp(Remain, 1);
	case IBASE:
	case OBASE:
		SKIP;
		i = atof(Lp);
		Lp = Atof;
		if(i != 10 && i!= 8 && i != 16)
			cerror("ibase/obase must be 8, 10, or 16");
		if(Rescode[ns] == IBASE)
			Ibase = i;
		else
			Obase = i;
		break;
	case EXIT:
	case RETURN:
	case TRACE:
		if(!expr(0))
			op(INTCONS), push(0);
		op(Rescode[ns]);
		break;
	case FRETURN:
		op(FRETURN);
		break;
	case WHILE:
		s[Nest].tp = FOR;
		s[Nest++].loc = Ip;
		expr(0);
		goto forop;
	case FOR:
		/* 1st expr */
		save1.cp = Lp;
		expr(0);
		if(*Lp == ',') { /* for init,test,incr */
			++Lp;
			op(JUMP);
			save2.ip = Ip++; /* for JUMP around incr */
			s[Nest].tp = FOR;
			s[Nest++].loc = Ip;
			save3.cp = Lp; /* to rescan test */
			save1.ip = Ip;
			expr(0); /* throw away to get incr */
			Ip = save1.ip;
			if(*Lp != ',')
				goto forerr;
			++Lp;
			expr(0); /* incr */
			*save2.ip = (int)Ip;
			save2.cp = Lp;
			Lp = save3.cp; /* do test for real */
			expr(0);
			Lp = save2.cp;
		} else  { /* for init limit */
			if(Last_op!=ASG
			&& Last_op != LASG && Last_op!=ASGSUBS)
forerr:				cerror("For");
			op(JUMP); /* setup jump around incr */
			save2.ip = Ip++;
			save3.cp = Lp;
			s[Nest].tp = FOR; /* loc for jump from `next' */
			s[Nest++].loc = Ip;
			Lp = save1.cp; /* re-parse left side of 1st expr for incr */
			e9(1);
			Lp = save3.cp;
			op(INCR);
			*save2.ip = (int)Ip; /* fill in jump around init */
			save3.cp = Lp; /* re-do init */
			Lp = save1.cp;
			e9(1);
			Lp = save3.cp;
			expr(1);
			op(LEQ);
		}
forop:		op(IF);
		s[Nest].loc = Ip++; /* setup for jump out of loop */
		s[Nest++].tp = NEXT;
		SKIP; if(!EOL) {
			comp();
			goto next;
		}
		break;
	case NEXT:
		Ip -= 2; /* get rid of LINE */
next:
		if(s[--Nest].tp != NEXT)
			cerror("Next: no for");
		save1.ip = s[Nest].loc;
		op(JUMP); /* jump back to incr & test */
		*Ip++ = (int)s[--Nest].loc;
		*save1.ip = (int)Ip; /* fill in jump out of loop */
		break;
	case BREAK:
		for(i = Nest-1; i>=0; --i)
			if(s[i].tp == NEXT) {
				op(BREAK);
				*Ip++ = (int)s[i].loc;
				break;
			}
		if(i < 0)
brkerr:			cerror("No for");
		break;
	case CONTINUE:
		for(i = Nest-1; i>=0; --i)
			if(s[i].tp == FOR) {
				op(JUMP);
				*Ip++ = (int)s[i].loc;
				break;
			}
		if(i < 0)
			goto brkerr;
		break;
	case FUNCDEF:
		if(!aname(tname))
funerr:			cerror("Func def.");
		(void)cklabel(tname, Ip);
		op(FUNCDEF); savefun = Ip++; save1.ip = Ip; Ip += 2;
		SKIP;
		i = Lnames = 0;
		if(!*Lp++ == '(') goto funerr;
		while(aname(tname)) {
			strncpy(Locname[Lnames++], tname, 6);
			++i;
			SKIP;
			if(*Lp == ',') {
				++Lp;
				continue;
			} else if(*Lp == ')') break;
			else goto funerr;
		}
		++Lp;
		while(!EOL) {
			if(aname(tname)) {
				strncpy(Locname[Lnames++], tname, 6);
				SKIP;
				if(*Lp ==',') ++Lp;
			} else goto funerr;
		}
		if(Lnames >= 10) goto funerr;
		*save1.ip = i;
		*(save1.ip+1) = Lnames;
		break;
	case NUF:
		op(INTCONS); push(0);
		op(RETURN);
		*savefun = (int)Ip;
		Lnames = 0;
		break;
	case DUMP:
		op(DUMP);
		if(aname(tname)) {
			savelp = salloc(sizeof tname + 2, ALLOC);
			strcpy(savelp, tname);
			push(savelp);
		} else
			push((char *)0);
		break;
	case ONINTR:
		i = aname(tname)? cklabel(tname, (int *)0): ALLOC;
		op(ONINTR);
		push(i);
		break;
	case RUN:
		push(0);
		if(Cerrs)
			Cerrs = 0, intr();
		srand((unsigned)(Time >> 16));
		(void)execute(Instr, Estack);
			if(Graphout) {
				pclose(Graphout);
				Graphout = 0;
			}
		setup();
	case STOP:
		op(0);
		break;
	case CLEAR:
		clear();
		setup();
	case INCLUDE:
		Savelnum = Lnum;
		if(Sinput)
			cerror("Nested `include's");
		if(Input != stdin)
			Sinput = Input;
		Ip = Sinstr;
	case COMPILE:
		State = COMPILE;
		Lnum = 1;
		if(expr(0)) {
			save1.cp = to_str(execute(Sinstr, Estack));
			Filename = malloc((unsigned)strlen(save1.cp+1));
			strcpy(Filename, save1.cp);
			Input = fopen(Filename, "r");
			if(Input == NULL) {
				Input = stdin;
				free(Filename); Filename = 0;
				cerror("Cannot open file");
			}
			*Sinstr = '\0';
			if(Rescode[ns] == INCLUDE)
				if(Ip >= Sinstr)
					Ip = Instr;
			longjmp(Reset, 1);
		}
		if(Rescode[ns] == INCLUDE)
			cerror("Missing filename");
		if(Ip < Instr || Ip > (Instr+INSTRUCTIONS-100))
			Ip = Instr;
		break;
	case EXECUTE:
		State = EXECUTE;
		break;
	}
	return 0;
}
expr(a)
{
	register *saveip = Ip;
	register namei, subs;
	register saveop;

	e1(a);
	if(*Lp == '=') {
		++Lp;
		namei = *(Ip - 1);
		if(Last_op == NAME || Last_op == LNAME) {
			Ip -= 2;
			saveop = Last_op;
			expr(1);
			op(saveop==NAME? ASG: LASG);
			push(namei);
		 } else if(Last_op == SUBSCR) {
			subs = *(Ip - 2);
			Ip -= 3;
			expr(1);
			op(ASGSUBS);
			push(subs), push(namei);
		 } else
			Lp -= 2, cerror("Name Required");
	}
	return saveip != Ip;
}
e1(a)
{
	register i;

	e2(a);
	for(i = 0; Token && *Lp == '_'; ++i) {
		++Lp;
		e2(1);
	}
	if(i)
		op(CAT),push(i);
}
e2(a)
{
	register tp;

	e3(a);
	for(tp=0; Token;tp=0) {
		if(*Lp == '&')
			tp = AND;
		else if(*Lp == '|')
			tp = OR;
		if(tp) {
			++Lp;
			if(*Lp == '&' || *Lp == '|')
				++Lp;
			e3(1);
			op(tp);
		} else
			break;
	}
}
e3(a)
{
	register i = 0;
	register opr;

	e4(a);
	if(Token && (opr = e3a())) {
more:
		++i;
		e4(1);
		op(opr);
		if(opr = e3a()) {
			op(EXTR);
			goto more;
		}
		--i;
		while(i--)
			op(AND);
	}
}
e3a()
{
	switch(*Lp++) {
	case '>':
		if(*Lp == '=') {
			++Lp;
			return GEQ;
		} else {
			return GT;
		}
	case '<':
		if(*Lp == '=') {
			++Lp;
			return LEQ;
		} else {
			return LT;
		}
	case '=':
		if(*Lp == '=') {
			++Lp;
			return EQ;
		}
	case '!':
		if(*Lp == '=') {
			++Lp;
			return NEQ;
		}
	}
	--Lp;
	return 0;
}
e4(a)
{
	e5(a);
	for(; Token;) {
		if(*Lp == '+' && Lp[1] != '+') {
			++Lp;
			e5(1);
			op(ADD);
		} else if(*Lp == '-' && Lp[1] != '-') {
			++Lp;
			e5(1);
			op(SUBT);
		} else
			break;
	}
}
e5(a)
{
	e6(a);
	for(; Token;) {
		if(*Lp == '*') {
			++Lp;
			e6(1);
			op(MULT);
		} else if(*Lp == '/') {
			++Lp;
			e6(1);
			op(DIV);
		} else if(*Lp == '%') {
			++Lp;
			e6(1);
			op(MOD);
		} else
			break;
	}
}
e6(a)
{
	e7(a);
	while(Token && *Lp == '^') {
		++Lp;
		e7(1);
		op(EXPO);
	}
}
e7(a)
{
	register *saveip;

	SKIP;
	if(*Lp == '?') {
		++Lp;
		op(INTSET);
		saveip = Ip++;
		e8(1);
		op(INTERROGATE);
		*saveip = (int)Ip;
		return;
	}
	e8(a);
}
e8(a)
{
	register opr;

	SKIP;
	while((*Lp == '-' && *(Lp+1) != '-') || *Lp == '!') {
		opr = *Lp == '-'? NEG: NOT;
		++Lp;
		e8(1);
		op(opr);
		return;
	}
	e9(a);
}
e9(a)
{
	long cvbase();
	register i, j;
	union { double db; int intg[DBLSIZE/INTSIZE]; } dbl;
	char *cp, *cp2;
	register builtin = -1;
	register incrflg = 0;
	char tname[16];

	Token = 0;
	SKIP;
	if(*Lp == '#') /* comment */
		*Lp = '\0';
	if(EOL)
		if(a)
			goto e9err;
		else
			goto ret_false;
	if(*Lp == '(') {
		++Lp;
		if((i = rlist(')')) > 1) {
			SKIP;
			if(*Lp++ != '[')
				cerror("?");
			expr(1);
			SKIP;
			if(*Lp++ != ']')
				cerror("?");
			op(SELECT); push(i);
		}
ret:
		Token = 1;
ret_false:
		SKIP;
		return;
	}
	if(NUMBER) {
		dbl.db = Ibase==10? atof(Lp): cvbase(Ibase, Lp);
		if(Lp != Atof) /* atof succeeded */ {
			Lp = Atof;
			if(dbl.db > -(1<<INTBITS) && dbl.db < (1<<INTBITS)
			&& ((int)dbl.db == dbl.db)) { /* small int constant */
				op(INTCONS);
				push((int)dbl.db);
				goto ret;
			}
			op(DBLCONS);
			for(i = 0; i < (DBLSIZE/INTSIZE); ++i)
				push(dbl.intg[i]);
			goto ret;
		}
	}
	if(*Lp == '"') {
		op(STRING);
		cp = cp2 = salloc(128, ALLOC);
		while(*++Lp != '"')
			if(EOL)
				cerror("Unbal. quote");
			else {
				if(*Lp == '\\') {
					switch(*++Lp) {
					case '"': break;
					case 'n': *Lp = '\n'; break;
					case 'b': *Lp = '\b'; break;
					case 't': *Lp = '\t'; break;
					case 'r': *Lp = '\r'; break;
					default: --Lp;
					}
				}
				*cp2++ = *Lp;
			}
		*cp2 = '\0';
		cp2 = cp;
		cp = salloc(strlen(cp2), ALLOC);
		strcpy(cp, cp2);
		free(cp2);
		push((int)cp);
		++Lp;
		goto ret;
	}
	if(*Lp=='+' && *(Lp+1)=='+')
		Lp += 2, incrflg = 1;
	else if(*Lp=='-' && *(Lp+1)=='-')
		Lp += 2, incrflg = -1;
	if(!aname(tname)) {
		if(a || incrflg)
e9err:
			cerror("Expression");
		else
			goto ret_false;
	}
	if(ckname(tname, Resnames) != -1)
		cerror("Reserved Name in Expression");
	SKIP;
	if(*Lp == '(') {
		++Lp;
		i = rlist(')');
		if((builtin = ckname(tname, Fnames)) != -1) {
			j = Fargct[builtin];
			if((j>=0 && j!=i)
			|| (i < -(j+1)))
argerr:				cerror("Arg. count");
			op(BUILTIN);
			push(Fcode[builtin]);
			push(i);
		} else if((builtin = ckname(tname, Libnames)) != -1) {
			if(i != 1)
				goto argerr;
			op(LIBRTN);
			*Ip++ = (int)Libcode[builtin];
		} else {
			op(FUNCCALL);
			push(i);
			push(cklabel(tname, (int *)0));
		}
		goto ret;
	}
	i = 0;
	while(*Lp == '[') {
		++Lp;
		i += rlist(']');
		SKIP;
	}
	if(i) {
		op(SUBSCR);
		push(i);
		if(local(tname) != -1)
			cerror("Subscript on local name");
		push((int)lookup(tname));
	} else {
		if((i = local(tname)) != -1) {
			op(LNAME);
			push(i);
		} else {
			op(NAME);
			push((int)lookup(tname));
		}
	}
	if(incrflg)
		op(incrflg == 1? INCR: DECR);
	goto ret;
}
op(a)
register a;
{
	Last_op = a;
	*Ip++ = a;
	*Ip = 0;
}
push(v)
register v;
{
	*Ip++ = v;
	*Ip = 0;
}
cerror(s)
char *s;
{
	register i ;
	char eline[128];

	if(Expr) {
		Expr = 0;
		longjmp(J->save, 1);
	}
	if(State == COMPILE)
		Ip = Instr;
	if((Lp-Line) > 80) {
		Line[0] = eline[0] = '\0';
		goto pskip;
	}
	for(i = 0; &Line[i] < Lp; ++i)
		eline[i] = Line[i]=='\t'? '\t': ' ';
	eline[i] = '\0';
pskip:
	if(Lnum) {
		if(Filename) {
			fprintf(stderr, "%s: ", Filename);
			Filename = 0;
		}
		fprintf(stderr, "Source line %d: ", Lnum);
	}
	fprintf(stderr, "%s\n%s%s^\b|\n", s, Line, eline);
	if(Lnum)
		Cerrs++, longjmp(Afterr, 1);
	else
		intr();
}
aname(np)
register char *np;
{
	register i;
	SKIP;
	if(!LETTER)
		return 0;
	for(i = 1; LETNUM; ++i)
		if(i < 16)
			*np++ = *Lp++;
		else
			++Lp;
	*np = '\0';
	return 1;
}
ckname(np, table)
register char *np, **table;
{
	register i;

	for(i = 0; *table; ++i,++table)
		if(EQL(*table, np))
			return i;
	return -1;
}

long cvbase(b, s)
register char *s;
{
	register n, c;
	register long ans = 0;

	while(c = *s++) {
		if(c >= '0' && c <= '9')
			n = c - '0';
		else if(c >= 'a' && c <= 'f')
			n = c - 'a' + 10;
		else
			break;
		ans *= b;
		ans += n;
	}
	Atof = --s;
	return ans;
}
cklabel(tname, s)
register char *tname;
register *s;
{
	register i;

	for(i = 0; i < LABELS; ++i) {
		if(EQL(tname, Label[i].l_name)) {
			if(s && Label[i].l_instr)
				cerror("Dup. label");
			if(s)
				Label[i].l_instr = s;
			return i;
		} else {
			if(Label[i].l_name[0])
				continue;
			strncpy(Label[i].l_name, tname, 6);
			Label[i].l_instr = s;
			return i;
		}
	}
	cerror("Too many labels");
}
struct names *lookup(namep)
register char *namep;
{
	register i;
	register struct names *np;

	for(i = 0; i < LastN; ++i) {
		np = nextname(i);
		if(EQL(namep, np->u.sname))
			return np;
	}
	np = nextname(LastN++);
	strncpy(np->u.sname, namep, 6);
	np->v.d = 0;
	np->t = DOUBLE;
	np->set = 0;
	return np;
}
struct names *nextname(i)
register i;
{
	register nn;

	Namegrps = i / NAMES;
	nn = i % NAMES;
	if(Namegrps >= NAMEGRPS)
symerr:
		error("Symbol table overflow");
	if(!Np[Namegrps]) {
		Np[Namegrps] = (struct names *)malloc((unsigned)sizeof(struct names)*NAMES);
		if(!(int)Np[Namegrps])
			goto symerr;
	}
	return &(Np[Namegrps][nn]);
}
rlist(delim)
char delim;
{
	register int args = 0;
	for(;;) {
		SKIP;
		if(*Lp == delim) {
			++Lp;
			return args;
		}
		expr(0);
		++args;
		SKIP;
		if(*Lp == ',') {
			++Lp;
			continue;
		}
		if(*Lp != delim)
			cerror("Wrong delimiter");
	}
}
short ht_sizes[] = { 61, 113, 229, 317, 421, 521, 761, 1049 };

struct htab *htable(sz)
{
	register i;
	register struct htab *rv;

	for(i = 0; i < 8; ++i) {
		if(sz < ht_sizes[i]) {
			rv = (struct htab *)calloc(1, sizeof (short)
				+ ht_sizes[i] * sizeof(struct htitem));
			rv->ht_size = ht_sizes[i];
			return rv;
		}
	}
	error("Table too big");
}
struct htitem *htitem(h, key)
struct htab *h;
register char *key;
{
	register struct htitem *hp;
	register s, r, q;
	int first;

	for(s = r = 0; *key; ++s)
		r += *key++;
	key -= s;

	/* quadratic quotient--see CACM Feb '70, p107 */
	q = r;
	first = r = q % h->ht_size;
	q = q / h->ht_size;
	s += q;
	do {
		hp =  &(h->ht_item[r]);
		if(hp->ht_key == (char *)0) {
			if(Iskey)
				return (struct htitem *)0;
			hp->ht_key = salloc(s, ALLOC);
			strcpy(hp->ht_key, key);
			return hp;
		}
		if(EQS(key, hp->ht_key))
			return hp;
		r = (r + s) % h->ht_size;
	} while(r != first);
	error("Table overflow");
}

struct estack *execute(instr, estackp)
register *instr;
struct estack *estackp;
{
	register struct estack *estack = estackp;
	register union gen r;
	register char *s1;
	register ct;
	register char *s2;
	int x;
	double (*func)();
	double dbl;
	char *tstr;
	int opr;
	struct estack *ep;
	static struct estack *sv_estack;
	int args;

	/* A bad spot for these tests, but ... */
	if(Lnames || Nest)
		error("Open function, if, or for");
	for(;;) {
		switch(opr = *instr++) {
		case 0: /* end-of-instr */
			Last = *estack;
			return estack;
		case DBLCONS:
			++estack;
			for(r.i = 0; r.i < (DBLSIZE/INTSIZE); )
				estack->v.intg[r.i++] = *instr++;
			estack->t = DOUBLE;
			break;
		case INTCONS:
			++estack;
			estack->v.d = *instr++;
			estack->t = DOUBLE;
			break;
		case STRING:
			++estack;
			estack->v.sp = (char *)*instr++;
			estack->t = STRING;
			break;
		case LNAME:
			++estack;
			r.esp = Fstackp->estk + *instr++;
			goto nameck;
		case NAME:
			++estack;
			r.np = (struct names *)*instr++;
nameck:
			if(r.np->t==DOUBLE || r.np->t==STRING) {
				if(*instr==INCR || *instr==DECR) {
				    MKDOUBLE(r.np);
				    r.np->v.d += *instr++==INCR? 1: -1;
				    r.np->set = ALLOC;
				}
nameset:
				estack->v = r.np->v;
				estack->t = r.np->t;
			} else if(r.np->t == INPUT) {
				if(fgets(Line,512,r.np->v.io.file)==NULL) {
					if(J != &Jmp[0]) {
						sv_estack = estack;
						longjmp(J->save, 1);
					}
					estack->v.sp = "\0";
					estack->t = STRING;
				} else {
					ct = strlen(Line);
					Line[--ct] = '\0';
					estack->v.sp = salloc(ct, 0);
					strcpy(estack->v.sp, Line);
					estack->t = STRING;
				}
			} else if(r.np->t == TABLE) {
				goto nameset;
			} else {
namerr:
				fprintf(stderr,"<%.6s>",r.np->u.sname);
				error(" name context");
			}
			break;
		case GT:
		case LT:
		case GEQ:
		case LEQ:
		case EQ:
		case NEQ:
			if((estack--)->t==STRING && estack->t==STRING)
				r.i = strcmp(estack->v.sp, (estack+1)->v.sp);
			else {
				MKDOUBLE(estack);
				MKDOUBLE(estack+1);
				r.i = (estack->v.d==(estack+1)->v.d)? 0:
					estack->v.d>(estack+1)->v.d? 1: -1;
			}
			r.i = (
				(opr==LEQ)? r.i<=0:
				(opr==EQ)? r.i==0:
				(opr==GT)? r.i>0:
				(opr==GEQ)? r.i>=0:
				(opr==LT)? r.i<0:
				r.i!=0);
			if(*instr == IF) { /* often follows RELs */
				++instr;
				goto ckif;
			}
			estack->v.d = r.i;
			estack->t = DOUBLE;
			break;
		case AND:
			r.i = true(estack--), estack->v.d = r.i&&true(estack);
			estack->t = DOUBLE;
			break;
		case OR:
			r.i = true(estack--), estack->v.d = r.i||true(estack);
			estack->t = DOUBLE;
			break;
		case ADD:
		case MULT:
		case SUBT:
		case DIV:
		case EXPO:
		case MOD:
			MKDOUBLE(estack);
			dbl = estack->v.d;
			--estack;
			MKDOUBLE(estack);
			r.i = opr;
			estack->v.d =
				r.i==ADD? estack->v.d + dbl:
				r.i==MULT? estack->v.d * dbl:
				r.i==SUBT? estack->v.d - dbl:
				r.i==DIV? estack->v.d / dbl:
				r.i==MOD? fmod(estack->v.d, dbl):
				/* EXPO--take care of pow() */
				dbl==0? 1:
				dbl>0 && (double)(int)dbl==dbl?
					intpow(estack->v.d, (int)dbl):
					pow(estack->v.d, dbl);
			break;
		case CAT:
			estack -= *instr;
			for(r.i = x = 0; r.i <= *instr; ++r.i)
				x += strlen(to_str(estack+r.i));
			s1 = s2 = salloc(x, 0);
			for(ct = 0; ct <= *instr; ++ct) {
				r.cp = to_str(estack+ct);
				while(*r.cp)
					*s1++ = *r.cp++;
			}
			++instr;
			*s1 = '\0';
			estack->v.sp = s2;
			estack->t = STRING;
			break;
		case EXTR:
			++estack;
			break;
		case NEG:
			estack->v.d = -mkdouble(estack);
			break;
		case NOT:
			estack->v.d = !true(estack);
			estack->t = DOUBLE;
			break;
		case LASG:
			r.esp = Fstackp->estk + *instr++;
			if(r.np->t==STRING && r.np->set==ALLOC)
					free(r.np->v.sp);
			goto asgn2;
		case ASG:
			r.np = (struct names *)*instr++;
asgn:
			switch(r.np->t) {
			case DOUBLE:
				break;
			case STRING:
				free(r.np->v.sp);
				break;
			case OUTPUT:
				grio();
				fputs(to_str(estack), r.np->v.io.file);
				if(r.np->v.io.ffunc != 'W')
					putc('\n', r.np->v.io.file);
				goto brk;
			default:
				goto namerr;
			}
asgn2:
			r.np->t = estack->t;
			if(estack->t == STRING) {
				if(estack->v.sp==Temps[Ti]) {
					Temps[Ti] = 0;
					r.np->v.sp = estack->v.sp;
				} else {
					r.np->v.sp = salloc(
					 strlen(estack->v.sp), ALLOC);
					strcpy(r.np->v.sp, estack->v.sp);
				}
			} else
				r.np->v.d = estack->v.d;
			r.np->set = ALLOC;
brk:			break;
		case ONINTR:
			if(setjmp(Intr.save)) {
				clearstk(estack, Intr.e_stackp);
				estack = estackp = Intr.e_stackp;
				Intr.e_stackp = 0;
				Fstackp = Intr.f_stackp;
				if((instr = (int *)Intr.fail)==0)
					exit(1);
				break;
			} else {
				Intr.e_stackp = estackp;
				if(*instr == ALLOC) {
					++instr;
					Intr.fail = 0;
					break;
				}
				Intr.f_stackp = Fstackp;
				if(!(Intr.fail = (int *)Label[*instr].l_instr))
					error("No label");
			}
			break;
		case INTSET:
			++J;
			if(setjmp(J->save)) {
				clearstk(sv_estack, J->e_stackp);
				estack = estackp = J->e_stackp;
				estack->v.d = 0;
				estack->t = DOUBLE;
				Fstackp = J->f_stackp;
				instr = (int *)J->fail;
				--J;
			} else {
				J->f_stackp = Fstackp;
				J->fail = (int *)*instr++;
				J->e_stackp = estackp;
			}
			break;
		case INTERROGATE:
			--J;
			estack->v.d = 1;
			estack->t = DOUBLE;
			break;
		case ASGSUBS:
		case SUBSCR:
			args = *instr++;
			r.np = (struct names *)*instr++;
			estack -= (opr == SUBSCR? args-1: args);
			if(r.np->t == TABLE) {
				if(args != 1)
					error("Only single dim. tables");
				r.ht = htitem((struct htab *)r.np->v.htabl, to_str(estack));
				r.esp = &(r.ht->ht_v);
				goto subsc;
			}
			for(ct = 0; ct < args; ++ct) {
				if(r.np->set == ALLOC)
					error("Subscript");
				r.np->t = ARRAY;
				x = mkdouble(estack+ct);
				if(x < 0)
					error("Subscr. range");
				r.np = getar(r.np, x);
			}
subsc:
			if(opr == SUBSCR)
				goto nameck;
			*estack = *(estack+args);
			goto asgn;
		case SELECT:
			r.i = mkdouble(estack);
			if(r.i >= *instr || r.i < 0)
				error("Range");
			estack -= *instr;
			*estack = *(estack + r.i);
			++instr;
			break;
		case IF:
			r.i = true(estack);
ckif:
			if(r.i)
				++instr;
			else
				instr = (int *)*instr;
			estack = estackp;
			break;
		case JUMP:
		case FUNCDEF: /* jump around func body */
			instr = (int *)*instr;
			break;
		case GOTO:
			instr = (int *)Label[*instr].l_instr;
			if(instr == 0)
				error("No label");
			break;
		case EXIT:
			if(Graphout)
				pclose(Graphout);
			exit(to_int(estack));
		case LINE:
			estack = estackp;
			if(estack > (Estack+E_STACK-15))
				error("Stack over");
			Lnum = *instr++;
			break;
		case FUNCCALL:
			ep = ++estack;
			r.fp = ++Fstackp;
			if(r.fp >= (Fstack+F_STACK-1))
				--Fstackp, error("Recursion");
			r.fp->estk = estack - *instr; /* less args */
			r.fp->estkp = estackp;
			r.fp->callargs = *instr++;
			r.fp->fip = (instr + 1);
			r.fp->fname = *instr;
			instr = (int *)Label[*instr].l_instr;
			if(*instr++ != FUNCDEF) {
				printf("Undefined function ");
				prtrace(r.fp);
				printf("Return:  ");
				Lp = salloc(128, 0);
				(void)fgets(Lp, 128, Input);
				Ip = (int *)(tstr = salloc(128, ALLOC));
				comp();
				(void)execute((int *)tstr, estack-1);
				free(tstr);
				goto ret;
			}
			++instr;
			r.fp->dclargs = ct = *instr++;
			r.fp->tot_var = x = *instr++;
			if(r.fp->callargs > ct)
				error("Too many args");
			estack = Fstackp->estk + ct;
			for(r.i = ct; r.i < x; ++r.i, ++estack, ++ep) {
				if(r.i < Fstackp->callargs) {
					continue;
				}
				estack->v.d = 0;
				estack->t = DOUBLE;
			}
			estack = estackp = ep;
			if(Trace)
				prtrace(Fstackp);
			break;
		case RETURN:
ret:
			Last = *estack;
			if(estack->t == STRING) {
				Last.v.sp = salloc(strlen(estack->v.sp), 0);
				strcpy(Last.v.sp, estack->v.sp);
			}
retn:
			clearstk(estack, Fstackp->estkp);
			instr = Fstackp->fip;
			estackp = Fstackp->estkp;
			estack = Fstackp->estk;
			*estack = Last;
			if(Trace) {
				--Trace;
				printf("%d: %.16s\n", (int)(Fstackp - Fstack),
					to_str(estack));
			}
			--Fstackp;
			if(Fstackp < Fstack)
				error("Return");
			break;
		case FRETURN:
			if(J != &Jmp[0]) {
				sv_estack = estack;
				longjmp(J->save, 1);
			}
			Last.t = DOUBLE;
			Last.v.d = 0;
			goto retn;
		case BREAK:
			estack = estackp;
			instr = (int *)*instr;
			instr = (int *)*instr;
			break;
		case LIBRTN:
			func = (double (*)())*instr++;
			estack->v.d = (*func)(mkdouble(estack));
			estack->t = DOUBLE;
			break;
		case BUILTIN:
			r.i = *instr++;
			args = *instr++;
			estack -= (args - 1); /* adj. stack */
			switch(r.i) {
			case RAND:
				dbl = rand();
				dbl /= 32768.;
				estack->v.d = dbl;
				estack->t = DOUBLE;
				break;
			case ARG:
				r.i = to_int(estack);
				if(Fstackp == Fstack) { /* main func */
					if(r.i >= Argc || r.i < 0)
argerr:						error("Arg");
					estack->v.sp = Argv[r.i];
					estack->t = STRING;
					break;
				}
				if(r.i > Fstackp->callargs || r.i < 1)
					goto argerr;
				--r.i;
				*estack = *(Fstackp->estk + r.i);
				break;
			case NARG:
				estack->v.d = Fstackp==Fstack?
					Argc: Fstackp->callargs;
				estack->t = DOUBLE;
				break;
			case IO: /* open and close */
				io(args, to_str(estack),
					((estack+1)->t==DOUBLE?
						(estack+1)->v.d: 99.9),
					to_str(estack+1), to_str(estack+2));
				break;
			case EVAL:
				Expr = *instr == INTERROGATE;
				Lp = to_str(estack);
				Ip = (int *)(tstr = salloc(512, ALLOC));
				comp();
				(void)execute((int *)tstr, estack-1);
				free(tstr);
				Expr = 0;
				break;
			case ACCESS:
				estack->v.d = access(to_str(estack),
					to_int(estack+1))==0? 1: 0;
				estack->t = DOUBLE;
				break;
			case FTYPE:
				if(stat(to_str(estack), &Statb) == -1) {
					estack->v.d = 0;
					estack->t = DOUBLE;
					break;
				}
				r.i = Statb.st_mode & S_IFMT;
				estack->v.sp = r.i&S_IFDIR? "d":
					r.i&S_IFCHR? "c":
					r.i&S_IFIFO? "p":
					r.i&S_IFBLK? "b": "f";
				estack->t = STRING;
				break;
			case LAST:
				*estack = Last;
				break;
			case SUBSTR:
				estack->v.sp = substr(to_str(estack),
					to_int(estack+1),
					to_int(estack+2));
				estack->t = STRING;
				break;
			case MATCH:
				estack->v.d = ematch(to_str(estack), to_str(estack+1));
				estack->t = DOUBLE;
				break;
			case MSTRING:
				r.i = to_int(estack);
				if(r.i<1 || r.i>10)
					goto argerr;
				estack->v.sp = r.i<=nbra? Mstring[r.i - 1]: "\0";
				estack->t = STRING;
				break;
			case INDEX:
				estack->v.d = sindex(to_str(estack),
					to_str(estack+1));
				estack->t = DOUBLE;
				break;
			case TRANSLATE:
				estack->v.sp = trans(to_str(estack),
					to_str(estack+1), to_str(estack+2));
				estack->t = STRING;
				break;
			case SIZE:
				estack->v.d = (double)strlen(to_str(estack));
				estack->t = DOUBLE;
				break;
			case FORMAT:
				sprintf((tstr=salloc(64,0)), to_str(estack),
					*(estack+1));
				estack->v.sp = tstr;
				estack->t = STRING;
				break;
			case GRAPH:
				graph(args, estack);
				estack->v.sp = "\0";
				estack->t = STRING;
				break;
			case TABLE:
				r.np = lookup(to_str(estack));
				x = mkdouble(estack+1);
				if(r.np->t)
					error("Table: allocated name");
				r.np->v.htabl = (int *)htable(x);
				r.np->t = TABLE;
				break;
			case ITEM:
				if(estack->t != TABLE)
nottab:
					error("arg not table");
				r.htbl = (struct htab *)estack->v.htabl;
				x = mkdouble(estack+1);
				if(x >= r.htbl->ht_size) {
					if(J != &Jmp[0]) {
						sv_estack = estack;
						longjmp(J->save, 1);
					} else {
						estack->v.sp = "\0";
						estack->t = STRING;
					}
				} else {
					*estack = r.htbl->ht_item[x].ht_v;
					Key = r.htbl->ht_item[x].ht_key;
				}
				break;
			case ISKEY:
				if(estack->t != TABLE)
					goto nottab;
				++Iskey;
				s1 = to_str(estack+1);
				r.htbl = (struct htab *)estack->v.htabl;
				x = (int)htitem(r.htbl, s1);
				Iskey = 0;
				estack->t = DOUBLE;
				estack->v.d = x? 1: 0;
				break;
			case KEY:
				estack->v.sp = Key;
				estack->t = STRING;
				break;
			}
			break;
		case TRACE:
			Trace = to_int(estack);
			break;
		case DUMP:
			s1 = (char *)*instr++;
			if(LastS)
				printf("Statement: %d\n", LastS);
			if(Ftrace > Fstack) {
				printf("Function stack:\n");
				for(; Ftrace > Fstack; --Ftrace)
					prtrace(Ftrace);
			}
			for(ct = 0; ct < LastN; ++ct) {
				r.np = nextname(ct);
				if(s1)
					if(!EQS(s1, r.np->u.sname))
						continue;
				if(r.np->u.ar.subscr < 0)
					continue;
				if(r.np->t == ARRAY) {
					sprintf(Line, "%.6s",  r.np->u.sname);
					prtsubs(Line, r.np->v.ar_hdr.head);
					continue;
				}
				if(r.np->t == TABLE) {
					s1 = r.np->u.sname;
					r.htbl = (struct htab *)r.np->v.htabl;
					for(x=0; x<r.htbl->ht_size; ++x)
						if(*r.htbl->ht_item[x].ht_key)
						 printf("%s[%s]=%s\n", s1, r.htbl->ht_item[x].ht_key,
						   to_str(&(r.htbl->ht_item[x].ht_v)));
					continue;
				}
				if(r.np->set==ALLOC) {
					printf("%.6s=%s\n",
					 r.np->u.sname, to_str(r.np));
				}
			}
			break;
		default:
			error("Compiler");
		}
		Last_op = opr;
	}
}
true(estack)
register struct estack *estack;
{
	if(estack->t == STRING)
		if(estack->v.sp[0])
			return 1;
		else
			return 0;
	else if(estack->v.d)
			return 1;
		else
			return 0;
}
struct names *getar(np, val)
struct names *np;
register val;
{
	register struct names *np1, *np2;

	val = -val;  /* subscripts are stored as complements */
	if(np1 = np->v.ar_hdr.head) {
/* shortcut for equal or +1 subscripts (didn't help)
		if((np2 = np->v.ar_hdr.lastref)->ar.subscr == val)
			return np2;
		if((np2 = np2->u.ar.next)->subscr == val)
			return np->v.ar_hdr.lastref = np2;
*/
loop:
		if(np1->u.ar.subscr == val) {
			np->v.ar_hdr.lastref = np1;
			return np1;
		}
		if(np2 = np1->u.ar.next ) {
			np1 = np2;
			goto loop;
		}
		np2 = nextname(LastN++);
		np1->u.ar.next = np2;
set:
		np2->u.ar.next = (struct names *)0;
		np2->u.ar.subscr = val;
		np2->v.d = 0.0;
		np2->set = 0;
		np2->t = DOUBLE;
		np->v.ar_hdr.lastref = np2;
		return np2;
	}
	np2 = np->v.ar_hdr.head = nextname(LastN++);
	goto set;
}
error(s)
register char *s;
{
	if(Expr) {
		Expr = 0;
		longjmp(J->save, 1);
	}
	fprintf(stderr, "%s - execution error", s);
	if(Lnum)
		fprintf(stderr, " in source line %d", Lnum);
	fprintf(stderr, "\n");
	intr();
}
prtsubs(ps, np)
register char *ps;
register struct names *np;
{
	register char *lc;

	lc = ps + strlen(ps);
	for(;;) {
		*lc = '\0';
		sprintf(ps, "%s[%d]", ps, -(np->u.ar.subscr));
		if(np->set == ALLOC)
			printf("%s=%s\n", ps, to_str(np));
		else if(np->t == ARRAY)
			prtsubs(ps, np->v.ar_hdr.head);
		if(!np->u.ar.next)
			return;
		np = np->u.ar.next;
	}
}
double mkdouble(estack)
register struct estack *estack;
{
	if(estack->t == DOUBLE)
		return estack->v.d;
	if(estack->t==INPUT || estack->t==OUTPUT)
		goto conv;
	else if(estack->t == STRING) {
		register char *s = estack->v.sp, *strend;

		strend = strlen(s) + s;
		estack->v.d = Ibase==10? atof(s): cvbase(Ibase, s);
		if(estack->set == ALLOC)
			free(s);
		if(Atof != strend)
conv:			error("Conversion");
		estack->t = DOUBLE;
		return estack->v.d;
	}
}
to_int(estack)
register struct estack *estack;
{
	return (int)(estack->t==DOUBLE? estack->v.d: mkdouble(estack));
}
char *to_str(estack)
register struct estack *estack;
{
	register char *s, *st;
	static char rv[3][42];
	static which = 0;

	st = rv[which = which==2? 0: ++which];
	if(estack->t == DOUBLE) {
		if(Obase == 10)
			sprintf(st, "%.10f", estack->v.d);
		else  {
			sprintf(st, (Obase==8? "%lo": "%lx"), (long)estack->v.d);
			return st;
		}
		s = st + strlen(st);
		while(*--s != '.')
			if(*s == '0') {
				*s = '\0';
				continue;
			} else
				return st;
		*s = '\0';
		return st;
	} else if(estack->t==INPUT || estack->t==OUTPUT)
		return "I/O";
	else
		return estack->v.sp;
}
char *salloc(ct, t)
register ct, t;
{
	register char *rv;

	if(rv = malloc((unsigned)(ct+1))) {
		if(t != ALLOC) { /* temp */
			if(!(++Ti < STRTEMPS))
				Ti = 0;
			if(Temps[Ti]) {
				free(Temps[Ti]);
			}
			Temps[Ti] = rv;
		}
		return rv;
	}
	error("Out of string space");
}
/*VARARGS2*/
io(args, ns, fd, flname, func)
char *ns;
double fd;
register char *flname, *func;
{
	FILE *fl;
	int i;
	register struct names *np;

	np = lookup(ns);
	if(np->t==INPUT || np->t==OUTPUT) {
		fl = np->v.io.file;
		if(fl!=stdin && fl!=stdout && fl!=stderr)
			fclose(fl);
		if(np->v.io.f_or_p == '!')
			wait(&i);
		np->v.d = np->set = 0;
		np->t = DOUBLE;
	}
	if(args == 1)
		return;
	switch(np->v.io.ffunc = *func) {
	case 'r':
		if(*flname == '!')
			np->v.io.file = popen(&flname[1], func);
		else if (fd == 0)
			np->v.io.file = stdin, *flname = 0;
		else
			np->v.io.file = fopen(flname, func);
		if(np->v.io.file == NULL)
accerr:			error("Cannot access file");
		np->t = INPUT;
		break;
	case 'W': /* no CR */
		*func = 'w';
	case 'w':
	case 'a':
		if(fd == 1)
			np->v.io.file = stdout, *flname = 0;
		else if(fd == 2)
			np->v.io.file = stderr, *flname = 0;
		else if(*flname == '!')
			np->v.io.file = popen(&flname[1], func);
		else
			np->v.io.file = fopen(flname, func);
		if(np->v.io.file == NULL)
			goto accerr;
		np->t = OUTPUT;
		break;
	default:
		error("Last arg to open() must be r, w, or a");
	}
	np->v.io.f_or_p = *flname;
	np->set = ALLOC;
}
clear()
{
	register i, j;
	register struct names *np;

	Fstackp = Fstack;
	for(i = 0; i < LABELS; ++i) {
		Label[i].l_instr = 0;
		for(j = 0; j < 6; ++j)
			Label[i].l_name[j] = '\0';
	}
	for(i = 0; i < LastN; ++i) {
		np = nextname(i);
		if(np->t==INPUT || np->t==OUTPUT) {
			io(1, np->u.sname);
		} else if(np->t == STRING)
			free(np->v.sp);
		np->v.d = 0;
		np->t = DOUBLE;
		np->u.sname[0] = '\0';
	}
	Instr[0] = '\0';
	Ip = Instr;
	Trace = Namegrps = LastN = Lnum = 0;
}
local(tname)
register char *tname;
{
	register i;

	for(i = 0; i < Lnames; ++i) {
		if(EQL(tname, Locname[i]))
			return i;
	}
	return -1;
}
prtrace(fs)
register struct fstack *fs;
{
	register struct estack *estack;
	register i;

	printf("%d: ", (int)(fs - Fstack));
	printf("%.6s(", Label[fs->fname].l_name);
	estack = fs->estk;
	for(i = 0; i < fs->callargs; ++i) {
		if(i)
			fputs(", ", stdout);
		printf("%.20s", to_str(estack+i));
	}
	fputs(") ", stdout);
	for(i = fs->dclargs; i < fs->tot_var; ++i) {
		if(i > fs->dclargs)
			fputs(", ", stdout);
		printf("%.20s", to_str(estack+i));
	}
	fputs("\n", stdout);
}
double intpow(a, b)
register double a;
register int b;
{
	register double t = a;

	while(--b > 0)
		t *= a;
	return t;
}
clearstk(top, bottom)
register struct estack *top, *bottom;
{
	while(bottom <= top) {
		if(bottom->set == ALLOC) {
			if(bottom->t == STRING)
				free(bottom->v.sp);
			bottom->set = 0;
		}
		++bottom;
	}
}

/*
 * Graphic function table - argcount is number of
 * args in the user program.  Arg count of 2 
 * (label, linemod) have only a string argument.
 */

struct gr
{
	char	nargs;
	char	code;
} gr[] = {
	2,	0,	/* Open plotter */
	1,	'e',	/* Erase */
	2,	't',	/* Label */
	5,	'l',	/* Line */
	4,	'c',	/* Circle */
	7,	'a',	/* Arc */
	6,	0,	/* Dot - not implemented */
	3,	'm',	/* Move */
	3,	'n',	/* Cont */
	3,	'p',	/* Point */
	2,	'f',	/* Linemod */
	5,	's',	/* Space */
	5,	0,	/* Scale and shift */
};

#define NGRAPH	14

double	xscale = 1.0;
double	yscale = 1.0;
double	xshift = 0.0;
double	yshift = 0.0;

char *Plot = "tplot -TXXXXXXXXXXX";

graph(args, estack)
register struct estack *estack;
{
	register fcn;
	register char *stp;

	fcn = to_int(estack);
	if(fcn >= NGRAPH || fcn < 0)
		error("Illegal graph function");
	if(args != gr[fcn].nargs)
		error("graph: arg count");
	if(fcn && Graphout==0)
		error("Graphic output never specified");
	estack++;

	Gio++;
	if(fcn == 0) {		/* pipe output through plot(I) */
		Plot[7] = '\0';
		if(strlen(stp = to_str(estack)) >  10)
			error("graph: Bad dest.");
		strcat(Plot, stp);
		grio();
		return;
	}
	if(gr[fcn].code)
		putgr(fcn, estack);
	else
		switch(fcn) {
		case 6:		/* Dot */
			error("Dot not implemented");
			return;


		case 12:	/* Scale*/
			xscale = mkdouble(estack++);
			yscale = mkdouble(estack++);
			xshift = mkdouble(estack++);
			yshift = mkdouble(estack);
			return;

		}
}

putgr(n, estack)
register struct estack *estack;
{
	register narg, i;
	double loc;

	narg = gr[n].nargs;
	putc(gr[n].code, Graphout);
	if(narg == 2) {
		fputs(to_str(estack), Graphout);
		putc('\n', Graphout);
		return;
	}
	for(i = 0, narg -= 2; i <= narg; i++) {
		loc = mkdouble(estack++);
		if(i & 01)
			putwd((int)(loc * xscale + xshift));
		else if(i == narg)
			putwd((int)(loc * xscale));
		else
			putwd((int)(loc * yscale + yshift));
	}
}

grio()
{
	if(!Gio)
		return;
	if(Graphout)
		pclose(Graphout);
	Graphout = popen(Plot, "w");
	if(Graphout == NULL)
		error("No plot!");
	Gio = 0;
}

putwd(a)
{
	union {
		struct { char lo, hi; } chs;
		int wd;
	} w;

	w.wd = a;
	putc(w.chs.lo, Graphout);
	putc(w.chs.hi, Graphout);
}
