From 9fd0d073d8362386ec48740776d4453282db801a Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Mon, 30 May 2016 00:11:53 +0300 Subject: [PATCH] `switch` codegen --- gmlparser/ast.d | 10 +++---- gmlparser/parser.d | 19 +++++++++----- gmlvm.d | 77 +++++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 77 insertions(+), 29 deletions(-) diff --git a/gmlparser/ast.d b/gmlparser/ast.d index 207a742..8d1f281 100644 --- a/gmlparser/ast.d +++ b/gmlparser/ast.d @@ -500,6 +500,7 @@ class NodeRepeat : NodeStatement { // `switch` operator class NodeSwitch : NodeStatement { static struct Case { + Loc loc; Node e; // condition; `null` means "default" NodeBlock st; // can be `null` } @@ -509,13 +510,8 @@ class NodeSwitch : NodeStatement { this () {} this (Loc aloc) { super(aloc); } - void appendCase (Node ae, NodeBlock ast) { - if (ae is null) { - foreach (ref cc; cases) { - if (cc.e is null) throw new ErrorAt(loc, "duplicate `default`"); - } - } - cases ~= Case(ae, ast); + void appendCase (Loc aloc, Node ae, NodeBlock ast) { + cases ~= Case(aloc, ae, ast); } override string toStringInd (int indent) const { diff --git a/gmlparser/parser.d b/gmlparser/parser.d index f2b7fab..bd92232 100644 --- a/gmlparser/parser.d +++ b/gmlparser/parser.d @@ -208,10 +208,10 @@ final class Parser { mixin BuildExprBinOp!("Mul", "Unary", "Mul", "Div", "RDiv", "Mod"); mixin BuildExprBinOp!("Add", "Mul", "Add", "Sub"); // binop `~` is here too, but we don't have it mixin BuildExprBinOp!("Shift", "Add", "LShift", "RShift"); - mixin BuildExprBinOp!("Cmp", "Shift", "Less", "Great", "Equ", "NotEqu", "LessEqu", "GreatEqu", "Ass"); // `a is b`, `a in b` are here too - mixin BuildExprBinOp!("BitAnd", "Cmp", "BitAnd"); + mixin BuildExprBinOp!("BitAnd", "Shift", "BitAnd"); mixin BuildExprBinOp!("BitOr", "BitAnd", "BitOr", "BitXor"); - mixin BuildExprBinOp!("LogAnd", "BitOr", "LogAnd", "And"); + mixin BuildExprBinOp!("Cmp", "BitOr", "Less", "Great", "Equ", "NotEqu", "LessEqu", "GreatEqu", "Ass"); // `a is b`, `a in b` are here too + mixin BuildExprBinOp!("LogAnd", "Cmp", "LogAnd", "And"); mixin BuildExprBinOp!("LogOr", "LogAnd", "LogOr", "LogXor", "Or", "Xor"); Node parseExpr () { @@ -410,9 +410,14 @@ final class Parser { lex.expect(Keyword.LCurly); // parse case nodes; i won't support insane things like Duff's device here while (lex != Keyword.RCurly) { + auto cl = lex.loc; Node e; - if (lex.eatKw(Keyword.Default)) { - // do nothing here + if (lex.isKw(Keyword.Default)) { + // check for duplicate `default` + foreach (ref cc; sw.cases) { + if (cc.e is null) error(lex.loc, "duplicate `default`; previous was at "~cc.loc.toStringNoFile); + } + lex.popFront(); // eat it } else if (lex.eatKw(Keyword.Case)) { e = parseExpr(); } else { @@ -436,9 +441,9 @@ final class Parser { while (blk.stats.length == 1) { if (auto b = cast(NodeBlock)blk.stats[0]) blk = b; else break; } - sw.appendCase(e, blk); + sw.appendCase(cl, e, blk); } else { - sw.appendCase(e, null); + sw.appendCase(cl, e, null); } } lex.expect(Keyword.RCurly); diff --git a/gmlvm.d b/gmlvm.d index 008ec17..1a0e177 100644 --- a/gmlvm.d +++ b/gmlvm.d @@ -379,7 +379,7 @@ private: return res; } - uint emitJumpTo (Op op, uint addr) { + uint emitJumpTo (uint addr, Op op=Op.jump) { assert(addr <= 0xffffff); auto res = cast(uint)code.length; code ~= cast(uint)op|(addr<<8); @@ -921,7 +921,6 @@ private: uint breakChain; // current jump chain for `break` uint contChain; // current jump chain for `continue` bool contChainIsAddr; // is `contChain` an address, not a chain? - bool inSwitch; // are we in `switch` now? void compile (Node nn) { assert(nn !is null); @@ -970,7 +969,7 @@ private: }, (NodeStatementContinue n) { if (contChainIsAddr) { - emitJumpTo(Op.jump, contChain); + emitJumpTo(contChain); } else { contChain = emitJumpChain(contChain); } @@ -1002,7 +1001,7 @@ private: auto dest = compileExpr(n.econd); freeSlot(dest); // yep, right here emit(Op.xfalse, dest); // skip jump on false - emitJumpTo(Op.jump, stpc); + emitJumpTo(stpc); // "break" is here fixJumpChain(breakChain, pc); }, @@ -1025,7 +1024,7 @@ private: // body compile(n.ebody); // and again - emitJumpTo(Op.jump, contChain); + emitJumpTo(contChain); // "break" is here fixJumpChain(breakChain, pc); }, @@ -1050,7 +1049,7 @@ private: freeSlot(dest); // yep, right here emit(Op.xfalse, dest); // skip jump on false // and again - emitJumpTo(Op.jump, stpc); + emitJumpTo(stpc); // "break" is here fixJumpChain(breakChain, pc); }, @@ -1081,7 +1080,7 @@ private: // body compile(n.ebody); // and again - emitJumpTo(Op.jump, contChain); + emitJumpTo(contChain); // "break" is here fixJumpChain(breakChain, pc); // free used slots @@ -1089,15 +1088,63 @@ private: freeSlot(cnt); }, (NodeSwitch n) { - /* - if (auto r = visitNodes(n.e, dg)) return r; - foreach (ref ci; n.cases) { - if (auto r = visitNodes(ci.e, dg)) return r; - if (auto r = visitNodes(ci.st, dg)) return r; + // switch expression + auto expr = compileExpr(n.e); + if (n.cases.length) { + // has some cases + uint defaultBodyAddr = 0; // address of "default" node body (even if it is empty) + uint lastFalltrhuJump = 0; // this is the address of the Op.jump at the end of the previous case node + uint lastCaseSkipJumpAddr = 0; // this is the address of the Op.jump at the failed condition of the previous case node + // new "break" chain + auto obc = breakChain; + scope(exit) breakChain = obc; + breakChain = 0; + // now generate code for case nodes, skipping "default" by the way + foreach (immutable idx, ref ci; n.cases) { + uint nodeSkipChain = 0; + // check condition + if (ci.e !is null) { + // jump here from the last failed condition + fixJumpChain(lastCaseSkipJumpAddr, pc); + auto cond = compileExpr(ci.e); + // trick: reuse "cond" slot + freeSlot(cond); + emit(Op.eq, cond, cond, expr); + emit(Op.xtrue, cond); + // new skip chain + lastCaseSkipJumpAddr = emitJumpChain(0); + } else { + // this is default node, jump over it + nodeSkipChain = emitJumpChain(0); + // and save info + defaultBodyAddr = pc; + } + // fix fallthru jump + fixJumpChain(lastFalltrhuJump, pc); + // the body is here + compile(ci.st); + // new fallthru chain + lastFalltrhuJump = (idx < n.cases.length-1 ? emitJumpChain(0) : 0); + // fix "default skip" chain + fixJumpChain(nodeSkipChain, pc); + } + // we can free expression slot right here + freeSlot(expr); + // do we have default node? + if (defaultBodyAddr) { + // jump there from the last failed condition + fixJumpChain(lastCaseSkipJumpAddr, defaultBodyAddr); + } else { + // jump here from the last failed condition + fixJumpChain(lastCaseSkipJumpAddr, pc); + } + // fix last fallthru jump + fixJumpChain(lastFalltrhuJump, pc); + // fix "break" chain + fixJumpChain(breakChain, pc); + } else { + freeSlot(expr); } - return null; - */ - assert(0); }, () { assert(0, "unimplemented node: "~typeid(nn).name); }, ); -- 2.11.4.GIT