diff options
| -rw-r--r-- | chunk.d | 6 | ||||
| -rw-r--r-- | compiler.d | 94 | ||||
| -rw-r--r-- | dbg.d | 8 | ||||
| -rw-r--r-- | main.d | 39 | ||||
| -rw-r--r-- | vm.d | 198 |
5 files changed, 298 insertions, 47 deletions
@@ -19,11 +19,11 @@ class Function : Obj { string name; ObjType type; - this(ObjType type) { + this(ObjType type, string name = "") { this.type = type; this.chunk = new Chunk(); this.arity = 0; - this.name = ""; + this.name = name; } override string toString() { @@ -56,6 +56,8 @@ enum OpCode { OP_JUMP, OP_JUMP_IF_FALSE, OP_JUMP_IF_TRUE, + + OP_CALL, } class Chunk { @@ -84,19 +84,20 @@ class Compiler { func.chunk.writeOp(OpCode.OP_LESS, current.line); } - int parseVariable(Def def) { + //int parseVariable(Def def) { + int parseVariable(Symbol sym) { - declareVariable(def.name); - if (scopeDepth > 0) { + declareVariable(sym); + if (this.scopeDepth > 0) { return 0; } - int addr = func.chunk.addConstant(makeStringValue(def.name.name)); + int addr = func.chunk.addConstant(makeStringValue(sym.name)); return addr; } void defineVariable(int addr) { - if (scopeDepth > 0) { + if (this.scopeDepth > 0) { return; } func.chunk.writeOp(OpCode.OP_DEF_GLOBAL, current.line); @@ -105,13 +106,13 @@ class Compiler { void declareVariable(Symbol sym) { - if (scopeDepth == 0) { + if (this.scopeDepth == 0) { return; } for (int i = localCount - 1; i >= 0; i--) { Local local = locals[i]; - if (local.depth != -1 && local.depth < scopeDepth) { + if (local.depth != -1 && local.depth < this.scopeDepth) { break; } @@ -121,7 +122,7 @@ class Compiler { } } - Local loc = Local(sym, scopeDepth); + Local loc = Local(sym, this.scopeDepth); if (localCount == locals.length) { locals ~= loc; } else { @@ -137,10 +138,10 @@ class Compiler { resolve(def.val); // add the variable name to the chunk (if applicable) - int addr = parseVariable(def); + int addr = parseVariable(def.name); // are we setting a local? - if (scopeDepth > 0) { + if (this.scopeDepth > 0) { func.chunk.writeOp(OpCode.OP_DEF_LOCAL, current.line); } @@ -266,20 +267,74 @@ class Compiler { } void beginScope() { - scopeDepth++; + this.scopeDepth++; } void endScope() { - scopeDepth--; + this.scopeDepth--; while (localCount > 0 && - locals[localCount - 1].depth > scopeDepth) { + locals[localCount - 1].depth > this.scopeDepth) { func.chunk.writeOp(OpCode.OP_POPB, -1); localCount--; } } + void call(Form[] args) { + //ubyte argCount = argumentList(); + foreach (Form f ; args) { + resolve(f); + } + func.chunk.writeOp(OpCode.OP_CALL, -1); + //func.chunk.writeOp(argCount, -1); + func.chunk.writeOp(to!ubyte(args.length), -1); + } + + void compileFunc(Form form) { + Func f = cast(Func)form; + + // name the function + int global = parseVariable(f.name); + + Compiler compiler = new Compiler(ObjType.FUNCTION, parser, f.name.name); + compiler.beginScope(); + + if (f.args.type != FormType.NIL) { + Symbol sym = cast(Symbol)f.args.head; + int constant = compiler.parseVariable(sym); + compiler.defineVariable(constant); + + foreach (Form inner ; f.args.tail) { + sym = cast(Symbol)inner; + constant = compiler.parseVariable(sym); + compiler.defineVariable(constant); + } + } + + /* + // compile each inner Form + foreach (Form inner; f.funcBody) { + compiler.resolve(inner); + } + */ + + advance(); // ?? + + Block b = new Block(f.line); + b.blockBody = f.funcBody; + compiler.compileBlock(b); + + // write the function as a Value + Function outFunc = compiler.finish(); + func.chunk.writeOp(OpCode.OP_CONSTANT, f.line); + int funcAddr = func.chunk.addConstant(makeObjValue(outFunc)); + func.chunk.writeOp(to!ubyte(funcAddr), f.line); + + // define the global variable + defineVariable(global); + } + void compileCons(Form form) { Cons cons = cast(Cons)form; Form head = cons.head; @@ -309,6 +364,8 @@ class Compiler { advance(); */ resolve(head); + call(cons.tail); + advance(); break; } @@ -344,6 +401,9 @@ class Compiler { case FormType.OR: this.compileOr(form); break; + case FormType.FUNC: + this.compileFunc(form); + break; default: write("not sure how to resolve: "); printForm(form); @@ -363,13 +423,13 @@ class Compiler { Function finish() { this.func.chunk.writeOp(OpCode.OP_RETURN, current.line); - disassembleChunk(func.chunk, to!string(func)); - return func; + //disassembleChunk(func.chunk, to!string(func)); + return this.func; } - this(ObjType type, Parser* parser) { + this(ObjType type, Parser* parser, string name = "") { this.parser = parser; - this.func = new Function(type); + this.func = new Function(type, name); //localCount = 0; locals ~= Local(new Symbol("", -1), 0); @@ -75,12 +75,14 @@ string printableValue(Value val) { } else { return "false"; } + case ValueType.OBJ: + return printableFunction(val.as.obj); default: return "! unknown value type !"; } } -string printableFunction(Function func) { +string printableFunction(Obj func) { return format("%s", func); } @@ -147,6 +149,8 @@ int disassemble(Chunk chunk, int offset) { return constantInstruction("OP_CONSTANT", chunk, offset); case OpCode.OP_DEF_GLOBAL: return constantInstruction("OP_DEF_GLOBAL", chunk, offset); + case OpCode.OP_GET_GLOBAL: + return constantInstruction("OP_GET_GLOBAL", chunk, offset); case OpCode.OP_NEGATE: return simpleInstruction("OP_NEGATE", offset); case OpCode.OP_POP: @@ -165,6 +169,8 @@ int disassemble(Chunk chunk, int offset) { return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset); case OpCode.OP_JUMP_IF_TRUE: return jumpInstruction("OP_JUMP_IF_TRUE", 1, chunk, offset); + case OpCode.OP_CALL: + return byteInstruction("OP_CALL", chunk, offset); default: writeln("unknown opcode?"); @@ -1,10 +1,14 @@ import std.stdio; import std.string; +import std.file; +import std.conv; +/* import parser; import dbg; import chunk; import compiler; +*/ import vm; /* @@ -26,18 +30,47 @@ void repl() { continue; } + /* Parser parser = new Parser(input); Compiler compiler = new Compiler(ObjType.SCRIPT, &parser); Function func = compiler.compile(); + */ + /* VM vm = new VM(func); vm.run(); - + */ + interpret(input); } } +string readFile(string fname) { + + File f = File(fname, "r"); + + char[] ret = []; + while (!f.eof()) { + ret = ret ~ f.readln(); + } + f.close(); + return to!string(ret); +} + +int main(string[] args) { + if (args.length <= 1) { + repl(); + } else { + string fname = args[1]; + + if (!exists(fname)) { + writeln("file doesn't exist: ", fname); + return 1; + } + + string data = readFile(fname); -void main() { - repl(); + interpret(data); + } + return 0; } @@ -4,6 +4,7 @@ import std.conv; import chunk; import parser; +import compiler; //import value; //import obj; import dbg; @@ -14,20 +15,52 @@ enum InterpretResult { RUNTIME_ERROR, } +struct CallFrame { + Function func; + ubyte ip; + Value[] slots; + int frameStart; +} + class VM { //int topOffset = 0; + /* ubyte ip = 0; + */ + CallFrame[] frames; + CallFrame* current; + int frameCount; + Value[] aStack; - int aTop = 0; + int aTop; Value[] bStack; - int bTop = 0; + int bTop; + //ObjFunction func; - Function func; + //Function func; Value[string] globals; + /* this(Function func) { this.func = func; } + */ + + this() { + this.aTop = 0; + this.bTop = 0; + this.frameCount = 0; + } + + void pushFrame(CallFrame frame) { + if (this.frameCount < frames.length) { + this.frames[this.frameCount] = frame; + } else { + this.frames ~= frame; + } + current = &frames[this.frameCount]; + frameCount++; + } Value peekA(int offset) { if (offset >= aTop) { @@ -58,14 +91,28 @@ class VM { Value popB() { if (bTop > 0) { bTop--; + //current.frameStart--; } else { writeln("popB() on an empty stack!!"); } - return bStack[bTop]; + //return bStack[bTop]; + return current.slots[bTop - current.frameStart - 1]; } void pushB(Value value) { + + /* + writefln("length %d, bTop %d", current.slots.length, bTop); + if (current.slots.length > bTop) { + current.slots[bTop] = value; + } else { + current.slots ~= value; + } + bTop++; + current.frameStart++; + */ + if (bStack.length > bTop) { bStack[bTop] = value; } else { @@ -75,16 +122,15 @@ class VM { } ubyte readByte() { - return this.func.chunk.code[this.ip++]; + return current.func.chunk.code[current.ip++]; } uint readShort() { - ip += 2; - uint high = this.func.chunk.code[ip - 2] << 8; - uint low = this.func.chunk.code[ip - 1]; + current.ip += 2; + uint high = current.func.chunk.code[current.ip - 2] << 8; + uint low = current.func.chunk.code[current.ip - 1]; return high | low; - //return 0; } @@ -100,6 +146,14 @@ class VM { return value.type == ValueType.BOOLEAN; } + bool isObj(Value value) { + return value.type == ValueType.OBJ; + } + + ObjType objTypeOf(Value value) { + return value.as.obj.type; + } + double asNumber(Value value) { return value.as.number; } @@ -112,15 +166,52 @@ class VM { return value.as.boolean; } + Function asFunction(Value value) { + return cast(Function)value.as.obj; + } + /* bool isFalsey(Value val) { return isBoolean(val) && val.as.boolean; } */ + /* + CallFrame currentFrame() { + return frames[frameCount - 1]; + } + */ + + bool callValue(Value callee, int argCount) { + if (isObj(callee)) { + switch (callee.as.obj.type) { + case ObjType.FUNCTION: + return call(asFunction(callee), argCount); + case ObjType.SCRIPT: + writeln("trying to call the script?"); + break; + default: + break; + } + } else { + writeln("callee is not an object :("); + } + writeln("can only call functions!"); + return false; + } + + bool call(Function func, int argCount) { + CallFrame frame = { func, 0, bStack, bTop - argCount - 1 }; // i need to do sthg with argCount + //frames ~= frame; + //frameCount++; + pushFrame(frame); + return true; + } + InterpretResult run() { writeln("== VM running =="); while (true) { + /* writeln(" Stacks:"); write(" A> "); for (int i = 0; i < aTop; i++) { @@ -128,9 +219,11 @@ class VM { } write("\n B> "); for (int i = 0; i < bTop; i++) { - writef("[ %s ]", printableValue(bStack[i])); + //writef("[ %s ]", printableValue(bStack[i])); + writef("[ %s ]", printableValue(current.slots[i])); } writeln("\n--"); + */ /* write(" globals >"); @@ -141,11 +234,11 @@ class VM { writeln("\n--"); */ - disassemble(this.func.chunk, this.ip); + //disassemble(current.func.chunk, current.ip); ubyte inst; switch (inst = this.readByte()) { case OpCode.OP_DEF_GLOBAL: - Value name = func.chunk.constants[readByte()]; + Value name = current.func.chunk.constants[readByte()]; if (!isString(name)) { writefln("variables must be strings, got %s", printableValue(name)); return InterpretResult.RUNTIME_ERROR; // TODO error @@ -154,8 +247,8 @@ class VM { globals[asString(name)] = val; break; case OpCode.OP_GET_GLOBAL: - Value name = func.chunk.constants[readByte()]; - writefln(asString(name)); + Value name = current.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 @@ -170,18 +263,25 @@ class VM { case OpCode.OP_DEF_LOCAL: Value val = popA(); pushB(val); + //current.slots ~= val; break; case OpCode.OP_GET_LOCAL: ubyte slot = readByte(); - pushA(bStack[bTop - slot - 1]); + //pushA(bStack[bTop - slot - 1]); + //pushA(current.slots[slot - 1]); + //pushA(current.slots[current.frameStart + slot + 1]); + pushA(current.slots[current.frameStart + slot]); break; case OpCode.OP_SET_LOCAL: ubyte slot = readByte(); - bStack[bTop + slot - 1] = peekA(0); + //bStack[bTop + slot - 1] = peekA(0); + current.slots[slot] = peekA(0); + //bStack[bTop + slot - 1] = peekA(0); popA(); break; case OpCode.OP_CONSTANT: - Value constant = this.func.chunk.constants[this.readByte()]; + Value constant = current.func.chunk.constants[readByte()]; + //Value constant = current.func.chunk.constants[b]; pushA(constant); break; case OpCode.OP_NEGATE: @@ -238,9 +338,24 @@ class VM { pushA(makeBooleanValue(anum < bnum)); break; case OpCode.OP_RETURN: + Value ret = popA(); - writefln("returned %s", printableValue(ret)); - return InterpretResult.OK; + popA(); // function + this.frameCount--; + //writefln("frameCount: %d", frameCount); + //if (this.frameCount == 0) { + if (this.frameCount == 1) { + popA(); + writefln("returned %s", printableValue(ret)); + return InterpretResult.OK; + } + // do something with the stack top/frame slots?? + while(bTop > current.frameStart + 1) { + popB(); + } + pushA(ret); + current = &frames[this.frameCount -1]; + break; case OpCode.OP_POP: popA(); break; @@ -255,7 +370,8 @@ class VM { case OpCode.OP_JUMP: uint offset = readShort(); - ip += offset; + //ip += offset; + current.ip += offset; break; case OpCode.OP_JUMP_IF_FALSE: @@ -265,7 +381,8 @@ class VM { return InterpretResult.RUNTIME_ERROR; // TODO error } if (!asBoolean(peekA(0))) { - ip += offset; + //ip += offset; + current.ip += offset; } break; case OpCode.OP_JUMP_IF_TRUE: @@ -275,9 +392,30 @@ class VM { return InterpretResult.RUNTIME_ERROR; // TODO error } if (asBoolean(peekA(0))) { - ip += offset; + current.ip += offset; } break; + case OpCode.OP_CALL: + ubyte argCount = readByte(); + + // TODO i think i need to move the arguments from stack A to stack B (preserving order) + int cnt = to!int(argCount) - 1; + Value[] tmp; + while (cnt >= 0) { + tmp ~= popA(); + cnt--; + } + + foreach (Value val ; tmp) { + pushB(val); + } + + //if (!callValue(peekA(argCount), argCount)) { // i'm allocating variables wrong + if (!callValue(peekA(0), argCount)) { // i'm allocating variables wrong + return InterpretResult.RUNTIME_ERROR; + } + current = &frames[this.frameCount - 1]; + break; default: writeln("unknown opcode to run"); break; @@ -287,7 +425,19 @@ class VM { } } -/* InterpretResult interpret(string source) { Parser parser = new Parser(source); -*/ + Compiler compiler = new Compiler(ObjType.SCRIPT, &parser); + Function func = compiler.compile(); + + VM vm = new VM(); + + vm.pushA(makeObjValue(func)); + CallFrame frame = { func, 0, vm.bStack, 0 }; + vm.pushFrame(frame); + vm.call(func, 0); + + //InterpretResult result = run(); + return vm.run(); + +} |
