]> git.mxchange.org Git - simgear.git/blobdiff - simgear/nasal/codegen.c
Fix endianness tests, allowing arm64 support
[simgear.git] / simgear / nasal / codegen.c
index 2f2b6baa527802310fc52ab7a3511b89769e844d..495456a6cf91b72a8901b52742027983dfe96e61 100644 (file)
@@ -1,32 +1,36 @@
+#include <string.h>
 #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; i<p->cg->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)
@@ -40,99 +44,214 @@ static void genBinOp(int op, struct Parser* p, struct Token* t)
 
 static int newConstant(struct Parser* p, naRef c)
 {
-    int i = p->cg->nConsts++;
+    int i;
+    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);
-    naHash_set(p->cg->consts, naNum(i), c);
-    return i;
-}
-
-static naRef getConstant(struct Parser* p, int idx)
-{
-    naRef c;
-    naHash_get(p->cg->consts, naNum(idx), &c);
-    return c;
+   return i;
 }
 
 // Interns a scalar (!) constant and returns its index
 static int internConstant(struct Parser* p, naRef c)
 {
-    naRef r;
-    naHash_get(p->cg->interned, c, &r);
-    if(!IS_NIL(r)) {
-        return (int)r.num;
-    } else {
-        int idx = newConstant(p, c);
-        naHash_set(p->cg->interned, c, naNum(idx));
-        return idx;
+    int i, n = naVec_size(p->cg->consts);
+    if(IS_CODE(c)) return newConstant(p, c);
+    for(i=0; i<n; i++) {
+        naRef b = naVec_get(p->cg->consts, 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));
@@ -143,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_NEWARGS);
-    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;
@@ -176,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
@@ -191,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);
 }
 
 
@@ -222,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);
@@ -240,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,
@@ -255,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);
 
@@ -263,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
 }
 
@@ -275,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);
@@ -302,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));
@@ -322,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)
@@ -363,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);
@@ -376,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; i<levels; i++)
-        emit(p, OP_BREAK);
+        emit(p, (i<levels-1) ? OP_BREAK2 : OP_BREAK);
     if(t->type == 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; i<p->cg->loopTop; i++) emit(p, OP_UNMARK);
         emit(p, OP_RETURN);
         break;
     case TOK_NOT:
@@ -443,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 {
@@ -464,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);
     };
@@ -493,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);
@@ -504,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;
@@ -513,28 +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.consts = naNewHash(p->context);
-    cg.interned = naNewHash(p->context);
-    cg.nConsts = 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->nConstants = cg.nConsts;
-    code->constants = naAlloc(code->nConstants * sizeof(naRef));
+    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->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; i<code->nConstants; i++)
-        code->constants[i] = getConstant(p, i);
+        code->constants[i] = naVec_get(p->cg->consts, i);
+
+    for(i=0; i<code->nArgs; i++) ARGSYMS(code)[i] = cg.argSyms[i];
+    for(i=0; i<code->nOptArgs; i++) OPTARGSYMS(code)[i] = cg.optArgSyms[i];
+    for(i=0; i<code->nOptArgs; i++) OPTARGVALS(code)[i] = cg.optArgVals[i];
+    for(i=0; i<code->codesz; i++) BYTECODE(code)[i] = cg.byteCode[i];
+    for(i=0; i<code->nLines; i++) LINEIPS(code)[i] = cg.lineIps[i];
 
     return codeObj;
 }