/*
* NAME: compiler.c
* DESCRIPTION: generate LPC code from a MOO AST
*/
# define DEBUG 0
inherit "/std/string";
# if DEBUG
inherit "/std/vartext";
# endif
# include <objects.h>
# include <moo/tokens.h>
# include <moo/verbinfo.h>
# include <dgd/limits.h>
# define PROTO(elt) static void c_##elt(mixed *ast)
# define COMPILE(ast, elt) c_##elt(ast)
# define LINENO(num) emit("LINENO(" + (string) (num) + ");")
# define VF_DEFINED 0x80000000
# define VAR_DEFINED(ref) (ref & VF_DEFINED)
# define VAR_ID(ref) (ref & ~VF_DEFINED)
# define COMMA ", "
# define NL "\n"
private object optimizer, btable;
private int lineno, max_var, jumpno, sp_overhead;
private int fork_id, fork_cur, nest;
private mapping vars;
private string **forked;
PROTO(program);
PROTO(if);
PROTO(for);
PROTO(while);
PROTO(fork);
PROTO(return);
PROTO(expression);
/*
* NAME: create()
* DESCRIPTION: initialize object
*/
static
void create(void)
{ optimizer = load_object(OPTIMIZER); }
/*
* NAME: main()
* DESCRIPTION: compile MOO AST -> LPC directly to a file
*/
void main(mixed *ast, string file)
{
int i, sz;
lineno = max_var = jumpno = sp_overhead = fork_cur = fork_id = nest = 0;
forked = ({ });
if (! btable)
btable = load_object(BTABLE);
vars = ([
"args" : V_ARGS | VF_DEFINED,
"argstr" : V_ARGSTR | VF_DEFINED,
"buf" : V_BUF | VF_DEFINED,
"caller" : V_CALLER | VF_DEFINED,
"dobj" : V_DOBJ | VF_DEFINED,
"dobjstr" : V_DOBJSTR | VF_DEFINED,
"err" : V_ERR | VF_DEFINED,
"float" : V_FLOAT | VF_DEFINED,
"iobj" : V_IOBJ | VF_DEFINED,
"iobjstr" : V_IOBJSTR | VF_DEFINED,
"list" : V_LIST | VF_DEFINED,
"num" : V_NUM | VF_DEFINED,
"obj" : V_OBJ | VF_DEFINED,
"player" : V_PLAYER | VF_DEFINED,
"prepstr" : V_PREPSTR | VF_DEFINED,
"str" : V_STR | VF_DEFINED,
"table" : V_TABLE | VF_DEFINED,
"this" : V_THIS | VF_DEFINED,
"verb" : V_VERB | VF_DEFINED,
]);
if (query_editor(this_object()) == "insert")
editor(".");
ast = optimizer->main(ast);
editor("e! " + file + ".c");
editor("%d");
editor("0a");
editor("MOOVAL main(JS_PROTO) { int sp; MOOVAL *vars;" +
" JS_BEGIN; ref(); RESET();");
COMPILE(ast, program);
editor("return STORE(), del(), NUM(0); JS_END; }");
for (i = 0, sz = sizeof(forked); i < sz; ++i)
{
int j, sz;
editor("MOOVAL fork" + (string) (i + 1) +
"(JS_PROTO) { int sp; MOOVAL *vars;" +
" JS_BEGIN; RESET();");
for (j = 0, sz = sizeof(forked[i]); j < sz; ++j)
editor(forked[i][j]);
editor("del(); JS_END; }");
}
forked = 0;
editor(".");
if (max_var)
{
string varmap;
editor("1a");
editor("VARS += allocate(" + (string) max_var + "); PUT_VARS();");
for (varmap = ""; max_var--; varmap += "0");
editor("info[I_VARDEFS] = \"" + varmap + "\";");
editor(".");
}
vars = 0;
editor("0a");
editor("# include <moo/runtime.h>");
editor(".");
editor("w!");
}
/*
* NAME: var_ref()
* DESCRIPTION: return a (new) variable index
*/
private
int var_ref(string varname)
{
int ref;
varname = tolower(varname);
return (ref = vars[varname]) ? ref :
(vars[varname] = VAR_OFFSET + (max_var++));
}
/*
* NAME: var_assigned()
* DESCRIPTION: note that a variable has been assigned
*/
private
void var_assigned(string varname)
{
if (nest == 0)
vars[tolower(varname)] |= VF_DEFINED;
}
/*
* NAME: emit()
* DESCRIPTION: handle fork contexts
*/
private
void emit(string line)
{
string *lines;
lines = explode(line, "\n");
if (fork_cur)
forked[fork_cur - 1] += lines;
else
{
int i, sz;
for (i = 0, sz = sizeof(lines); i < sz; ++i)
editor(lines[i]);
}
}
private string bool_expr(mixed *ast);
private string simple_expr(mixed *ast);
/*
* NAME: bfun_args()
* DESCRIPTION: compile simple builtin function arguments
*/
private
string bfun_args(string func, mixed *ast)
{
string argstr;
int i, sz;
argstr = "";
for (i = 1, sz = sizeof(ast); i < sz; ++i)
{
if (TAG(ast[i]) == TOK_SPLICE)
return "*";
argstr += COMMA + simple_expr(ast[i]);
}
--sz;
if (sz < btable->minargs(func) ||
((i = btable->maxargs(func)) >= 0 && sz > i))
return 0;
return argstr;
}
/*
* NAME: simple_expr()
* DESCRIPTION: return code for a simple expression
*/
private
string simple_expr(mixed *ast)
{
int ref;
switch (TAG(ast))
{
case TOK_LIT_NUM:
if (ast[1] == INT_MIN)
return "NUM(" + (string) (INT_MIN + 1) + " - 1)";
else
return "NUM(" + ast[1] + ")";
case TOK_LIT_STR:
return "STR(\"" + ast[1] + "\")";
case TOK_LIT_OBJ:
if (ast[1] == INT_MIN)
return "OBJ(" + (string) (INT_MIN + 1) + " - 1)";
else
return "OBJ(" + ast[1] + ")";
case TOK_LIT_ERR:
return "ERR(" + ast[1] + ")";
case TOK_LIT_FLT:
return "FLT(" + ast[1] + ")";
case TOK_IDENTIFIER:
ref = var_ref(ast[1]);
if (VAR_DEFINED(ref))
return "VARS[" + (string) VAR_ID(ref) + "]";
else
return "VAR(" + (string) VAR_ID(ref) + ")";
case TOK_PLUS:
return "KFUN2(plus, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_MINUS:
return "KFUN2(minus, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_TIMES:
return "KFUN2(times, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_DIVIDE:
return "KFUN2(divide, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_PERCENT:
return "KFUN2(modulus, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_DOT:
return "KFUN2(getprop, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_EQUAL:
return "(EQUALP(" +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ") ? NUM(1) : NUM(0))";
case TOK_NEQUAL:
return "(EQUALP(" +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ") ? NUM(0) : NUM(1))";
case TOK_LESS:
return "KFUN2(less, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_LSEQUAL:
return "KFUN2(lsequal, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_GREATER:
return "KFUN2(greater, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_GREQUAL:
return "KFUN2(grequal, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_IN:
return "KFUN2(in, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
# if 0
case TOK_ASSOC:
return "KFUN2(bound, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
# endif
case TOK_LBRACKET:
if (TAG(ast[2]) == TOK_RANGE)
return "KFUN3(range, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2][1]) + COMMA + NL +
simple_expr(ast[2][2]) + ")";
else
return "KFUN2(index, " +
simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_BANG:
return "(" + bool_expr(ast[1]) + " ? NUM(0) : NUM(1))";
case TOK_U_MINUS:
return "KFUN1(negate, " + simple_expr(ast[1]) + ")";
case TOK_QUESTION:
return "(" + bool_expr(ast[1]) + "\n? " +
simple_expr(ast[2]) + "\n: " + simple_expr(ast[3]) + ")";
case TOK_AND:
return "(TRUTHOF(RET = " + simple_expr(ast[1]) + ")\n? " +
simple_expr(ast[2]) + " : RET)";
case TOK_OR:
return "(TRUTHOF(RET = " + simple_expr(ast[1]) + ")\n? " +
"RET : " + simple_expr(ast[2]) + ")";
case TOK_LPAREN:
{
string func, args;
func = ast[1];
switch (args = bfun_args(func, ast[2]))
{
case 0: /* incorrect # args */
return "(" + simple_expr(ast[2]) + ", RAISE(E_ARGS))";
case "*": /* spliced arguments */
return "(argcheck(RET = " + simple_expr(ast[2]) + COMMA +
(string) btable->minargs(func) + COMMA +
(string) btable->maxargs(func) + ")\n? " +
btable->invokation(func, 0) + COMMA +
"RET...) : RAISE(E_ARGS))";
default:
return btable->invokation(func, 0) + args + ")";
}
}
case TOK_LIST:
{
string text, splices;
int i, sz;
sz = sizeof(ast);
if (sz == 1)
return "LST(LNEW())";
for (text = splices = "", i = 1; i < sz; ++i)
{
if (TAG(ast[i]) == TOK_SPLICE)
{
text += (i > 1 ? COMMA + NL : "") + simple_expr(ast[i][1]);
splices += (string) (i - 1) + COMMA;
}
else
text += (i > 1 ? COMMA + NL : "") + simple_expr(ast[i]);
}
if (strlen(splices))
return "slist(info, ({ " + splices + "}), " + text + ")";
else
return "LST( ({ " + text + " }) )";
}
case TOK_TABLE:
{
string text, splices;
int i, sz, spcount;
if (sizeof(ast) == 1)
return "TBL(TNEW())";
spcount = 0;
for (text = splices = "", i = 1, sz = sizeof(ast); i < sz; ++i)
{
if (TAG(ast[i]) == TOK_SPLICE)
{
text += COMMA + NL + simple_expr(ast[i][1]) + COMMA + "0";
splices += "@", ++spcount;
}
else
{
text += COMMA + NL + simple_expr(ast[i][1]) + COMMA +
simple_expr(ast[i][2]);
splices += ".";
}
}
return "table(info, " +
(spcount ? "\"" + splices + "\"" : "0") + text + ")";
}
case TOK_AMBAGGR:
{
string text;
int i, sz;
for (text = "", i = 1, sz = sizeof(ast); i < sz; ++i)
text += COMMA + NL + simple_expr(ast[i][1]);
return "ambaggr(info" + text + ")";
}
case TOK_BUFFER:
{
string text, splices;
int i, sz, spcount;
sz = sizeof(ast);
if (sz == 1)
return "BUF(\"\")";
spcount = 0;
for (text = splices = "", i = 1; i < sz; ++i)
{
if (TAG(ast[i]) == TOK_SPLICE)
{
text += COMMA + NL + simple_expr(ast[i][1]);
splices += "@", ++spcount;
}
else
{
text += COMMA + NL + simple_expr(ast[i]);
splices += ".";
}
}
return "buffer(info, " +
(spcount ? "\"" + splices + "\"" : "0") + text + ")";
}
case TOK_ASSIGN:
{
mixed *frame;
int icount, range;
string rvalue, indices;
rvalue = simple_expr(ast[2]);
frame = ast[1];
icount = range = 0;
if (TAG(frame) == TOK_LBRACKET && TAG(frame[2]) == TOK_RANGE)
{
indices = simple_expr(frame[2][1]) + COMMA +
simple_expr(frame[2][2]);
range = 1, icount = 2;
frame = frame[1];
}
else
indices = "";
while (TAG(frame) == TOK_LBRACKET)
{
indices = simple_expr(frame[2]) +
(strlen(indices) ? COMMA : "") + indices;
++icount;
frame = frame[1];
}
indices = "LST( ({ " + indices + " }) )";
if (TAG(frame) == TOK_IDENTIFIER)
{
int var;
var = VAR_ID(var_ref(frame[1]));
var_assigned(frame[1]);
if (icount)
return "avari(info, " + (string) var + COMMA + (string) range +
COMMA + indices + COMMA + rvalue + ")";
else
return "avar(info, " + (string) var + COMMA + rvalue + ")";
}
else /* TOK_DOT */
{
string obj, prop;
obj = simple_expr(frame[1]);
prop = simple_expr(frame[2]);
if (icount)
return "apropi(info, " + obj + COMMA + prop + COMMA +
(string) range + COMMA + indices + COMMA + rvalue + ")";
else
return "aprop(info, " + obj + COMMA + prop + COMMA +
rvalue + ")";
}
}
default:
# if DEBUG
error("Unknown token: " + var2str(ast));
# else
error("Unknown token: " + TAG(ast));
# endif
}
}
/*
* NAME: bool_expr()
* DESCRIPTION: compile a boolean expression (evaluates to LPC true/false)
*/
private
string bool_expr(mixed *ast)
{
switch (TAG(ast))
{
case TOK_LIT_NUM:
return ast[1] ? "1" : "0";
case TOK_LIT_STR:
return strlen(ast[1]) ? "1" : "0";
case TOK_LIT_OBJ:
case TOK_LIT_ERR:
return "0";
case TOK_LIT_FLT:
return (float) ast[1] != 0.0 ? "1" : "0";
case TOK_EQUAL:
return "EQUALP(" + simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + ")";
case TOK_NEQUAL:
return "(! EQUALP(" + simple_expr(ast[1]) + COMMA + NL +
simple_expr(ast[2]) + "))";
case TOK_BANG:
return "(! " + bool_expr(ast[1]) + ")";
case TOK_QUESTION:
return "(" + bool_expr(ast[1]) + " ? " +
bool_expr(ast[2]) + " : " + bool_expr(ast[3]) + ")";
case TOK_AND:
return "(" + bool_expr(ast[1]) + " ? " + bool_expr(ast[2]) + " : 0)";
case TOK_OR:
return "(" + bool_expr(ast[1]) + " ? 1 : " + bool_expr(ast[2]) + ")";
default:
return "TRUTHOF(" + simple_expr(ast) + ")";
}
}
/*
* NAME: stack_kfun2()
* DESCRIPTION: compile a 2-arg kfun (at least 1 arg is non-simple)
*/
private
void stack_kfun2(string kfun, mixed *ast1, mixed *ast2)
{
string arg1, arg2;
int offset;
COMPILE(ast1, expression), offset = 1, arg1 = "stack[SP]";
if (SIMPLE(ast2))
arg2 = simple_expr(ast2);
else
COMPILE(ast2, expression),
arg2 = "stack[SP + " + (string) (offset++) + "]";
emit("stack[SP -= " + (string) (offset - 1) + "] = KFUN2(" + kfun +
COMMA + arg1 + COMMA + arg2 + ");");
}
/*
* NAME: stack_kfun3()
* DESCRIPTION: compile a 3-arg kfun (at least 1 arg is non-simple)
*/
private
void stack_kfun3(string kfun, mixed *ast1, mixed *ast2, mixed *ast3)
{
string arg1, arg2, arg3;
int offset;
COMPILE(ast1, expression), offset = 1, arg1 = "stack[SP]";
if (SIMPLE(ast2) && SIMPLE(ast3))
arg2 = simple_expr(ast2);
else
COMPILE(ast2, expression),
arg2 = "stack[SP + " + (string) (offset++) + "]";
if (SIMPLE(ast3))
arg3 = simple_expr(ast3);
else
COMPILE(ast3, expression),
arg3 = "stack[SP + " + (string) (offset++) + "]";
emit("stack[SP -= " + (string) (offset - 1) + "] = KFUN3(" + kfun +
COMMA + arg1 + COMMA + arg2 + COMMA + arg3 + ");");
}
PROTO(program)
{
int i, sz, tag;
if (DEBUG)
emit("/* program */");
/* program == ({ statement, statement, ... }) */
for (i = 0, sz = sizeof(ast); i < sz; ++i)
{
switch (tag = TAG(ast[i]))
{
case TOK_IF:
COMPILE(ast[i], if);
break;
case TOK_FOR:
COMPILE(ast[i], for);
break;
case TOK_WHILE:
COMPILE(ast[i], while);
break;
case TOK_FORK:
COMPILE(ast[i], fork);
break;
case TOK_RETURN:
COMPILE(ast[i], return);
break;
case TOK_NOP:
case TOK_LIT_NUM: /* ignore expressions with no side-effects */
case TOK_LIT_STR:
case TOK_LIT_ERR:
case TOK_LIT_OBJ:
case TOK_LIT_FLT:
++lineno;
break;
default: /* expression */
LINENO(++lineno);
if (SIMPLE(ast[i]))
emit(simple_expr(ast[i]) + ";");
else
{
COMPILE(ast[i], expression);
emit("--SP;");
}
}
}
}
PROTO(expression)
{
if (DEBUG)
emit("/* expression (" + TAG(ast) + ") */");
if (SIMPLE(ast))
{
emit("PUSH(" + simple_expr(ast) + ");");
return;
}
switch (TAG(ast))
{
case TOK_LIST:
{
string text, splices;
int i, sz;
sz = sizeof(ast);
if (sz == 2 && TAG(ast[1]) != TOK_SPLICE)
{
COMPILE(ast[1], expression);
emit("stack[SP] = LST( ({ stack[SP] }) );");
return;
}
for (splices = "", i = 1; i < sz; ++i)
{
if (TAG(ast[i]) == TOK_SPLICE)
{
COMPILE(ast[i][1], expression);
splices += (string) (i - 1) + COMMA;
}
else
COMPILE(ast[i], expression);
}
sz -= 2;
text = "stack[SP .. SP + " + (string) sz + "]";
if (strlen(splices))
text = "slist(info, ({ " + splices + "}), " + text + "...)";
else
text = "LST(" + text + ")";
emit("SP -= " + (string) sz + "; stack[SP] = " + text + ";");
return;
}
case TOK_TABLE:
{
string splices;
int i, sz, spcount;
spcount = 0;
for (splices = "", i = 1, sz = sizeof(ast); i < sz; ++i)
{
if (TAG(ast[i]) == TOK_SPLICE)
{
COMPILE(ast[i][1], expression);
emit("PUSH(NUM(0));");
splices += "@", ++spcount;
}
else
{
COMPILE(ast[i][1], expression);
COMPILE(ast[i][2], expression);
splices += ".";
}
}
sz = (sz - 1) * 2 - 1;
emit("SP -= " + (string) sz + "; stack[SP] = table(info, " +
(spcount ? "\"" + splices + "\"" : "0") +
", stack[SP .. SP + " + (string) sz + "]...);");
return;
}
case TOK_AMBAGGR:
{
int i, sz;
for (i = 1, sz = sizeof(ast); i < sz; ++i)
COMPILE(ast[i][1], expression);
sz -= 2;
emit("SP -= " + (string) sz +
"; stack[SP] = ambaggr(info, stack[SP .. SP + " + (string) sz +
"]...);");
return;
}
case TOK_BUFFER:
{
string splices;
int i, sz, spcount;
sz = sizeof(ast);
spcount = 0;
for (splices = "", i = 1; i < sz; ++i)
{
if (TAG(ast[i]) == TOK_SPLICE)
{
COMPILE(ast[i][1], expression);
splices += "@", ++spcount;
}
else
{
COMPILE(ast[i], expression);
splices += ".";
}
}
sz -= 2;
emit("SP -= " + (string) sz + "; stack[SP] = buffer(info, " +
(spcount ? "\"" + splices + "\"" : "0") +
", stack[SP .. SP + " + (string) sz + "]...);");
return;
}
case TOK_PLUS:
stack_kfun2("plus", ast[1], ast[2]);
return;
case TOK_MINUS:
stack_kfun2("minus", ast[1], ast[2]);
return;
case TOK_TIMES:
stack_kfun2("times", ast[1], ast[2]);
return;
case TOK_DIVIDE:
stack_kfun2("divide", ast[1], ast[2]);
return;
case TOK_PERCENT:
stack_kfun2("modulus", ast[1], ast[2]);
return;
case TOK_DOT:
stack_kfun2("getprop", ast[1], ast[2]);
return;
case TOK_ASSIGN:
{
mixed *frame, *list;
int icount, range;
list = ({ ast[2] }); /* rvalue */
frame = ast[1];
icount = range = 0;
if (TAG(frame) == TOK_LBRACKET && TAG(frame[2]) == TOK_RANGE)
{
list += ({ frame[2][2], frame[2][1] });
range = 1, icount = 2;
frame = frame[1];
}
while (TAG(frame) == TOK_LBRACKET)
{
list += ({ frame[2] });
++icount;
frame = frame[1];
}
if (TAG(frame) == TOK_IDENTIFIER)
{
int i, var;
for (i = sizeof(list); i--; )
COMPILE(list[i], expression);
var = VAR_ID(var_ref(frame[1]));
if (icount)
emit("SP -= " + (string) icount + "; " +
"stack[SP] = avari(info" + COMMA +
(string) var + COMMA + (string) range + COMMA +
"stack[SP .. SP + " + (string) (icount - 1) + "]" + COMMA +
"stack[SP + " + (string) icount + "]);");
else
emit("avar(info" + COMMA + (string) var + COMMA + "stack[SP]);");
var_assigned(frame[1]);
}
else /* TOK_DOT */
{
int i;
COMPILE(frame[1], expression); /* obj */
COMPILE(frame[2], expression); /* prop */
for (i = sizeof(list); i--; )
COMPILE(list[i], expression);
if (icount)
emit("SP -= " + (string) (icount + 2) + "; " +
"stack[SP] = apropi(info, stack[SP], stack[SP + 1], " +
(string) range + COMMA + "stack[SP + 2 .. SP + " +
(string) (icount - 1 + 2) + "]" + COMMA + "stack[SP + " +
(string) (icount + 2) + "]);");
else
emit("SP -= 2; " +
"stack[SP] = aprop(info" + COMMA +
"stack[SP]" + COMMA +
"stack[SP + 1]" + COMMA +
"stack[SP + 2]);");
}
return;
}
case TOK_COLON:
{
mixed *expr;
string obj, verb, args;
int offset;
if (SIMPLE(expr = ast[1]) && SIMPLE(ast[2][0]) && SIMPLE(ast[2][1]))
obj = simple_expr(expr);
else
COMPILE(expr, expression),
obj = "stack[SP + 1]", offset = 1;
if (SIMPLE(expr = ast[2][0]) && SIMPLE(ast[2][1]))
verb = simple_expr(expr);
else
COMPILE(expr, expression),
verb = "stack[SP + " + (string) (++offset) + "]";
if (SIMPLE(expr = ast[2][1]))
args = simple_expr(expr);
else
COMPILE(expr, expression),
args = "stack[SP + " + (string) (++offset) + "]";
emit("SP -= " + (string) offset + "; JS_PREP(" + (string) (++jumpno) +
"); RET = verbcall(JS_DATA(" + (string) jumpno + ")" + COMMA +
obj + COMMA + verb + COMMA + NL + args + "); JS_END; PUSH(RET);");
return;
}
case TOK_LPAREN:
{
string func, args;
func = ast[1];
if (SIMPLE(ast[2]))
{
switch (args = bfun_args(func, ast[2]))
{
case 0: /* incorrect # args */
emit(simple_expr(ast[2]) + ", PUSH(RAISE(E_ARGS));");
return;
case "*": /* spliced args */
emit("if (! argcheck(RET = " + simple_expr(ast[2]) + COMMA +
(string) btable->minargs(func) + COMMA +
(string) btable->maxargs(func) + "))");
emit("PUSH(RAISE(E_ARGS)); else");
args = COMMA + "RET...";
break;
}
}
else
{
COMPILE(ast[2], expression);
emit("if (! argcheck(RET = POP(), " +
(string) btable->minargs(func) + COMMA +
(string) btable->maxargs(func) + "))");
emit("PUSH(RAISE(E_ARGS)); else");
args = COMMA + "RET...";
}
emit("{ JS_PREP(" + (string) (++jumpno) +
"); RET = " + btable->invokation(func, jumpno) + args +
"); JS_END; PUSH(RET); }");
return;
}
case TOK_LBRACKET:
if (TAG(ast[2]) == TOK_RANGE)
stack_kfun3("range", ast[1], ast[2][1], ast[2][2]);
else
stack_kfun2("index", ast[1], ast[2]);
return;
case TOK_BANG:
COMPILE(ast[1], expression);
emit("stack[SP] = TRUTHOF(stack[SP]) ? NUM(0) : NUM(1);");
return;
case TOK_U_MINUS:
COMPILE(ast[1], expression);
emit("stack[SP] = KFUN1(negate, stack[SP]);");
return;
case TOK_EQUAL:
stack_kfun2("equal", ast[1], ast[2]);
return;
case TOK_NEQUAL:
stack_kfun2("nequal", ast[1], ast[2]);
return;
case TOK_LESS:
stack_kfun2("less", ast[1], ast[2]);
return;
case TOK_LSEQUAL:
stack_kfun2("lsequal", ast[1], ast[2]);
return;
case TOK_GREATER:
stack_kfun2("greater", ast[1], ast[2]);
return;
case TOK_GREQUAL:
stack_kfun2("grequal", ast[1], ast[2]);
return;
case TOK_IN:
stack_kfun2("in", ast[1], ast[2]);
return;
# if 0
case TOK_ASSOC:
stack_kfun2("bound", ast[1], ast[2]);
return;
# endif
case TOK_QUESTION:
if (SIMPLE(ast[1]))
{
emit("if (" + bool_expr(ast[1]) + ") {");
}
else
{
COMPILE(ast[1], expression);
emit("if (TRUTHOF(POP())) {");
}
COMPILE(ast[2], expression);
emit("} else {");
COMPILE(ast[3], expression);
emit("}");
return;
case TOK_AND:
COMPILE(ast[1], expression);
emit("if (TRUTHOF(stack[SP])) {");
if (SIMPLE(ast[2]))
{
emit("stack[SP] = " + simple_expr(ast[2]) + ";");
}
else
{
emit("--SP;");
COMPILE(ast[2], expression);
}
emit("}");
return;
case TOK_OR:
COMPILE(ast[1], expression);
emit("if (! TRUTHOF(stack[SP])) {");
if (SIMPLE(ast[2]))
{
emit("stack[SP] = " + simple_expr(ast[2]) + ";");
}
else
{
emit("--SP;");
COMPILE(ast[2], expression);
}
emit("}");
return;
default:
# if DEBUG
error("Unknown token: " + var2str(ast));
# else
error("Unknown token: " + TAG(ast));
# endif
}
}
PROTO(if)
{
int i, sz, close;
string closers;
if (DEBUG)
emit("/* if */");
LINENO(++lineno);
if (SIMPLE(ast[1]))
emit("if (" + bool_expr(ast[1]) + ") {");
else
{
COMPILE(ast[1], expression);
emit("if (TRUTHOF(POP())) {");
}
++nest, COMPILE(ast[2], program), --nest;
i = 3; sz = sizeof(ast);
while (i < sz && ast[i] == TOK_ELSEIF)
{
emit("} else {");
LINENO(++lineno);
if (SIMPLE(ast[i + 1]))
emit("if (" + bool_expr(ast[i + 1]) + ") {");
else
{
COMPILE(ast[i + 1], expression);
emit("if (TRUTHOF(POP())) {");
}
++nest, COMPILE(ast[i + 2], program), --nest;
i += 3, ++close;
}
if (i < sz && ast[i] == TOK_ELSE)
{
emit("} else {");
++lineno;
++nest, COMPILE(ast[i + 1], program), --nest;
}
for (closers = "}"; close--; closers += " }");
emit(closers);
++lineno;
}
PROTO(for)
{
string key, value;
int key_var, value_var, table_iterp;
if (DEBUG)
emit("/* for */");
if (arrayp(ast[1]))
{
table_iterp = 1;
key = ast[1][0];
value = ast[1][1];
key_var = VAR_ID(var_ref(key));
value_var = VAR_ID(var_ref(value));
}
else
{
key = ast[1];
key_var = VAR_ID(var_ref(key));
}
sp_overhead += 3;
if (TAG(ast[2]) == TOK_RANGE)
{
LINENO(++lineno);
COMPILE(ast[2][1], expression);
COMPILE(ast[2][2], expression);
emit("if (! NUMP(stack[SP - 1]) || ! NUMP(stack[SP]))" +
" { SP -= 2; RAISE(E_TYPE); } else {" +
" ++SP; stack[SP] = stack[SP - 2];");
emit("while (stack[SP] <= stack[SP - 1]) {" +
" avar(info" + COMMA + (string) key_var + COMMA + "stack[SP]);");
var_assigned(key);
++nest, COMPILE(ast[3], program), --nest;
emit("++stack[SP]; } SP -= 3; }");
}
else
{
LINENO(++lineno);
COMPILE(ast[2][1], expression);
if (table_iterp)
{
emit("if (! TBLP(stack[SP]))" +
" { --SP; RAISE(E_TYPE); } else {" +
" stack[SP] = map_values(TBLVAL(stack[SP])); PUSH(0);");
emit("while (stack[SP] < sizeof(stack[SP - 1])) { ++SP;");
emit("stack[SP] = stack[SP - 2][stack[SP - 1]][0];" +
" avar(info" + COMMA + (string) key_var + COMMA +
"stack[SP]);" +
" stack[SP] = stack[SP - 2][stack[SP - 1]][1];" +
" avar(info" + COMMA + (string) value_var + COMMA +
"stack[SP]);");
var_assigned(key);
var_assigned(value);
}
else
{
emit("if (! LSTP(stack[SP]))" +
" { --SP; RAISE(E_TYPE); } else {" +
" stack[SP] = LSTVAL(stack[SP]); PUSH(0);");
emit("while (stack[SP] < sizeof(stack[SP - 1])) { ++SP;" +
" stack[SP] = stack[SP - 2][stack[SP - 1]];" +
" avar(info" + COMMA + key_var + COMMA + "stack[SP]);");
var_assigned(key);
}
++nest, COMPILE(ast[3], program), --nest;
# if DEBUG
emit("debug(\"LOOP, sp == \" + sp + \", counter == \" + stack[sp - 1] + \", max == \" + var2str(stack[sp - 2]));");
# endif
emit("++stack[--SP]; } SP -= 2; }");
}
sp_overhead -= 3;
++lineno;
}
PROTO(while)
{
if (DEBUG)
emit("/* while */");
emit("while (1) {");
LINENO(++lineno);
if (SIMPLE(ast[1]))
emit("if (" + bool_expr(ast[1]) + ") {");
else
{
COMPILE(ast[1], expression);
emit("if (TRUTHOF(POP())) {");
}
++nest, COMPILE(ast[2], program), --nest;
emit("} else break; }");
++lineno;
}
PROTO(fork)
{
string var;
int var_id, save_fork;
if (DEBUG)
emit("/* fork */");
if (var = ast[1])
var_id = VAR_ID(var_ref(var));
LINENO(++lineno);
COMPILE(ast[2], expression);
emit("if (! NUMP(stack[SP])) { --SP; RAISE(E_TYPE); } else {" +
" PUSH(NUM(global->take_task()));");
if (var)
{
emit("avar(info" + COMMA + (string) var_id + COMMA + "stack[SP]);");
var_assigned(var);
}
emit("STORE(); PREP_FORK(" + (string) (++fork_id) + "); RESET(); }");
save_fork = fork_cur;
fork_cur = fork_id;
forked += ({ ({ "global->task_started(info[I_TASKID]);" }) });
++nest, COMPILE(ast[3], program), --nest;
fork_cur = save_fork;
++lineno;
}
PROTO(return)
{
string value;
if (DEBUG)
emit("/* return */");
if (sp_overhead)
emit("SP -= " + sp_overhead + ";");
LINENO(++lineno);
if (sizeof(ast) == 1)
emit("return STORE(), del(), NUM(0);");
else
{
if (SIMPLE(ast[1]))
value = simple_expr(ast[1]);
else
COMPILE(ast[1], expression), value = "POP()";
emit("return RET = " + value + COMMA + "STORE(), del(), RET;");
}
}