/* * 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;"); } }