/* Copyright (c) 1993 Stephen F. White */
#include <stdio.h>
#ifdef SYSV
#include <string.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include <sys/time.h>
#include "config.h"
#include "cool.h"
#include "proto.h"
#include "sys_proto.h"
#include "netio.h"
#include "execute.h"
#include "x.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();
if (i.type != NUM) {
raise(E_TYPE);
} else switch (base.type) {
case STR:
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.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;
default:
raise(E_TYPE);
}
var_free(base); var_free(i);
}
void
op_subset(void)
{
Var base, left, right, ret;
int len;
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_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();
}
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();
} 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);
}