/* Copyright (c) 1993 Stephen F. White */
#include "cool.h"
#include "proto.h"
#include "netio.h"
#include "execute.h"
#include "y.tab.h"
void op_add (void)
{
Var right, left, ret;
right = pop ();
left = pop ();
if (left.type != right.type && left.type != LIST) {
var_free (left);
var_free (right);
raise (E_TYPE);
} else if (left.type == NUM) {
left.v.num = left.v.num + right.v.num;
push (left);
} else if (left.type == STR) {
ret.type = STR;
if (left.v.str->ref == 1) {
ret.v.str = string_cat (left.v.str, right.v.str->str);
} else {
ret.v.str = string_new (left.v.str->len + right.v.str->len + 1);
strcpy (ret.v.str->str, left.v.str->str);
strcpy (ret.v.str->str + left.v.str->len, right.v.str->str);
ret.v.str->len = left.v.str->len + right.v.str->len;
var_free (left);
}
var_free (right);
push (ret);
} else if (left.type == LIST) {
ret.type = LIST;
ret.v.list = list_setadd (left.v.list, right);
push (ret);
} else {
var_free (left);
var_free (right);
raise (E_TYPE);
}
}
void op_sub (void)
{
Var right, left;
right = pop ();
left = pop ();
if (left.type != right.type && left.type != LIST) {
var_free (left);
var_free (right);
raise (E_TYPE);
} else if (left.type == NUM) {
left.v.num -= right.v.num;
push (left);
} else if (left.type == LIST) {
left.v.list = list_setremove (left.v.list, right);
var_free (right);
push (left);
} else {
raise (E_ARGTYPE);
}
}
void op_mul (void)
{
Var right, left;
right = pop ();
left = pop ();
if (left.type != NUM || right.type != NUM) {
var_free (left);
var_free (right);
raise (E_TYPE);
} else {
left.v.num *= right.v.num;
push (left);
}
}
void op_div (void)
{
Var right, left;
right = pop ();
left = pop ();
if (left.type != NUM || right.type != NUM) {
var_free (left);
var_free (right);
raise (E_TYPE);
} else if (!right.v.num) {
raise (E_DIV);
} else {
left.v.num /= right.v.num;
push (left);
}
}
void op_mod (void)
{
Var right, left;
right = pop ();
left = pop ();
if (left.type != NUM || right.type != NUM) {
var_free (left);
var_free (right);
raise (E_TYPE);
} else if (!right.v.num) {
raise (E_DIV);
} else {
left.v.num %= right.v.num;
push (left);
}
}
void op_negate (void)
{
Var arg;
arg = pop ();
if (arg.type != NUM) {
var_free (arg);
raise (E_TYPE);
} else {
arg.v.num = -arg.v.num;
push (arg);
}
}
void op_and (void)
{
Var left;
Var ret;
left = pop ();
if (ISTRUE (left)) { /* LHS true, evaluate RHS */
frame.pc++; /* skip over breakout goto */
} else { /* LHS false, abort */
ret.type = NUM;
ret.v.num = 0;
push (ret);
frame.pc = frame.m->code[frame.pc]; /* skip to breakout */
}
var_free (left);
}
void op_or (void)
{
Var left;
Var ret;
left = pop ();
if (!ISTRUE (left)) { /* LHS false, evaluate RHS */
frame.pc++; /* skip over breakout goto */
} else { /* LHS true, skip to end */
ret.type = NUM;
ret.v.num = 1;
push (ret);
frame.pc = frame.m->code[frame.pc]; /* skip to breakout */
}
var_free (left);
}
void op_not (void)
{
Var ret, arg;
arg = pop ();
ret.type = NUM;
ret.v.num = !ISTRUE (arg);
var_free (arg);
push (ret);
}
#define REL(NAME, OP) \
void NAME(void) \
{ \
Var ret, right, left; \
\
right = pop(); left = pop(); \
if (left.type != right.type) { \
raise(E_TYPE); \
} else if (left.type != STR && left.type != NUM) { \
raise(E_TYPE); \
} else { \
ret.type = NUM; ret.v.num = var_compare(left, right) OP 0; \
push(ret); \
} \
var_free(left); var_free(right); \
}
REL (op_lt, <)
REL (op_gt, >)
REL (op_le, <=)
REL (op_ge, >=)
void op_eq (void)
{
Var ret, right, left;
right = pop ();
left = pop ();
ret.type = NUM;
ret.v.num = var_eq (left, right);
push (ret);
var_free (left);
var_free (right);
}
void op_ne (void)
{
Var ret, right, left;
right = pop ();
left = pop ();
ret.type = NUM;
ret.v.num = !var_eq (left, right);
push (ret);
var_free (left);
var_free (right);
}
void op_index (void)
{
Var ret, i, base;
i = pop ();
base = pop ();
switch (base.type) {
case STR:
if (i.type != NUM) {
raise (E_TYPE);
} else if (i.v.num < 1 || i.v.num > base.v.str->len) {
raise (E_RANGE);
} else {
ret.type = STR;
ret.v.str = string_new (2);
ret.v.str->str[0] = base.v.str->str[i.v.num - 1];
ret.v.str->str[1] = '\0';
ret.v.str->len = 1;
push (ret);
}
break;
case LIST:
if (i.type != NUM) {
raise (E_TYPE);
} else if (i.v.num < 1 || i.v.num > base.v.list->len) {
raise (E_RANGE);
} else {
ret = var_dup (base.v.list->el[i.v.num - 1]);
push (ret);
}
break;
case MAP:
if (map_find (base.v.map, i, &ret)) {
raise (E_MAPNF);
} else {
push (var_dup (ret));
}
break;
default:
raise (E_TYPE);
}
var_free (base);
var_free (i);
}
void op_subset (void)
{
Var base, left, right, ret;
int len = 0;
right = pop ();
left = pop ();
base = pop ();
ret.type = base.type;
if (base.type == STR) {
len = base.v.str->len;
} else if (base.type == LIST) {
len = base.v.list->len;
}
if (left.type != NUM || right.type != NUM) {
var_free (left);
var_free (right);
var_free (base);
raise (E_TYPE);
return;
}
if (right.v.num < 1) {
right.v.num = len;
}
if (left.v.num > right.v.num
|| left.v.num < 1 || left.v.num > len
|| right.v.num < 1 || right.v.num > len) {
raise (E_RANGE);
} else if (base.type == STR) {
len = right.v.num - left.v.num + 1; /* substring length */
ret.type = STR;
ret.v.str = string_new (len);
strncpy (ret.v.str->str, base.v.str->str + left.v.num - 1, len);
ret.v.str->str[len] = '\0';
ret.v.str->len = len;
push (ret);
} else if (base.type == LIST) {
ret.v.list = list_subset (base.v.list, left.v.num, right.v.num);
push (ret);
} else {
raise (E_TYPE);
}
var_free (base);
var_free (left);
var_free (right);
}
void op_lsubset (void)
{
Var left, right;
right = pop ();
left.type = NUM;
left.v.num = 1;
push (left);
push (right);
op_subset ();
}
void op_rsubset (void)
{
Var left, right;
left = pop ();
right.type = NUM;
right.v.num = -1;
push (left);
push (right);
op_subset ();
}
void op_return (void)
{
ex_retval = pop ();
ex_state = STOPPED;
}
void op_pop (void)
{
Var v;
v = pop ();
var_free (v);
}
void op_numpush (void)
{
Var n;
n.type = NUM;
n.v.num = frame.m->code[frame.pc++];
push (n);
}
void op_strpush (void)
{
Var s;
int symno;
symno = frame.m->code[frame.pc++];
s.type = STR;
s.v.str = string_dup (sym_get (frame.on, symno));
push (s);
}
void op_objpush (void)
{
Var o;
o.type = OBJ;
o.v.obj.id = frame.m->code[frame.pc++];
o.v.obj.server = frame.m->code[frame.pc++];
push (o);
}
void op_listpush (void)
{
Var list;
list = pop_args (frame.m->code[frame.pc++]);
push (list);
}
void op_mappush (void)
{
int num = frame.m->code[frame.pc++], i;
Var v;
Var from, to;
v.type = MAP;
v.v.map = map_new (num);
for (i = 0; i < num; i++) {
to = pop ();
from = pop ();
v.v.map = map_add (v.v.map, from, to);
}
push (v);
}
void op_errpush (void)
{
Var e;
e.type = ERR;
e.v.err = frame.m->code[frame.pc++];
push (e);
}
void op_message (void)
{
Var args;
Var dest;
String *msg;
args = pop_args (frame.m->code[frame.pc++]);
dest = pop ();
if (dest.type != OBJ) {
var_free (args);
var_free (dest);
frame.pc++;
raise (E_INVIND);
} else {
msg = string_dup (sym_get (frame.on, frame.m->code[frame.pc]));
frame.pc++;
send_message_and_block (frame.this, dest.v.obj, msg,
args.v.list, dest.v.obj);
}
}
void op_message_expr (void)
{
Var args;
Var msg, dest;
args = pop_args (frame.m->code[frame.pc++]);
msg = pop ();
dest = pop ();
if (dest.type != OBJ) {
var_free (args);
var_free (dest);
var_free (msg);
raise (E_INVIND);
} else if (msg.type != STR) {
var_free (args);
var_free (msg);
raise (E_TYPE);
} else {
send_message_and_block (frame.this, dest.v.obj, msg.v.str,
args.v.list, dest.v.obj);
}
}
void op_if (void)
{
Var cond;
cond = pop ();
if (ISTRUE (cond)) {
pushn (frame.m->code[frame.pc + 1]); /* push end address */
frame.pc += 2; /* skip to code */
} else if (frame.m->code[frame.pc]) { /* if there's an elseif */
pushn (frame.m->code[frame.pc + 1]); /* push end address */
frame.pc = frame.m->code[frame.pc]; /* go to elseif code */
} else {
frame.pc = frame.m->code[frame.pc + 1]; /* go to end address */
}
var_free (cond);
}
void op_null (void)
{ /* null function */
}
void op_elseif (void)
{
Var cond;
cond = pop ();
if (ISTRUE (cond)) {
frame.pc++;
} else {
frame.pc = frame.m->code[frame.pc];
}
var_free (cond);
}
void op_for (void)
{
Var idx, list;
idx = pop ();
list = pop ();
if (list.type != LIST) {
var_free (list);
raise (E_FOR);
} else if (idx.v.num >= list.v.list->len) { /* loop is complete */
var_free (list);
frame.pc = frame.m->code[frame.pc + 1]; /* skip to end */
} else {
var_assign_local (frame.stack, frame.m->code[frame.pc],
var_dup (list.v.list->el[idx.v.num]));
idx.v.num++;
push (list); /* push list */
push (idx); /* push new index */
pushpc (frame.pc - 1); /* push address of FOR statement */
frame.pc += 2; /* go to first instruction in loop */
}
}
void op_forrng (void)
{
Var v, upper;
upper = pop ();
v = pop ();
if (v.type != NUM || upper.type != NUM) {
var_free (upper);
var_free (v);
raise (E_TYPE);
} else if (v.v.num > upper.v.num) {
frame.pc = frame.m->code[frame.pc + 1];
} else {
var_assign_local (frame.stack, frame.m->code[frame.pc], v);
v.v.num++;
push (v);
push (upper);
pushpc (frame.pc - 1);
frame.pc += 2;
}
}
void op_pushpc (void)
{
pushpc (frame.pc - 1);
}
void op_while (void)
{
Var cond;
cond = pop ();
if (!ISTRUE (cond)) {
(void) pop (); /* take pc off stack */
frame.pc = frame.m->code[frame.pc];
} else {
frame.pc++; /* go to first instruction in loop */
}
var_free (cond);
}
void op_do (void)
{
pushpc (frame.m->code[frame.pc++]);
}
void op_dowhile (void)
{
Var cond;
cond = pop ();
if (ISTRUE (cond)) { /* keep looping */
frame.pc = frame.m->code[frame.pc];
} else { /* loop is finished */
(void) pop (); /* remove sentinel */
frame.pc += 2; /* skip to end */
}
var_free (cond);
}
void op_break (void)
{
Var newpc;
int break_lvl = frame.m->code[frame.pc++];
while (break_lvl--) {
do {
newpc = pop ();
var_free (newpc);
} while (newpc.type != PC);
}
if (frame.m->code[newpc.v.num] == FOR) {
(void) pop (); /* pop index */
newpc = pop ();
var_free (newpc); /* pop list */
frame.pc = frame.m->code[newpc.v.num + 2];
} else if (frame.m->code[newpc.v.num] == FORRNG) {
(void) pop (); /* pop upper range */
(void) pop (); /* pop current value */
frame.pc = frame.m->code[newpc.v.num + 2];
} else if (frame.m->code[newpc.v.num] == DOWHILE) {
frame.pc = newpc.v.num + 3;
} else if (frame.m->code[newpc.v.num] == PUSHPC) { /* WHILE, actually */
while (frame.m->code[newpc.v.num] != WHILE) {
newpc.v.num++;
}
frame.pc = frame.m->code[newpc.v.num + 1];
}
}
void op_continue (void)
{
Var newpc;
int break_lvl = frame.m->code[frame.pc++];
while (break_lvl--) {
do {
newpc = pop ();
var_free (newpc);
} while (newpc.type != PC);
}
if (frame.m->code[newpc.v.num] == DOWHILE) {
pushpc (newpc.v.num);
frame.pc = frame.m->code[newpc.v.num + 2];
} else {
frame.pc = newpc.v.num; /* all the others push their own PC */
}
}
void op_asgnlvar (void)
{
Var v;
v = pop ();
var_assign_local (frame.stack, frame.m->code[frame.pc++], v);
}
void op_asgngvar (void)
{
Var v;
int varno = frame.m->code[frame.pc++];
v = pop ();
raise (var_assign_global (this, sym_get (frame.on, varno), v));
(void) pop ();
}
static int do_asgnindex (Var var, Var idx, Var expr, Var * ret);
void op_asgnlvarindex (void)
{
Var var, idx, expr, ret;
int varno = frame.m->code[frame.pc++];
expr = pop ();
idx = pop ();
var = frame.stack[varno];
if (!do_asgnindex (var, idx, expr, &ret)) {
frame.stack[varno] = ret;
}
}
void op_asgngvarindex (void)
{
Var var, idx, expr, ret;
int varno = frame.m->code[frame.pc++];
Error r;
expr = pop ();
idx = pop ();
r = var_get_global (this, sym_get (frame.on, varno)->str, &var);
if (r != E_NONE) {
raise (r);
} else if (!do_asgnindex (var, idx, expr, &ret)) {
var = var_dup (var);
var_assign_global (this, sym_get (frame.on, varno), ret);
}
}
static int do_asgnindex (Var var, Var idx, Var expr, Var * ret)
{
ret->type = var.type;
if (var.type == LIST) {
if (idx.type != NUM) {
var_free (idx);
var_free (expr);
raise (E_TYPE);
return -1;
} else if (idx.v.num <= 0 || idx.v.num > var.v.list->len) {
var_free (expr);
raise (E_RANGE);
return -2;
} else {
ret->v.list = list_assign (var.v.list, expr, idx.v.num);
return 0;
}
} else if (var.type == MAP) {
ret->v.map = map_add (var.v.map, idx, expr);
return 0;
} else {
var_free (expr);
var_free (idx);
raise (E_TYPE);
return -3;
}
}
void op_getlvar (void)
{
Var v;
var_get_local (frame.stack, frame.m->code[frame.pc++], &v);
push (var_dup (v));
}
void op_getgvar (void)
{
Var ret;
Error r;
int varname;
varname = frame.m->code[frame.pc++];
r = var_get_global (this, sym_get (frame.on, varname)->str, &ret);
if (r != E_NONE) {
raise (r);
(void) pop (); /* what's this for? i forget */
} else {
push (var_dup (ret));
}
}
void op_getgvarexpr (void)
{
Var arg, ret;
Error r;
frame.pc++; /* skip nargs */
arg = pop ();
if (arg.type != STR) {
raise (E_ARGTYPE);
} else {
r = var_get_global (this, arg.v.str->str, &ret);
if (r != E_NONE) {
raise (r);
} else {
push (var_dup (ret));
}
}
var_free (arg);
}
void op_asgngvarexpr (void)
{
Var arg1, arg2;
Error r;
frame.pc++; /* skip nargs */
arg2 = pop ();
arg1 = pop ();
if (arg1.type != STR) {
raise (E_ARGTYPE);
} else if (!valid_ident (arg1.v.str->str)) {
raise (E_RANGE);
} else {
r = var_assign_global (this, arg1.v.str, arg2);
if (r != E_NONE) {
raise (r);
} else {
push (var_dup (arg2));
}
}
var_free (arg1);
}
void op_stop (void)
{
Var newpc;
newpc = pop ();
if (newpc.type != PC && newpc.type != NUM) {
writelog ();
fprintf (stderr, "EXECUTION ERROR: STOP encountered non-num PC\n");
raise (E_INTERNAL);
var_free (newpc);
} else {
if (newpc.v.num < 0) {
ex_state = STOPPED;
} else {
frame.pc = newpc.v.num;
}
}
}
void op_in (void)
{
Var left, right, r;
right = pop ();
left = pop ();
r.type = NUM;
if (right.type == LIST) {
r.v.num = list_ismember (left, right.v.list);
push (r);
} else if (right.type == STR) {
if (left.type != STR) {
raise (E_TYPE);
} else {
r.v.num = str_in (left.v.str->str, right.v.str->str);
push (r);
}
} else {
raise (E_TYPE);
}
var_free (left);
var_free (right);
}