661 lines
10 KiB
C
661 lines
10 KiB
C
/*
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
* in the LICENSE file.
|
|
*/
|
|
|
|
#include <u.h>
|
|
#include <lib9.h>
|
|
#include <bio.h>
|
|
#include "hoc.h"
|
|
#include "y.tab.h"
|
|
|
|
#define NSTACK 256
|
|
|
|
static Datum stack[NSTACK]; /* the stack */
|
|
static Datum *stackp; /* next free spot on stack */
|
|
|
|
#define NPROG 2000
|
|
Inst prog[NPROG]; /* the machine */
|
|
Inst *progp; /* next free spot for code generation */
|
|
Inst *pc; /* program counter during execution */
|
|
Inst *progbase = prog; /* start of current subprogram */
|
|
int returning; /* 1 if return stmt seen */
|
|
int indef; /* 1 if parsing a func or proc */
|
|
|
|
typedef struct Frame { /* proc/func call stack frame */
|
|
Symbol *sp; /* symbol table entry */
|
|
Inst *retpc; /* where to resume after return */
|
|
Datum *argn; /* n-th argument on stack */
|
|
int nargs; /* number of arguments */
|
|
} Frame;
|
|
#define NFRAME 100
|
|
Frame frame[NFRAME];
|
|
Frame *fp; /* frame pointer */
|
|
|
|
void
|
|
initcode(void)
|
|
{
|
|
progp = progbase;
|
|
stackp = stack;
|
|
fp = frame;
|
|
returning = 0;
|
|
indef = 0;
|
|
}
|
|
|
|
void
|
|
push(Datum d)
|
|
{
|
|
if (stackp >= &stack[NSTACK])
|
|
execerror("stack too deep", 0);
|
|
*stackp++ = d;
|
|
}
|
|
|
|
Datum
|
|
pop(void)
|
|
{
|
|
if (stackp == stack)
|
|
execerror("stack underflow", 0);
|
|
return *--stackp;
|
|
}
|
|
|
|
void
|
|
xpop(void) /* for when no value is wanted */
|
|
{
|
|
if (stackp == stack)
|
|
execerror("stack underflow", (char *)0);
|
|
--stackp;
|
|
}
|
|
|
|
void
|
|
constpush(void)
|
|
{
|
|
Datum d;
|
|
d.val = ((Symbol *)*pc++)->u.val;
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
varpush(void)
|
|
{
|
|
Datum d;
|
|
d.sym = (Symbol *)(*pc++);
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
whilecode(void)
|
|
{
|
|
Datum d;
|
|
Inst *savepc = pc;
|
|
|
|
execute(savepc+2); /* condition */
|
|
d = pop();
|
|
while (d.val) {
|
|
execute(*((Inst **)(savepc))); /* body */
|
|
if (returning)
|
|
break;
|
|
execute(savepc+2); /* condition */
|
|
d = pop();
|
|
}
|
|
if (!returning)
|
|
pc = *((Inst **)(savepc+1)); /* next stmt */
|
|
}
|
|
|
|
void
|
|
forcode(void)
|
|
{
|
|
Datum d;
|
|
Inst *savepc = pc;
|
|
|
|
execute(savepc+4); /* precharge */
|
|
pop();
|
|
execute(*((Inst **)(savepc))); /* condition */
|
|
d = pop();
|
|
while (d.val) {
|
|
execute(*((Inst **)(savepc+2))); /* body */
|
|
if (returning)
|
|
break;
|
|
execute(*((Inst **)(savepc+1))); /* post loop */
|
|
pop();
|
|
execute(*((Inst **)(savepc))); /* condition */
|
|
d = pop();
|
|
}
|
|
if (!returning)
|
|
pc = *((Inst **)(savepc+3)); /* next stmt */
|
|
}
|
|
|
|
void
|
|
ifcode(void)
|
|
{
|
|
Datum d;
|
|
Inst *savepc = pc; /* then part */
|
|
|
|
execute(savepc+3); /* condition */
|
|
d = pop();
|
|
if (d.val)
|
|
execute(*((Inst **)(savepc)));
|
|
else if (*((Inst **)(savepc+1))) /* else part? */
|
|
execute(*((Inst **)(savepc+1)));
|
|
if (!returning)
|
|
pc = *((Inst **)(savepc+2)); /* next stmt */
|
|
}
|
|
|
|
void
|
|
define(Symbol* sp, Formal *f) /* put func/proc in symbol table */
|
|
{
|
|
Fndefn *fd;
|
|
int n;
|
|
|
|
fd = emalloc(sizeof(Fndefn));
|
|
fd->code = progbase; /* start of code */
|
|
progbase = progp; /* next code starts here */
|
|
fd->formals = f;
|
|
for(n=0; f; f=f->next)
|
|
n++;
|
|
fd->nargs = n;
|
|
sp->u.defn = fd;
|
|
}
|
|
|
|
void
|
|
call(void) /* call a function */
|
|
{
|
|
Formal *f;
|
|
Datum *arg;
|
|
Saveval *s;
|
|
int i;
|
|
|
|
Symbol *sp = (Symbol *)pc[0]; /* symbol table entry */
|
|
/* for function */
|
|
if (fp >= &frame[NFRAME-1])
|
|
execerror(sp->name, "call nested too deeply");
|
|
fp++;
|
|
fp->sp = sp;
|
|
fp->nargs = (int)(uintptr_t)pc[1];
|
|
fp->retpc = pc + 2;
|
|
fp->argn = stackp - 1; /* last argument */
|
|
if(fp->nargs != sp->u.defn->nargs)
|
|
execerror(sp->name, "called with wrong number of arguments");
|
|
/* bind formals */
|
|
f = sp->u.defn->formals;
|
|
arg = stackp - fp->nargs;
|
|
while(f){
|
|
s = emalloc(sizeof(Saveval));
|
|
s->val = f->sym->u;
|
|
s->type = f->sym->type;
|
|
s->next = f->save;
|
|
f->save = s;
|
|
f->sym->u.val = arg->val;
|
|
f->sym->type = VAR;
|
|
f = f->next;
|
|
arg++;
|
|
}
|
|
for (i = 0; i < fp->nargs; i++)
|
|
pop(); /* pop arguments; no longer needed */
|
|
execute(sp->u.defn->code);
|
|
returning = 0;
|
|
}
|
|
|
|
void
|
|
restore(Symbol *sp) /* restore formals associated with symbol */
|
|
{
|
|
Formal *f;
|
|
Saveval *s;
|
|
|
|
f = sp->u.defn->formals;
|
|
while(f){
|
|
s = f->save;
|
|
if(s == 0) /* more actuals than formals */
|
|
break;
|
|
f->sym->u = s->val;
|
|
f->sym->type = s->type;
|
|
f->save = s->next;
|
|
free(s);
|
|
f = f->next;
|
|
}
|
|
}
|
|
|
|
void
|
|
restoreall(void) /* restore all variables in case of error */
|
|
{
|
|
while(fp>=frame && fp->sp){
|
|
restore(fp->sp);
|
|
--fp;
|
|
}
|
|
fp = frame;
|
|
}
|
|
|
|
static void
|
|
ret(void) /* common return from func or proc */
|
|
{
|
|
/* restore formals */
|
|
restore(fp->sp);
|
|
pc = (Inst *)fp->retpc;
|
|
--fp;
|
|
returning = 1;
|
|
}
|
|
|
|
void
|
|
funcret(void) /* return from a function */
|
|
{
|
|
Datum d;
|
|
if (fp->sp->type == PROCEDURE)
|
|
execerror(fp->sp->name, "(proc) returns value");
|
|
d = pop(); /* preserve function return value */
|
|
ret();
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
procret(void) /* return from a procedure */
|
|
{
|
|
if (fp->sp->type == FUNCTION)
|
|
execerror(fp->sp->name,
|
|
"(func) returns no value");
|
|
ret();
|
|
}
|
|
|
|
void
|
|
bltin(void)
|
|
{
|
|
|
|
Datum d;
|
|
d = pop();
|
|
d.val = (*(double (*)(double))*pc++)(d.val);
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
add(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val += d2.val;
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
sub(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val -= d2.val;
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
mul(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val *= d2.val;
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
div(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
if (d2.val == 0.0)
|
|
execerror("division by zero", (char *)0);
|
|
d1 = pop();
|
|
d1.val /= d2.val;
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
mod(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
if (d2.val == 0.0)
|
|
execerror("division by zero", (char *)0);
|
|
d1 = pop();
|
|
/* d1.val %= d2.val; */
|
|
d1.val = fmod(d1.val, d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
negate(void)
|
|
{
|
|
Datum d;
|
|
d = pop();
|
|
d.val = -d.val;
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
verify(Symbol* s)
|
|
{
|
|
if (s->type != VAR && s->type != UNDEF)
|
|
execerror("attempt to evaluate non-variable", s->name);
|
|
if (s->type == UNDEF)
|
|
execerror("undefined variable", s->name);
|
|
}
|
|
|
|
void
|
|
eval(void) /* evaluate variable on stack */
|
|
{
|
|
Datum d;
|
|
d = pop();
|
|
verify(d.sym);
|
|
d.val = d.sym->u.val;
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
preinc(void)
|
|
{
|
|
Datum d;
|
|
d.sym = (Symbol *)(*pc++);
|
|
verify(d.sym);
|
|
d.val = d.sym->u.val += 1.0;
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
predec(void)
|
|
{
|
|
Datum d;
|
|
d.sym = (Symbol *)(*pc++);
|
|
verify(d.sym);
|
|
d.val = d.sym->u.val -= 1.0;
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
postinc(void)
|
|
{
|
|
Datum d;
|
|
double v;
|
|
d.sym = (Symbol *)(*pc++);
|
|
verify(d.sym);
|
|
v = d.sym->u.val;
|
|
d.sym->u.val += 1.0;
|
|
d.val = v;
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
postdec(void)
|
|
{
|
|
Datum d;
|
|
double v;
|
|
d.sym = (Symbol *)(*pc++);
|
|
verify(d.sym);
|
|
v = d.sym->u.val;
|
|
d.sym->u.val -= 1.0;
|
|
d.val = v;
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
gt(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val > d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
lt(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val < d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
ge(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val >= d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
le(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val <= d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
eq(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val == d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
ne(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val != d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
and(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val != 0.0 && d2.val != 0.0);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
or(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = (double)(d1.val != 0.0 || d2.val != 0.0);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
not(void)
|
|
{
|
|
Datum d;
|
|
d = pop();
|
|
d.val = (double)(d.val == 0.0);
|
|
push(d);
|
|
}
|
|
|
|
void
|
|
power(void)
|
|
{
|
|
Datum d1, d2;
|
|
d2 = pop();
|
|
d1 = pop();
|
|
d1.val = Pow(d1.val, d2.val);
|
|
push(d1);
|
|
}
|
|
|
|
void
|
|
assign(void)
|
|
{
|
|
Datum d1, d2;
|
|
d1 = pop();
|
|
d2 = pop();
|
|
if (d1.sym->type != VAR && d1.sym->type != UNDEF)
|
|
execerror("assignment to non-variable",
|
|
d1.sym->name);
|
|
d1.sym->u.val = d2.val;
|
|
d1.sym->type = VAR;
|
|
push(d2);
|
|
}
|
|
|
|
void
|
|
addeq(void)
|
|
{
|
|
Datum d1, d2;
|
|
d1 = pop();
|
|
d2 = pop();
|
|
if (d1.sym->type != VAR && d1.sym->type != UNDEF)
|
|
execerror("assignment to non-variable",
|
|
d1.sym->name);
|
|
d2.val = d1.sym->u.val += d2.val;
|
|
d1.sym->type = VAR;
|
|
push(d2);
|
|
}
|
|
|
|
void
|
|
subeq(void)
|
|
{
|
|
Datum d1, d2;
|
|
d1 = pop();
|
|
d2 = pop();
|
|
if (d1.sym->type != VAR && d1.sym->type != UNDEF)
|
|
execerror("assignment to non-variable",
|
|
d1.sym->name);
|
|
d2.val = d1.sym->u.val -= d2.val;
|
|
d1.sym->type = VAR;
|
|
push(d2);
|
|
}
|
|
|
|
void
|
|
muleq(void)
|
|
{
|
|
Datum d1, d2;
|
|
d1 = pop();
|
|
d2 = pop();
|
|
if (d1.sym->type != VAR && d1.sym->type != UNDEF)
|
|
execerror("assignment to non-variable",
|
|
d1.sym->name);
|
|
d2.val = d1.sym->u.val *= d2.val;
|
|
d1.sym->type = VAR;
|
|
push(d2);
|
|
}
|
|
|
|
void
|
|
diveq(void)
|
|
{
|
|
Datum d1, d2;
|
|
d1 = pop();
|
|
d2 = pop();
|
|
if (d1.sym->type != VAR && d1.sym->type != UNDEF)
|
|
execerror("assignment to non-variable",
|
|
d1.sym->name);
|
|
d2.val = d1.sym->u.val /= d2.val;
|
|
d1.sym->type = VAR;
|
|
push(d2);
|
|
}
|
|
|
|
void
|
|
modeq(void)
|
|
{
|
|
Datum d1, d2;
|
|
int32_t x;
|
|
d1 = pop();
|
|
d2 = pop();
|
|
if (d1.sym->type != VAR && d1.sym->type != UNDEF)
|
|
execerror("assignment to non-variable",
|
|
d1.sym->name);
|
|
/* d2.val = d1.sym->u.val %= d2.val; */
|
|
x = d1.sym->u.val;
|
|
x %= (int32_t) d2.val;
|
|
d2.val = d1.sym->u.val = x;
|
|
d1.sym->type = VAR;
|
|
push(d2);
|
|
}
|
|
|
|
void
|
|
printtop(void) /* pop top value from stack, print it */
|
|
{
|
|
Datum d;
|
|
static Symbol *s; /* last value computed */
|
|
if (s == 0)
|
|
s = install("_", VAR, 0.0);
|
|
d = pop();
|
|
print("%.12g\n", d.val);
|
|
s->u.val = d.val;
|
|
}
|
|
|
|
void
|
|
prexpr(void) /* print numeric value */
|
|
{
|
|
Datum d;
|
|
d = pop();
|
|
print("%.12g ", d.val);
|
|
}
|
|
|
|
void
|
|
prstr(void) /* print string value */
|
|
{
|
|
print("%s", (char *) *pc++);
|
|
}
|
|
|
|
void
|
|
varread(void) /* read into variable */
|
|
{
|
|
Datum d;
|
|
extern Biobuf *bin;
|
|
Symbol *var = (Symbol *) *pc++;
|
|
int c;
|
|
|
|
Again:
|
|
do
|
|
c = Bgetc(bin);
|
|
while(c==' ' || c=='\t' || c=='\n');
|
|
if(c == Beof){
|
|
Iseof:
|
|
if(moreinput())
|
|
goto Again;
|
|
d.val = var->u.val = 0.0;
|
|
goto Return;
|
|
}
|
|
|
|
if(strchr("+-.0123456789", c) == 0)
|
|
execerror("non-number read into", var->name);
|
|
Bungetc(bin);
|
|
if(Bgetd(bin, &var->u.val) == Beof)
|
|
goto Iseof;
|
|
else
|
|
d.val = 1.0;
|
|
Return:
|
|
var->type = VAR;
|
|
push(d);
|
|
}
|
|
|
|
Inst*
|
|
code(Inst f) /* install one instruction or operand */
|
|
{
|
|
Inst *oprogp = progp;
|
|
if (progp >= &prog[NPROG])
|
|
execerror("program too big", (char *)0);
|
|
*progp++ = f;
|
|
return oprogp;
|
|
}
|
|
|
|
void
|
|
execute(Inst* p)
|
|
{
|
|
for (pc = p; *pc != STOP && !returning; )
|
|
(*((++pc)[-1]))();
|
|
}
|