diff options
| -rw-r--r-- | chunk.d | 3 | ||||
| -rw-r--r-- | compiler.d | 116 | ||||
| -rw-r--r-- | dbg.d | 13 | ||||
| -rw-r--r-- | parser.d | 94 | ||||
| -rw-r--r-- | vm.d | 66 |
5 files changed, 213 insertions, 79 deletions
@@ -48,6 +48,9 @@ enum OpCode { OP_POP_SCOPE, OP_SUBTRACT, OP_NIL, + + OP_JUMP, + OP_JUMP_IF_FALSE, } class Chunk { @@ -92,13 +92,9 @@ class Compiler { int parseVariable(Def def) { - writefln("in parseVariable(), scopeDepth is %d", scopeDepth); - declareVariable(def.name); if (scopeDepth > 0) { return 0; - //return localCount - 1; - //return localCount; } int addr = func.chunk.addConstant(makeStringValue(def.name.name)); @@ -131,7 +127,6 @@ class Compiler { } } - writefln("> > > creating a local '%s' at %d", sym.name, scopeDepth); Local loc = Local(sym, scopeDepth); if (localCount == locals.length) { locals ~= loc; @@ -139,7 +134,6 @@ class Compiler { locals[localCount] = loc; } localCount++; - writefln("localcount is now %d", localCount); } void compileDef(Form form) { @@ -153,27 +147,16 @@ class Compiler { // are we setting a local? if (scopeDepth > 0) { - //func.chunk.writeOp(OpCode.OP_SET_LOCAL, current.line); func.chunk.writeOp(OpCode.OP_DEF_LOCAL, current.line); - //func.chunk.writeOp(to!ubyte(addr), current.line); } // define the variable defineVariable(addr); - /* - func.chunk.writeOp(OpCode.OP_DEFINE_GLOBAL, current.line); - func.chunk.writeOp(to!ubyte(addr), current.line); - */ - - // is this right? - //func.chunk.writeOp(OpCode.OP_POP, def.line); } int resolveLocal(Symbol sym) { - //for (int i = localCount - 1; i >= 0; i--) { for (int i = localCount - 1; i >= 0; i--) { Local local = locals[i]; - writefln(" > > looping, looking at '%s' (%d) (depth: %d) (resolving: %s)", local.sym.name, i, local.depth, sym.name); if (local.sym.name == sym.name) { return i; } @@ -185,32 +168,80 @@ class Compiler { void compileSymbol(Form form) { Symbol sym = cast(Symbol)form; - //OpCode getOp; - //OpCode setOp; int arg = resolveLocal(sym); if (arg != -1) { - writeln("compiling a LOCAL symbol"); - //getOp = OpCode.OP_GET_LOCAL; func.chunk.writeOp(OpCode.OP_GET_LOCAL, sym.line); - //setOp = OpCode.OP_SET_LOCAL; // do we need this here } else { - writeln("compiling a GLOBAL symbol"); arg = func.chunk.addConstant(makeStringValue(sym.name)); - //getOp = OpCode.OP_GET_GLOBAL; func.chunk.writeOp(OpCode.OP_GET_GLOBAL, sym.line); - //setOp = OpCode.OP_SET_GLOBAL; // do we need this here } - // add the symbol name to the chunk - //int addr = func.chunk.addConstant(makeStringValue(sym.name)); - // get the variable - //func.chunk.writeOp(getOp, sym.line); func.chunk.writeOp(to!ubyte(arg), sym.line); //advance(); } + int jump(OpCode type) { + func.chunk.writeOp(to!ubyte(type), current.line); + func.chunk.writeOp(0xff, current.line); + func.chunk.writeOp(0xff, current.line); + return to!int(func.chunk.code.length) - 2; + } + + void patchJump(int offset) { + // additional -2 to account for the 2 byte jump itself + int jmp = to!int(func.chunk.code.length) - offset - 2; + + // TODO check to make sure we didn't jump too far? + + this.func.chunk.code[offset] = (jmp >> 8) & 0xff; + this.func.chunk.code[offset + 1] = jmp & 0xff; + } + + void compileIf(Form form) { + If if_ = cast(If)form; + + resolve(if_.cond); + + int thenJump = jump(OpCode.OP_JUMP_IF_FALSE); + this.func.chunk.writeOp(OpCode.OP_POP, if_.line); + + resolve(if_.thenForm); + + int elseJump = jump(OpCode.OP_JUMP); + + patchJump(thenJump); + this.func.chunk.writeOp(OpCode.OP_POP, if_.line); + + if (if_.hasElse) { + resolve(if_.elseForm); + } + + patchJump(elseJump); + + } + + void compileAnd(Form form) { + And and_ = cast(And)form; + + resolve(and_.clauses[0]); + int[] jumps; + jumps ~= jump(OpCode.OP_JUMP_IF_FALSE); + int count = 1; + while (count < and_.clauses.length) { + this.func.chunk.writeOp(OpCode.OP_POP, and_.line); + resolve(and_.clauses[count]); + jumps ~= jump(OpCode.OP_JUMP_IF_FALSE); + count++; + } + + // patch all the jumps + foreach (int jmp; jumps) { + patchJump(jmp); + } + } + void compileBlock(Form form) { Block block = cast(Block)form; beginScope(); @@ -226,14 +257,10 @@ class Compiler { void endScope() { - writefln("> > ending scope at depth %d", scopeDepth); scopeDepth--; while (localCount > 0 && locals[localCount - 1].depth > scopeDepth) { - writeln("> > looping, gonna pop"); - //func.chunk.writeOp(to!ubyte(OpCode.OP_POP_SCOPE), -1); - //func.chunk.writeOp(to!ubyte(OpCode.OP_POP), -1); func.chunk.writeOp(OpCode.OP_POPB, -1); localCount--; } @@ -274,8 +301,7 @@ class Compiler { } void resolve(Form form) { - write("resolving: "); - printForm(form); + //printForm(form); switch(form.type) { case FormType.ATOM: this.compileAtom(form); @@ -288,15 +314,19 @@ class Compiler { break; case FormType.DEF: this.compileDef(form); - writeln("DONE COMPILING DEF"); break; case FormType.SYMBOL: this.compileSymbol(form); break; case FormType.BLOCK: - writeln("COMPILING BLOCK"); this.compileBlock(form); break; + case FormType.IF: + this.compileIf(form); + break; + case FormType.AND: + this.compileAnd(form); + break; default: write("not sure how to resolve: "); printForm(form); @@ -306,28 +336,16 @@ class Compiler { } Function compile() { - writeln("compiling"); advance(); while(current.type != FormType.EOF) { - writeln("IN COMPILE LOOP"); resolve(current); - writeln("BOTTOM OF COMPILE LOOP"); - if (current.type == FormType.EOF) { - writeln("got an EOF"); - } } - writeln("outside compile loop"); - return finish(); } Function finish() { - //emitReturn(); - writeln("finishing the compiler!"); - this.func.chunk.writeOp(OpCode.OP_RETURN, current.line); - writeln("wrote a return code"); return func; } @@ -1,5 +1,6 @@ import std.stdio; import std.string; +import std.conv; import chunk; import parser; @@ -83,6 +84,13 @@ string atomAsString(Atom a) { return printableValue(a.value); } +int jumpInstruction(string message, int sign, Chunk chunk, int offset) { + uint jump = to!uint(chunk.code[offset + 1] << 8); + jump |= chunk.code[offset + 2]; + writefln("%-16s %4d -> %d", message, offset, offset + 3 + sign * jump); + return offset + 3; +} + int byteInstruction(string message, Chunk chunk, int offset) { ubyte slot = chunk.code[offset + 1]; writefln("%-16s %4d", message, slot); @@ -135,6 +143,11 @@ int disassemble(Chunk chunk, int offset) { return simpleInstruction("OP_RETURN", offset); case OpCode.OP_NIL: return simpleInstruction("OP_NIL", offset); + case OpCode.OP_JUMP: + return jumpInstruction("OP_JUMP", 1, chunk, offset); + case OpCode.OP_JUMP_IF_FALSE: + return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset); + default: writeln("unknown opcode?"); return offset + 1; @@ -14,6 +14,8 @@ enum FormType { FUNC, DEF, BLOCK, + IF, + AND, EOF, PARSE_ERROR @@ -134,6 +136,46 @@ class Cons : Form { } } +class If : Form { + + Form cond; + Form thenForm; + Form elseForm; + bool hasElse; + + this(int line, Form cond, Form then) { + this.line = line; + this.type = FormType.IF; + this.cond = cond; + this.thenForm = then; + this.hasElse = false; + } + + this(int line, Form cond, Form then, Form else_) { + this.line = line; + this.type = FormType.IF; + this.cond = cond; + this.thenForm = then; + this.elseForm = else_; + this.hasElse = true; + } + +} + +class And : Form { + + Form[] clauses; + + this(int line) { + this.line = line; + this.type = FormType.AND; + } + + void addClause(Form clause) { + clauses ~= clause; + } +} + class Func : Form { Symbol name; @@ -252,7 +294,6 @@ class Parser { int line; bool peekable() { - writefln("pos %d source %d", pos, source.length); return (pos < source.length); } @@ -260,14 +301,14 @@ class Parser { if (pos < source.length) { return source[pos]; } else { - writeln("peek returns null"); + //writeln("peek returns null"); return '\0'; } } char advance() { if (!peekable()) { - writeln("trying to advance() beyond the end!!!"); + //writeln("trying to advance() beyond the end!!!"); return '\0'; } else { char ret = peek(); @@ -288,7 +329,6 @@ class Parser { if (peek() == '\n') { line++; } - //pos++; advance(); } } @@ -365,8 +405,6 @@ class Parser { advance(); // closing paren - writefln("ending DEF pos %d", pos); - return def; } @@ -405,6 +443,43 @@ class Parser { return func; } + Form parseIf() { + If if_; + Form cond = parseForm(); + Form then = parseForm(); + if(peekable() && peek() == ')') { + if_ = new If(cond.line, cond, then); + } else { + Form else_ = parseForm(); + if_ = new If(cond.line, cond, then, else_); + } + + advance(); // consume closing paren + + return if_; + } + + Form parseAnd() { + And and_ = new And(line); + char next; + while(peekable()) { + next = peek(); + if (next == ')') { + break; + } + and_.addClause(parseForm()); + } + + if (!peekable()) { + return new ParseError("unterminated and", line); + } + + advance(); // consume closing paren + + return and_; + + } + Form parseBlock() { Block block = new Block(line); char next; @@ -420,7 +495,6 @@ class Parser { return new ParseError("unterminated block", line); } - writefln("ending block pos %d", pos); advance(); // consume closing paren return block; @@ -448,6 +522,10 @@ class Parser { return parseDef(); case "block": return parseBlock(); + case "if": + return parseIf(); + case "and": + return parseAnd(); default: break; } @@ -496,8 +574,6 @@ class Parser { return parseSymbol(); } - writeln("FELL THROUGH ON PARSE FORM"); - advance(); return new Atom(0, -1); @@ -31,7 +31,6 @@ class VM { Value peekA(int offset) { if (offset >= aTop) { - //if (offset > this.topOffset) { writefln("offset of %d greater than stack size %d", offset, aTop); } return this.aStack[aTop - offset - 1]; @@ -79,6 +78,16 @@ class VM { return this.func.chunk.code[this.ip++]; } + uint readShort() { + ip += 2; + uint high = this.func.chunk.code[ip - 2] << 8; + uint low = this.func.chunk.code[ip - 1]; + return high | low; + + + //return 0; + } + bool isNumber(Value value) { return value.type == ValueType.NUMBER; } @@ -87,6 +96,10 @@ class VM { return value.type == ValueType.STRING; } + bool isBoolean(Value value) { + return value.type == ValueType.BOOLEAN; + } + double asNumber(Value value) { return value.as.number; } @@ -95,9 +108,20 @@ class VM { return value.as.str; } + bool asBoolean(Value value) { + return value.as.boolean; + } + + /* + bool isFalsey(Value val) { + return isBoolean(val) && val.as.boolean; + } + */ + InterpretResult run() { - //int ip = 0; // TODO this is wrong + writeln("== VM running =="); while (true) { + writeln(" Stacks:"); write(" A> "); for (int i = 0; i < aTop; i++) { writef("[ %s ]", printableValue(aStack[i])); @@ -130,14 +154,12 @@ class VM { globals[asString(name)] = val; break; case OpCode.OP_GET_GLOBAL: - writeln("in OP_GET_GLOBAL"); Value name = func.chunk.constants[readByte()]; writefln(asString(name)); if (!isString(name)) { writefln("variables must be strings, got %s", printableValue(name)); return InterpretResult.RUNTIME_ERROR; // TODO error } - writefln(asString(name)); Value* member = asString(name) in globals; if (member is null) { writefln("no such variable: %s", printableValue(name)); @@ -146,31 +168,17 @@ class VM { pushA(*member); // do i need to dereference? break; case OpCode.OP_DEF_LOCAL: - //Value name = func.chunk.constants[readByte()]; - writefln("in OP_DEF_LOCAL"); - //Value val = peekA(0); Value val = popA(); pushB(val); break; - - case OpCode.OP_GET_LOCAL: ubyte slot = readByte(); - writefln("getting local '%d' stackTop '%d'", slot, bTop); - //push(stack[topOffset + slot - 2]); - //push(stack[topOffset + slot]); - pushA(bStack[bTop - slot - 1]); // NOTE change to bStack - //push(stack[slot]); + pushA(bStack[bTop - slot - 1]); break; case OpCode.OP_SET_LOCAL: - //writeln("in SET_LOCAL"); ubyte slot = readByte(); - //stack[slot] = peek(0); - bStack[bTop + slot - 1] = peekA(0); // NOTE change to bStack - popA(); // NOTE changed to bStack - //pop(); - //stack[slot] = pop(); - writefln("setting local '%d' stackTop '%d'", slot, bTop); + bStack[bTop + slot - 1] = peekA(0); + popA(); break; case OpCode.OP_CONSTANT: Value constant = this.func.chunk.constants[this.readByte()]; @@ -244,6 +252,22 @@ class VM { popA(); // throw this one away pushA(val); break; + + case OpCode.OP_JUMP: + uint offset = readShort(); + ip += offset; + break; + + case OpCode.OP_JUMP_IF_FALSE: + uint offset = readShort(); + if (!isBoolean(peekA(0))) { + writeln("if expects a boolean condition"); + return InterpretResult.RUNTIME_ERROR; // TODO error + } + if (!asBoolean(peekA(0))) { + ip += offset; + } + break; default: writeln("unknown opcode to run"); break; |
