X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fnasal%2Fcodegen.c;h=495456a6cf91b72a8901b52742027983dfe96e61;hb=27a91062bda197ff0e5c3d95f43a2f89c353fdc5;hp=fa605516f38c8a7f7f4d0367d28583b3dbbd43c4;hpb=d2cbed151bde4f95769a036de6ea0404357f7dbf;p=simgear.git diff --git a/simgear/nasal/codegen.c b/simgear/nasal/codegen.c index fa605516..495456a6 100644 --- a/simgear/nasal/codegen.c +++ b/simgear/nasal/codegen.c @@ -1,32 +1,36 @@ +#include #include "parse.h" #include "code.h" +#define MAX_FUNARGS 32 + // These are more sensical predicate names in most contexts in this file #define LEFT(tok) ((tok)->children) #define RIGHT(tok) ((tok)->lastChild) -#define BINARY(tok) (LEFT(tok) && RIGHT(tok) && LEFT(tok) != RIGHT(tok)) +#define UNARY(tok) (LEFT(tok) && LEFT(tok) == RIGHT(tok)) +#define BINARY(tok) (LEFT(tok) && RIGHT(tok) && LEFT(tok)->next == RIGHT(tok)) // Forward references for recursion static void genExpr(struct Parser* p, struct Token* t); static void genExprList(struct Parser* p, struct Token* t); +static naRef newLambda(struct Parser* p, struct Token* t); -static void emit(struct Parser* p, int byte) +static void emit(struct Parser* p, int val) { - if(p->cg->nBytes >= p->cg->codeAlloced) { + if(p->cg->codesz >= p->cg->codeAlloced) { int i, sz = p->cg->codeAlloced * 2; - unsigned char* buf = naParseAlloc(p, sz); + unsigned short* buf = naParseAlloc(p, sz*sizeof(unsigned short)); for(i=0; icg->codeAlloced; i++) buf[i] = p->cg->byteCode[i]; p->cg->byteCode = buf; p->cg->codeAlloced = sz; } - p->cg->byteCode[p->cg->nBytes++] = (unsigned char)byte; + p->cg->byteCode[p->cg->codesz++] = (unsigned short)val; } -static void emitImmediate(struct Parser* p, int byte, int arg) +static void emitImmediate(struct Parser* p, int val, int arg) { - emit(p, byte); - emit(p, arg >> 8); - emit(p, arg & 0xff); + emit(p, val); + emit(p, arg); } static void genBinOp(int op, struct Parser* p, struct Token* t) @@ -44,96 +48,210 @@ static int newConstant(struct Parser* p, naRef c) naVec_append(p->cg->consts, c); i = naVec_size(p->cg->consts) - 1; if(i > 0xffff) naParseError(p, "too many constants in code block", 0); - return i; -} - -static naRef getConstant(struct Parser* p, int idx) -{ - return naVec_get(p->cg->consts, idx); + return i; } // Interns a scalar (!) constant and returns its index static int internConstant(struct Parser* p, naRef c) { int i, n = naVec_size(p->cg->consts); + if(IS_CODE(c)) return newConstant(p, c); for(i=0; icg->consts, i); - if(IS_NUM(b) && IS_NUM(c) && b.num == c.num) - return i; - if(IS_REF(b) && IS_REF(c) && b.ref.ptr.obj->type != c.ref.ptr.obj->type) - continue; - if(naEqual(b, c)) - return i; + if(IS_NUM(b) && IS_NUM(c) && b.num == c.num) return i; + else if(IS_NIL(b) && IS_NIL(c)) return i; + else if(naStrEqual(b, c)) return i; } return newConstant(p, c); } -static void genScalarConstant(struct Parser* p, struct Token* t) +/* FIXME: this API is fundamentally a resource leak, because symbols + * can't be deregistered. The "proper" way to do this would be to + * keep a reference count for each symbol, and decrement it when a + * code object referencing it is deleted. */ +naRef naInternSymbol(naRef sym) { - naRef c = (t->str - ? naStr_fromdata(naNewString(p->context), t->str, t->strlen) - : naNum(t->num)); - int idx = internConstant(p, c); - emitImmediate(p, OP_PUSHCONST, idx); + naRef result; + if(naHash_get(globals->symbols, sym, &result)) + return result; + naHash_set(globals->symbols, sym, sym); + return sym; } -static int genLValue(struct Parser* p, struct Token* t) +static int findConstantIndex(struct Parser* p, struct Token* t) { - if(t->type == TOK_LPAR) { - return genLValue(p, LEFT(t)); // Handle stuff like "(a) = 1" + naRef c, dummy; + if(t->type == TOK_NIL) c = naNil(); + else if(t->str) { + c = naStr_fromdata(naNewString(p->context), t->str, t->strlen); + naHash_get(globals->symbols, c, &dummy); // noop, make c immutable + if(t->type == TOK_SYMBOL) c = naInternSymbol(c); + } else if(t->type == TOK_FUNC) c = newLambda(p, t); + else if(t->type == TOK_LITERAL) c = naNum(t->num); + else naParseError(p, "invalid/non-constant constant", t->line); + return internConstant(p, c); +} + +static int genScalarConstant(struct Parser* p, struct Token* t) +{ + int idx; + if(t->str == 0 && t->num == 1) { emit(p, OP_PUSHONE); return 0; } + if(t->str == 0 && t->num == 0) { emit(p, OP_PUSHZERO); return 0; } + emitImmediate(p, OP_PUSHCONST, idx = findConstantIndex(p, t)); + return idx; +} + +static int genLValue(struct Parser* p, struct Token* t, int* cidx) +{ + if(!t) naParseError(p, "bad lvalue", -1); + if(t->type == TOK_LPAR && t->rule != PREC_SUFFIX) { + return genLValue(p, LEFT(t), cidx); // Handle stuff like "(a) = 1" } else if(t->type == TOK_SYMBOL) { - genScalarConstant(p, t); - return OP_SETLOCAL; + *cidx = genScalarConstant(p, t); + return OP_SETSYM; } else if(t->type == TOK_DOT && RIGHT(t) && RIGHT(t)->type == TOK_SYMBOL) { genExpr(p, LEFT(t)); - genScalarConstant(p, RIGHT(t)); + *cidx = genScalarConstant(p, RIGHT(t)); return OP_SETMEMBER; } else if(t->type == TOK_LBRA) { genExpr(p, LEFT(t)); genExpr(p, RIGHT(t)); return OP_INSERT; + } else if(t->type == TOK_VAR && RIGHT(t) && RIGHT(t)->type == TOK_SYMBOL) { + *cidx = genScalarConstant(p, RIGHT(t)); + return OP_SETLOCAL; } else { naParseError(p, "bad lvalue", t->line); return -1; } } -static void genLambda(struct Parser* p, struct Token* t) +static void genEqOp(int op, struct Parser* p, struct Token* t) +{ + int cidx, n = 2, setop = genLValue(p, LEFT(t), &cidx); + if(setop == OP_SETMEMBER) { + emit(p, OP_DUP2); + emit(p, OP_POP); + emitImmediate(p, OP_MEMBER, cidx); + } else if(setop == OP_INSERT) { + emit(p, OP_DUP2); + emit(p, OP_EXTRACT); + } else { + emitImmediate(p, OP_LOCAL, cidx); + n = 1; + } + genExpr(p, RIGHT(t)); + emit(p, op); + emit(p, n == 1 ? OP_XCHG : OP_XCHG2); + emit(p, setop); +} + +static int defArg(struct Parser* p, struct Token* t) +{ + if(t->type == TOK_LPAR) { + // http://code.google.com/p/flightgear-bugs/issues/detail?id=737 + // TOK_LPAR can mean multi-value assignment or function call, + // disambigaute by checking the rule of the token + if (t->rule == PREC_SUFFIX) + naParseError(p, "default arguments cannot be function calls", t->line); + return defArg(p, RIGHT(t)); + } + + if(t->type == TOK_MINUS && RIGHT(t) && + RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) + { + /* default arguments are constants, but "-1" parses as two + * tokens, so we have to subset the expression generator for that + * case */ + RIGHT(t)->num *= -1; + return defArg(p, RIGHT(t)); + } + if(t->type == TOK_CAT && RIGHT(t) && + RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) + { + /* default arguments are constants, but "~1" parses as two + * tokens, so we have to subset the expression generator for that + * case */ + RIGHT(t)->num = ~(int)RIGHT(t)->num; + return defArg(p, RIGHT(t)); + } + return findConstantIndex(p, t); +} + +static void genArgList(struct Parser* p, struct naCode* c, struct Token* t) +{ + naRef sym; + if(t->type == TOK_EMPTY) return; + if(!IDENTICAL(p->cg->restArgSym, globals->argRef)) + naParseError(p, "remainder must be last", t->line); + if(t->type == TOK_ELLIPSIS) { + if(LEFT(t)->type != TOK_SYMBOL) + naParseError(p, "bad function argument expression", t->line); + sym = naStr_fromdata(naNewString(p->context), + LEFT(t)->str, LEFT(t)->strlen); + p->cg->restArgSym = naInternSymbol(sym); + c->needArgVector = 1; + } else if(t->type == TOK_ASSIGN) { + if(LEFT(t)->type != TOK_SYMBOL) + naParseError(p, "bad function argument expression", t->line); + p->cg->optArgSyms[c->nOptArgs] = findConstantIndex(p, LEFT(t)); + p->cg->optArgVals[c->nOptArgs++] = defArg(p, RIGHT(t)); + } else if(t->type == TOK_SYMBOL) { + if(c->nOptArgs) + naParseError(p, "optional arguments must be last", t->line); + if(c->nArgs >= MAX_FUNARGS) + naParseError(p, "too many named function arguments", t->line); + p->cg->argSyms[c->nArgs++] = findConstantIndex(p, t); + } else if(t->type == TOK_COMMA) { + if(!LEFT(t) || !RIGHT(t)) + naParseError(p, "empty function argument", t->line); + genArgList(p, c, LEFT(t)); + genArgList(p, c, RIGHT(t)); + } else + naParseError(p, "bad function argument expression", t->line); +} + +static naRef newLambda(struct Parser* p, struct Token* t) { - int idx; struct CodeGenerator* cgSave; naRef codeObj; - if(LEFT(t)->type != TOK_LCURL) + struct Token* arglist; + if(RIGHT(t)->type != TOK_LCURL) naParseError(p, "bad function definition", t->line); // Save off the generator state while we do the new one cgSave = p->cg; - codeObj = naCodeGen(p, LEFT(LEFT(t))); + arglist = LEFT(t)->type == TOK_LPAR ? LEFT(LEFT(t)) : 0; + codeObj = naCodeGen(p, LEFT(RIGHT(t)), arglist); p->cg = cgSave; + return codeObj; +} - idx = newConstant(p, codeObj); - emitImmediate(p, OP_PUSHCONST, idx); +static void genLambda(struct Parser* p, struct Token* t) +{ + emitImmediate(p, OP_PUSHCONST, newConstant(p, newLambda(p, t))); } -static void genList(struct Parser* p, struct Token* t) +static int genList(struct Parser* p, struct Token* t, int doAppend) { - if(t->type == TOK_COMMA) { + if(!t || t->type == TOK_EMPTY) { + return 0; + } else if(t->type == TOK_COMMA) { genExpr(p, LEFT(t)); - emit(p, OP_VAPPEND); - genList(p, RIGHT(t)); - } else if(t->type == TOK_EMPTY) { - return; + if(doAppend) emit(p, OP_VAPPEND); + return 1 + genList(p, RIGHT(t), doAppend); } else { genExpr(p, t); - emit(p, OP_VAPPEND); + if(doAppend) emit(p, OP_VAPPEND); + return 1; } } static void genHashElem(struct Parser* p, struct Token* t) { - if(t->type == TOK_EMPTY) + if(!t || t->type == TOK_EMPTY) return; - if(t->type != TOK_COLON) + if(t->type != TOK_COLON || !LEFT(t)) naParseError(p, "bad hash/object initializer", t->line); if(LEFT(t)->type == TOK_SYMBOL) genScalarConstant(p, LEFT(t)); else if(LEFT(t)->type == TOK_LITERAL) genExpr(p, LEFT(t)); @@ -144,32 +262,45 @@ static void genHashElem(struct Parser* p, struct Token* t) static void genHash(struct Parser* p, struct Token* t) { - if(t->type == TOK_COMMA) { + if(t && t->type == TOK_COMMA) { genHashElem(p, LEFT(t)); genHash(p, RIGHT(t)); - } else if(t->type != TOK_EMPTY) { + } else if(t && t->type != TOK_EMPTY) { genHashElem(p, t); } } +static int isHashcall(struct Parser* p, struct Token* t) +{ + if(t) { + int sep = LEFT(t) && t->type == TOK_COMMA ? t->children->type : t->type; + return sep == TOK_COLON; + } + return 0; +} + static void genFuncall(struct Parser* p, struct Token* t) { - int op = OP_FCALL; + int method = 0; if(LEFT(t)->type == TOK_DOT) { + method = 1; genExpr(p, LEFT(LEFT(t))); emit(p, OP_DUP); - genScalarConstant(p, RIGHT(LEFT(t))); - emit(p, OP_MEMBER); - op = OP_MCALL; + emitImmediate(p, OP_MEMBER, findConstantIndex(p, RIGHT(LEFT(t)))); } else { genExpr(p, LEFT(t)); } - emit(p, OP_NEWVEC); - if(RIGHT(t)) genList(p, RIGHT(t)); - emit(p, op); + if(isHashcall(p, RIGHT(t))) { + emit(p, OP_NEWHASH); + genHash(p, RIGHT(t)); + emit(p, method ? OP_MCALLH : OP_FCALLH); + } else { + int nargs = genList(p, RIGHT(t), 0); + emitImmediate(p, method ? OP_MCALL : OP_FCALL, nargs); + } } -static void pushLoop(struct Parser* p, struct Token* label) +static int startLoop(struct Parser* p, struct Token* label) { int i = p->cg->loopTop; p->cg->loops[i].breakIP = 0xffffff; @@ -177,13 +308,7 @@ static void pushLoop(struct Parser* p, struct Token* label) p->cg->loops[i].label = label; p->cg->loopTop++; emit(p, OP_MARK); -} - -static void popLoop(struct Parser* p) -{ - p->cg->loopTop--; - if(p->cg->loopTop < 0) naParseError(p, "BUG: loop stack underflow", -1); - emit(p, OP_UNMARK); + return p->cg->codesz; } // Emit a jump operation, and return the location of the address in @@ -192,30 +317,25 @@ static int emitJump(struct Parser* p, int op) { int ip; emit(p, op); - ip = p->cg->nBytes; - emit(p, 0xff); // dummy address - emit(p, 0xff); + ip = p->cg->codesz; + emit(p, 0xffff); // dummy address return ip; } // Points a previous jump instruction at the current "end-of-bytecode" static void fixJumpTarget(struct Parser* p, int spot) { - p->cg->byteCode[spot] = p->cg->nBytes >> 8; - p->cg->byteCode[spot+1] = p->cg->nBytes & 0xff; + p->cg->byteCode[spot] = p->cg->codesz; } static void genShortCircuit(struct Parser* p, struct Token* t) { - int jumpNext, jumpEnd, isAnd = (t->type == TOK_AND); + int end; genExpr(p, LEFT(t)); - if(isAnd) emit(p, OP_NOT); - jumpNext = emitJump(p, OP_JIFNOT); - emit(p, isAnd ? OP_PUSHNIL : OP_PUSHONE); - jumpEnd = emitJump(p, OP_JMP); - fixJumpTarget(p, jumpNext); + end = emitJump(p, t->type == TOK_AND ? OP_JIFNOT : OP_JIFTRUE); + emit(p, OP_POP); genExpr(p, RIGHT(t)); - fixJumpTarget(p, jumpEnd); + fixJumpTarget(p, end); } @@ -223,7 +343,7 @@ static void genIf(struct Parser* p, struct Token* tif, struct Token* telse) { int jumpNext, jumpEnd; genExpr(p, tif->children); // the test - jumpNext = emitJump(p, OP_JIFNOT); + jumpNext = emitJump(p, OP_JIFNOTPOP); genExprList(p, tif->children->next->children); // the body jumpEnd = emitJump(p, OP_JMP); fixJumpTarget(p, jumpNext); @@ -241,10 +361,25 @@ static void genIfElse(struct Parser* p, struct Token* t) genIf(p, t, t->children->next->next); } -static int countSemis(struct Token* t) +static void genQuestion(struct Parser* p, struct Token* t) { - if(!t || t->type != TOK_SEMI) return 0; - return 1 + countSemis(RIGHT(t)); + int jumpNext, jumpEnd; + if(!RIGHT(t) || RIGHT(t)->type != TOK_COLON) + naParseError(p, "invalid ?: expression", t->line); + genExpr(p, LEFT(t)); // the test + jumpNext = emitJump(p, OP_JIFNOTPOP); + genExpr(p, LEFT(RIGHT(t))); // the "if true" expr + jumpEnd = emitJump(p, OP_JMP); + fixJumpTarget(p, jumpNext); + genExpr(p, RIGHT(RIGHT(t))); // the "else" expr + fixJumpTarget(p, jumpEnd); +} + +static int countList(struct Token* t, int type) +{ + int n; + for(n = 1; t && t->type == type; t = RIGHT(t)) n++; + return n; } static void genLoop(struct Parser* p, struct Token* body, @@ -256,7 +391,7 @@ static void genLoop(struct Parser* p, struct Token* body, p->cg->loops[p->cg->loopTop-1].breakIP = jumpEnd-1; jumpOverContinue = emitJump(p, OP_JMP); - p->cg->loops[p->cg->loopTop-1].contIP = p->cg->nBytes; + p->cg->loops[p->cg->loopTop-1].contIP = p->cg->codesz; cont = emitJump(p, OP_JMP); fixJumpTarget(p, jumpOverContinue); @@ -264,9 +399,10 @@ static void genLoop(struct Parser* p, struct Token* body, emit(p, OP_POP); fixJumpTarget(p, cont); if(update) { genExpr(p, update); emit(p, OP_POP); } - emitImmediate(p, OP_JMP, loopTop); + emitImmediate(p, OP_JMPLOOP, loopTop); fixJumpTarget(p, jumpEnd); - popLoop(p); + p->cg->loopTop--; + emit(p, OP_UNMARK); emit(p, OP_PUSHNIL); // Leave something on the stack } @@ -276,24 +412,22 @@ static void genForWhile(struct Parser* p, struct Token* init, { int loopTop, jumpEnd; if(init) { genExpr(p, init); emit(p, OP_POP); } - pushLoop(p, label); - loopTop = p->cg->nBytes; + loopTop = startLoop(p, label); genExpr(p, test); - jumpEnd = emitJump(p, OP_JIFNOT); + jumpEnd = emitJump(p, OP_JIFNOTPOP); genLoop(p, body, update, label, loopTop, jumpEnd); } static void genWhile(struct Parser* p, struct Token* t) { struct Token *test=LEFT(t)->children, *body, *label=0; - int semis = countSemis(test); - if(semis == 1) { + int len = countList(test, TOK_SEMI); + if(len == 2) { label = LEFT(test); if(!label || label->type != TOK_SYMBOL) naParseError(p, "bad loop label", t->line); test = RIGHT(test); - } - else if(semis != 0) + } else if(len != 1) naParseError(p, "too many semicolons in while test", t->line); body = LEFT(RIGHT(t)); genForWhile(p, 0, test, 0, body, label); @@ -303,17 +437,14 @@ static void genFor(struct Parser* p, struct Token* t) { struct Token *init, *test, *body, *update, *label=0; struct Token *h = LEFT(t)->children; - int semis = countSemis(h); - if(semis == 3) { + int len = countList(h, TOK_SEMI); + if(len == 4) { if(!LEFT(h) || LEFT(h)->type != TOK_SYMBOL) naParseError(p, "bad loop label", h->line); label = LEFT(h); h=RIGHT(h); - } else if(semis != 2) { + } else if(len != 3) naParseError(p, "wrong number of terms in for header", t->line); - } - - // Parse tree hell :) init = LEFT(h); test = LEFT(RIGHT(h)); update = RIGHT(RIGHT(h)); @@ -323,33 +454,33 @@ static void genFor(struct Parser* p, struct Token* t) static void genForEach(struct Parser* p, struct Token* t) { - int loopTop, jumpEnd, assignOp; + int loopTop, jumpEnd, assignOp, dummy; struct Token *elem, *body, *vec, *label=0; struct Token *h = LEFT(LEFT(t)); - int semis = countSemis(h); - if(semis == 2) { + int len = countList(h, TOK_SEMI); + if(len == 3) { if(!LEFT(h) || LEFT(h)->type != TOK_SYMBOL) naParseError(p, "bad loop label", h->line); label = LEFT(h); h = RIGHT(h); - } else if (semis != 1) { + } else if (len != 2) { naParseError(p, "wrong number of terms in foreach header", t->line); } elem = LEFT(h); vec = RIGHT(h); body = RIGHT(t)->children; - pushLoop(p, label); genExpr(p, vec); emit(p, OP_PUSHZERO); - loopTop = p->cg->nBytes; - emit(p, OP_EACH); - jumpEnd = emitJump(p, OP_JIFNIL); - assignOp = genLValue(p, elem); - emit(p, OP_XCHG); + loopTop = startLoop(p, label); + emit(p, t->type == TOK_FOREACH ? OP_EACH : OP_INDEX); + jumpEnd = emitJump(p, OP_JIFEND); + assignOp = genLValue(p, elem, &dummy); emit(p, assignOp); emit(p, OP_POP); genLoop(p, body, 0, label, loopTop, jumpEnd); + emit(p, OP_POP); // Pull off the vector and index + emit(p, OP_POP); } static int tokMatch(struct Token* a, struct Token* b) @@ -364,6 +495,11 @@ static int tokMatch(struct Token* a, struct Token* b) static void genBreakContinue(struct Parser* p, struct Token* t) { int levels = 1, loop = -1, bp, cp, i; + // http://code.google.com/p/flightgear-bugs/issues/detail?id=587 + // Make sure we are inside of a loop + if(p->cg->loopTop <= 0) + naParseError(p, "break/continue outside of a valid loop", t->line); + if(RIGHT(t)) { if(RIGHT(t)->type != TOK_SYMBOL) naParseError(p, "bad break/continue label", t->line); @@ -377,66 +513,156 @@ static void genBreakContinue(struct Parser* p, struct Token* t) bp = p->cg->loops[p->cg->loopTop - levels].breakIP; cp = p->cg->loops[p->cg->loopTop - levels].contIP; for(i=0; itype == TOK_BREAK) - emit(p, OP_PUSHNIL); // breakIP is always a JIFNOT/JIFNIL! + emit(p, OP_PUSHEND); // breakIP is always a JIFNOTPOP/JIFEND! emitImmediate(p, OP_JMP, t->type == TOK_BREAK ? bp : cp); } +static void newLineEntry(struct Parser* p, int line) +{ + int i; + if(p->cg->nextLineIp >= p->cg->nLineIps) { + int nsz = p->cg->nLineIps*2 + 1; + unsigned short* n = naParseAlloc(p, sizeof(unsigned short)*2*nsz); + for(i=0; i<(p->cg->nextLineIp*2); i++) + n[i] = p->cg->lineIps[i]; + p->cg->lineIps = n; + p->cg->nLineIps = nsz; + } + p->cg->lineIps[p->cg->nextLineIp++] = (unsigned short) p->cg->codesz; + p->cg->lineIps[p->cg->nextLineIp++] = (unsigned short) line; +} + +static int parListLen(struct Token* t) +{ + if(t->type != TOK_LPAR || !LEFT(t) || LEFT(t)->type != TOK_COMMA) return 0; + return countList(LEFT(t), TOK_COMMA); +} + +static void genCommaList(struct Parser* p, struct Token* t) +{ + if(t->type != TOK_COMMA) { genExpr(p, t); return; } + genCommaList(p, RIGHT(t)); + genExpr(p, LEFT(t)); +} + +static void genMultiLV(struct Parser* p, struct Token* t, int var) +{ + if(!var) { emit(p, genLValue(p, t, &var)); return; } + if(t->type != TOK_SYMBOL) naParseError(p, "bad lvalue", t->line); + genScalarConstant(p, t); + emit(p, OP_SETLOCAL); +} + +static void genAssign(struct Parser* p, struct Token* t) +{ + struct Token *lv = LEFT(t), *rv = RIGHT(t); + int len, dummy, var=0; + if (!lv) + naParseError(p, "bad assignment, missing variable", t->line); + else + if(parListLen(lv) || (lv->type == TOK_VAR && parListLen(RIGHT(lv)))) { + if(lv->type == TOK_VAR) { lv = RIGHT(lv); var = 1; } + len = parListLen(lv); + // http://code.google.com/p/flightgear-bugs/issues/detail?id=585 + // TOK_LPAR can mean multi-value assignment or function call, + // disambigaute by checking the rule of the token + if(rv->type == TOK_LPAR && rv->rule != PREC_SUFFIX) { + if(len != parListLen(rv)) + naParseError(p, "bad assignment count", rv->line); + genCommaList(p, LEFT(rv)); + } else { + genExpr(p, rv); + emitImmediate(p, OP_UNPACK, len); + } + for(t = LEFT(lv); t && t->type == TOK_COMMA; t = RIGHT(t)) { + genMultiLV(p, LEFT(t), var); + emit(p, OP_POP); + } + genMultiLV(p, t, var); + } else { + genExpr(p, rv); + emit(p, genLValue(p, lv, &dummy)); + } +} + +static void genSlice(struct Parser* p, struct Token* t) +{ + if(!t || t->type==TOK_EMPTY) naParseError(p, "empty slice expression", -1); + if(t->type == TOK_COLON) { + if(LEFT(t)) genExpr(p, LEFT(t)); else emit(p, OP_PUSHNIL); + if(RIGHT(t)) genExpr(p, RIGHT(t)); else emit(p, OP_PUSHNIL); + emit(p, OP_SLICE2); + } else { + genExpr(p, t); + emit(p, OP_SLICE); + } +} + +static void genExtract(struct Parser* p, struct Token* t) +{ + genExpr(p, LEFT(t)); + if(countList(RIGHT(t), TOK_COMMA) == 1 && RIGHT(t)->type != TOK_COLON) { + genExpr(p, RIGHT(t)); + emit(p, OP_EXTRACT); + } else { + emit(p, OP_NEWVEC); + for(t = RIGHT(t); t->type == TOK_COMMA; t = RIGHT(t)) + genSlice(p, LEFT(t)); + genSlice(p, t); + emit(p, OP_XCHG); + emit(p, OP_POP); + } +} + static void genExpr(struct Parser* p, struct Token* t) { int i; - if(t == 0) - naParseError(p, "BUG: null subexpression", -1); + if(!t) naParseError(p, "parse error", -1); // throw line -1... + p->errLine = t->line; // ...to use this one instead if(t->line != p->cg->lastLine) - emitImmediate(p, OP_LINE, t->line); + newLineEntry(p, t->line); p->cg->lastLine = t->line; switch(t->type) { - case TOK_IF: - genIfElse(p, t); - break; - case TOK_WHILE: - genWhile(p, t); - break; - case TOK_FOR: - genFor(p, t); - break; - case TOK_FOREACH: + case TOK_TOP: genExprList(p, LEFT(t)); break; + case TOK_IF: genIfElse(p, t); break; + case TOK_QUESTION: genQuestion(p, t); break; + case TOK_WHILE: genWhile(p, t); break; + case TOK_FOR: genFor(p, t); break; + case TOK_FUNC: genLambda(p, t); break; + case TOK_ASSIGN: genAssign(p, t); break; + case TOK_LITERAL: genScalarConstant(p, t); break; + case TOK_FOREACH: case TOK_FORINDEX: genForEach(p, t); break; case TOK_BREAK: case TOK_CONTINUE: genBreakContinue(p, t); break; - case TOK_TOP: - genExprList(p, LEFT(t)); - break; - case TOK_FUNC: - genLambda(p, t); - break; case TOK_LPAR: - if(BINARY(t) || !RIGHT(t)) genFuncall(p, t); // function invocation - else genExpr(p, LEFT(t)); // simple parenthesis + if(BINARY(t) || !RIGHT(t)) genFuncall(p, t); + else genExpr(p, LEFT(t)); break; case TOK_LBRA: - if(BINARY(t)) { - genBinOp(OP_EXTRACT, p, t); // a[i] - } else { + if(UNARY(t)) { emit(p, OP_NEWVEC); - genList(p, LEFT(t)); + genList(p, LEFT(t), 1); + } + else if(BINARY(t)) { + genExtract(p, t); + } else { + // forbid usage as 'vec[]' + naParseError(p, "missing index or slice expression(s)", t->line); } break; case TOK_LCURL: emit(p, OP_NEWHASH); genHash(p, LEFT(t)); break; - case TOK_ASSIGN: - i = genLValue(p, LEFT(t)); - genExpr(p, RIGHT(t)); - emit(p, i); // use the op appropriate to the lvalue - break; case TOK_RETURN: if(RIGHT(t)) genExpr(p, RIGHT(t)); else emit(p, OP_PUSHNIL); + for(i=0; icg->loopTop; i++) emit(p, OP_UNMARK); emit(p, OP_RETURN); break; case TOK_NOT: @@ -444,16 +670,12 @@ static void genExpr(struct Parser* p, struct Token* t) emit(p, OP_NOT); break; case TOK_SYMBOL: - genScalarConstant(p, t); - emit(p, OP_LOCAL); - break; - case TOK_LITERAL: - genScalarConstant(p, t); + emitImmediate(p, OP_LOCAL, findConstantIndex(p, t)); break; case TOK_MINUS: if(BINARY(t)) { genBinOp(OP_MINUS, p, t); // binary subtraction - } else if(RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) { + } else if(RIGHT(t) && RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) { RIGHT(t)->num *= -1; // Pre-negate constants genScalarConstant(p, RIGHT(t)); } else { @@ -465,28 +687,53 @@ static void genExpr(struct Parser* p, struct Token* t) genExpr(p, RIGHT(t)); // unary negation (see also TOK_MINUS!) emit(p, OP_NEG); break; + case TOK_CAT: + if(BINARY(t)) { + genBinOp(OP_CAT, p, t); // string concatenation + } else if(RIGHT(t) && RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) { + RIGHT(t)->num = ~(int)RIGHT(t)->num; // Pre-negate constants + genScalarConstant(p, RIGHT(t)); + } else { + genExpr(p, RIGHT(t)); // unary, bitwise negation + emit(p, OP_BIT_NEG); + } + break; + case TOK_BIT_NEG: + genExpr(p, RIGHT(t)); // unary, bitwise negation (see also TOK_CAT!) + emit(p, OP_BIT_NEG); + break; case TOK_DOT: genExpr(p, LEFT(t)); - if(RIGHT(t)->type != TOK_SYMBOL) + if(!RIGHT(t) || RIGHT(t)->type != TOK_SYMBOL) naParseError(p, "object field not symbol", RIGHT(t)->line); - genScalarConstant(p, RIGHT(t)); - emit(p, OP_MEMBER); + emitImmediate(p, OP_MEMBER, findConstantIndex(p, RIGHT(t))); break; case TOK_EMPTY: case TOK_NIL: - emit(p, OP_PUSHNIL); break; // *NOT* a noop! + emit(p, OP_PUSHNIL); + break; case TOK_AND: case TOK_OR: genShortCircuit(p, t); break; + case TOK_BIT_AND:genBinOp(OP_BIT_AND, p, t); break; + case TOK_BIT_OR: genBinOp(OP_BIT_OR, p, t); break; + case TOK_BIT_XOR:genBinOp(OP_BIT_XOR, p, t); break; case TOK_MUL: genBinOp(OP_MUL, p, t); break; case TOK_PLUS: genBinOp(OP_PLUS, p, t); break; case TOK_DIV: genBinOp(OP_DIV, p, t); break; - case TOK_CAT: genBinOp(OP_CAT, p, t); break; case TOK_LT: genBinOp(OP_LT, p, t); break; case TOK_LTE: genBinOp(OP_LTE, p, t); break; case TOK_EQ: genBinOp(OP_EQ, p, t); break; case TOK_NEQ: genBinOp(OP_NEQ, p, t); break; case TOK_GT: genBinOp(OP_GT, p, t); break; case TOK_GTE: genBinOp(OP_GTE, p, t); break; + case TOK_PLUSEQ: genEqOp(OP_PLUS, p, t); break; + case TOK_MINUSEQ: genEqOp(OP_MINUS, p, t); break; + case TOK_MULEQ: genEqOp(OP_MUL, p, t); break; + case TOK_DIVEQ: genEqOp(OP_DIV, p, t); break; + case TOK_CATEQ: genEqOp(OP_CAT, p, t); break; + case TOK_BIT_ANDEQ: genEqOp(OP_BIT_AND, p, t); break; + case TOK_BIT_OREQ: genEqOp(OP_BIT_OR, p, t); break; + case TOK_BIT_XOREQ: genEqOp(OP_BIT_XOR, p, t); break; default: naParseError(p, "parse error", t->line); }; @@ -494,7 +741,7 @@ static void genExpr(struct Parser* p, struct Token* t) static void genExprList(struct Parser* p, struct Token* t) { - if(t->type == TOK_SEMI) { + if(t && t->type == TOK_SEMI) { genExpr(p, LEFT(t)); if(RIGHT(t) && RIGHT(t)->type != TOK_EMPTY) { emit(p, OP_POP); @@ -505,7 +752,7 @@ static void genExprList(struct Parser* p, struct Token* t) } } -naRef naCodeGen(struct Parser* p, struct Token* t) +naRef naCodeGen(struct Parser* p, struct Token* block, struct Token* arglist) { int i; naRef codeObj; @@ -514,26 +761,53 @@ naRef naCodeGen(struct Parser* p, struct Token* t) cg.lastLine = 0; cg.codeAlloced = 1024; // Start fairly big, this is a cheap allocation - cg.byteCode = naParseAlloc(p, cg.codeAlloced); - cg.nBytes = 0; + cg.byteCode = naParseAlloc(p, cg.codeAlloced *sizeof(unsigned short)); + cg.codesz = 0; cg.consts = naNewVector(p->context); cg.loopTop = 0; + cg.lineIps = 0; + cg.nLineIps = 0; + cg.nextLineIp = 0; p->cg = &cg; - genExprList(p, t); - + genExprList(p, block); + emit(p, OP_RETURN); + // Now make a code object codeObj = naNewCode(p->context); - code = codeObj.ref.ptr.code; - code->nBytes = cg.nBytes; - code->byteCode = naAlloc(cg.nBytes); - for(i=0; i < cg.nBytes; i++) - code->byteCode[i] = cg.byteCode[i]; + code = PTR(codeObj).code; + + // Parse the argument list, if any + p->cg->restArgSym = globals->argRef; + code->nArgs = code->nOptArgs = 0; + p->cg->argSyms = p->cg->optArgSyms = p->cg->optArgVals = 0; + code->needArgVector = 1; + if(arglist) { + p->cg->argSyms = naParseAlloc(p, sizeof(int) * MAX_FUNARGS); + p->cg->optArgSyms = naParseAlloc(p, sizeof(int) * MAX_FUNARGS); + p->cg->optArgVals = naParseAlloc(p, sizeof(int) * MAX_FUNARGS); + code->needArgVector = 0; + genArgList(p, code, arglist); + } + + code->restArgSym = internConstant(p, p->cg->restArgSym); + + /* Set the size fields and allocate the combined array buffer. + * Note cute trick with null pointer to get the array size. */ code->nConstants = naVec_size(cg.consts); - code->constants = naAlloc(code->nConstants * sizeof(naRef)); + code->codesz = cg.codesz; + code->nLines = cg.nextLineIp; code->srcFile = p->srcFile; + code->constants = 0; + code->constants = naAlloc((int)(size_t)(LINEIPS(code)+code->nLines)); for(i=0; inConstants; i++) - code->constants[i] = getConstant(p, i); + code->constants[i] = naVec_get(p->cg->consts, i); + + for(i=0; inArgs; i++) ARGSYMS(code)[i] = cg.argSyms[i]; + for(i=0; inOptArgs; i++) OPTARGSYMS(code)[i] = cg.optArgSyms[i]; + for(i=0; inOptArgs; i++) OPTARGVALS(code)[i] = cg.optArgVals[i]; + for(i=0; icodesz; i++) BYTECODE(code)[i] = cg.byteCode[i]; + for(i=0; inLines; i++) LINEIPS(code)[i] = cg.lineIps[i]; return codeObj; }