diff options
| -rw-r--r-- | chunk.d | 27 | ||||
| -rw-r--r-- | compiler.d | 80 | ||||
| -rw-r--r-- | vm.d | 40 |
3 files changed, 138 insertions, 9 deletions
@@ -7,6 +7,7 @@ import parser; enum ObjType { FUNCTION, SCRIPT, + LIST, } abstract class Obj { @@ -35,6 +36,26 @@ class Function : Obj { } } +class List : Obj { + int[] addresses; + + this() { + this.type = ObjType.LIST; + } + + void addItem(int addr) { + addresses ~= addr; + } + + int first() { + return addresses[0]; // TODO this fails on empty lists + } + + override string toString() { + return format("list (%d)", addresses.length); + } +} + enum OpCode { OP_ADD, OP_LESS, @@ -61,8 +82,14 @@ enum OpCode { OP_CALL, + OP_CONCAT, // No? + + OP_FIRST, // No? + OP_TYPE_CHECK_NUMBER, OP_TYPE_CHECK_BOOLEAN, + OP_TYPE_CHECK_STRING, + OP_TYPE_CHECK_LIST, } class Chunk { @@ -356,20 +356,27 @@ class Compiler { } void compileSymbol(Form form, ValueType expecting = ValueType.ANY) { + if (form.type != FormType.SYMBOL) { + writeln("NOT A SYMBOL!"); + } else { + writeln("it's a symbol"); + } Symbol sym = cast(Symbol)form; int arg = resolveLocal(sym); if (arg != -1) { - func.chunk.writeOp(OpCode.OP_GET_LOCAL, sym.line); + this.func.chunk.writeOp(OpCode.OP_GET_LOCAL, sym.line); } else { - arg = func.chunk.addConstant(makeStringValue(sym.name)); - func.chunk.writeOp(OpCode.OP_GET_GLOBAL, sym.line); + arg = this.func.chunk.addConstant(makeStringValue(sym.name)); + this.func.chunk.writeOp(OpCode.OP_GET_GLOBAL, sym.line); } + writefln("this is the addr: %d", arg); + // get the variable - func.chunk.writeOp(to!ubyte(arg), sym.line); + this.func.chunk.writeOp(to!ubyte(arg), sym.line); - //advance(); + advance(); } int jump(OpCode type) { @@ -465,6 +472,16 @@ class Compiler { return vt; } + ValueType compileConcat(Form[] args) { + ValueType vt = resolve(args[0], ValueType.STRING); + for (int i = 1; i < args.length; i++) { + vt = resolve(args[i], ValueType.STRING); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_STRING, currentLine); + func.chunk.writeOp(OpCode.OP_CONCAT, currentLine); + } + return ValueType.STRING; + } + void compileBlock(Form form) { Block block = cast(Block)form; beginScope(); @@ -499,6 +516,44 @@ class Compiler { func.chunk.writeOp(to!ubyte(args.length), -1); } + void compileList(Form[] args) { + List lst = new List(); + ValueType vt; + int addr; + foreach (Form arg ; args) { + vt = resolve(arg, ValueType.ANY); + // how do we get at the address? + addr = to!int(this.func.chunk.constants.length) - 1; // this is probably often wrong + lst.addItem(addr); + this.func.chunk.writeOp(OpCode.OP_POP, arg.line); // shouldn't use the stack for this (but how?) + } + + this.func.chunk.writeOp(OpCode.OP_CONSTANT, args[0].line); + int idx = this.func.chunk.addConstant(makeObjValue(lst)); + this.func.chunk.writeOp(to!ubyte(idx), args[0].line); + //return ValueType.OBJ; + + + //advance(); // ?? + } + + void compileFirst(Form[] args) { + // TODO how do we identify/propagate errors? + if (args.length != 1) { + writeln("COMPILE ERROR: 'first' expects exactly one argument"); + return; + } + ValueType vt = resolve(args[0], ValueType.OBJ); // TODO need a new type + this.func.chunk.writeOp(OpCode.OP_TYPE_CHECK_LIST, args[0].line); + + // pop the value off the stack + // get the address of the first item in the list + // push that item onto the stack + + // or, use a special instruction + this.func.chunk.writeOp(OpCode.OP_FIRST, args[0].line); + } + void compileFunc(Form form) { Func f = cast(Func)form; @@ -580,6 +635,19 @@ class Compiler { return vt; case "not": return compileNot(cons.tail); + + // STRINGS + case "concat": + return compileConcat(cons.tail); + + // LISTS + case "first": + compileFirst(cons.tail); + break; + case "list": + //return compileList(cons.tail); + compileList(cons.tail); + break; default: /* writefln("unsure how to compile %s", sym.name); @@ -648,7 +716,7 @@ class Compiler { Function finish() { this.func.chunk.writeOp(OpCode.OP_RETURN, current.line); - disassembleChunk(func.chunk, to!string(func)); + disassembleChunk(this.func.chunk, to!string(func)); return this.func; } @@ -155,6 +155,10 @@ class VM { return value.type == ValueType.OBJ; } + bool isList(Value value) { + return isObj(value) && objTypeOf(value) == ObjType.LIST; + } + ObjType objTypeOf(Value value) { return value.as.obj.type; } @@ -220,7 +224,6 @@ class VM { InterpretResult run() { writeln("== VM running =="); while (true) { - /* writeln(" Stacks:"); write(" A> "); for (int i = 0; i < aTop; i++) { @@ -231,8 +234,12 @@ class VM { //writef("[ %s ]", printableValue(bStack[i])); writef("[ %s ]", printableValue(current.slots[i])); } + + write("\n constants> "); + for (int i = 0; i < current.func.chunk.constants.length; i++) { + writef("[ %s ]", printableValue(current.func.chunk.constants[i])); + } writeln("\n--"); - */ /* write(" globals >"); @@ -243,7 +250,7 @@ class VM { writeln("\n--"); */ - //disassemble(current.func.chunk, current.ip); + disassemble(current.func.chunk, current.ip); ubyte inst; switch (inst = this.readByte()) { case OpCode.OP_DEF_GLOBAL: @@ -301,6 +308,18 @@ class VM { double val = asNumber(popA()); pushA(makeNumberValue(val * -1)); break; + case OpCode.OP_TYPE_CHECK_LIST: + if (!isList(peekA(0))) { + writeln("VM type check: not a list!"); + return InterpretResult.RUNTIME_ERROR; // TODO error + } + break; + case OpCode.OP_TYPE_CHECK_STRING: + if (!isString(peekA(0))) { + writeln("VM type check: not a string!"); + return InterpretResult.RUNTIME_ERROR; // TODO error + } + break; case OpCode.OP_TYPE_CHECK_NUMBER: if (!isNumber(peekA(0))) { writeln("VM type check: not a number!"); @@ -313,6 +332,14 @@ class VM { return InterpretResult.RUNTIME_ERROR; // TODO error } break; + case OpCode.OP_FIRST: + Value val = popA(); + List lst = cast(List)val.as.obj; // TODO this needs better checking + int addr = lst.first(); + writefln("got this address: %d", addr); + Value first = current.func.chunk.constants[to!ubyte(addr)]; + pushA(first); + break; case OpCode.OP_ADD: Value b = popA(); /* @@ -356,6 +383,13 @@ class VM { bool bval = asBoolean(val); pushA(makeBooleanValue(!bval)); break; + case OpCode.OP_CONCAT: + Value b = popA(); + Value a = popA(); + string bstr = asString(b); + string astr = asString(a); + pushA(makeStringValue(astr ~ bstr)); + break; case OpCode.OP_GREATER: Value b = popA(); Value a = popA(); |
