diff options
| author | Ben Winston | 2023-05-22 20:12:40 -0400 |
|---|---|---|
| committer | Ben Winston | 2023-05-22 20:12:40 -0400 |
| commit | b7fba62e9f1f9f7a5a67fd64d4aed55646d1b58e (patch) | |
| tree | 42c192475ed3e17dd57203cab03b41f1cbca40a9 | |
| parent | 229859cde2f0dae4714b972cf18e923027db265c (diff) | |
runtime type checking
| -rw-r--r-- | chunk.d | 3 | ||||
| -rw-r--r-- | compiler.d | 42 | ||||
| -rw-r--r-- | dbg.d | 5 | ||||
| -rw-r--r-- | parser.d | 8 | ||||
| -rw-r--r-- | vm.d | 44 |
5 files changed, 89 insertions, 13 deletions
@@ -58,6 +58,9 @@ enum OpCode { OP_JUMP_IF_TRUE, OP_CALL, + + OP_TYPE_CHECK_NUMBER, + OP_TYPE_CHECK_BOOLEAN, } class Chunk { @@ -22,6 +22,8 @@ class Compiler { Form current; Form previous; + int currentLine; + Form outer; int outerIdx; @@ -48,7 +50,12 @@ class Compiler { } void compileAdd(Form[] args) { + writeln("compiling add"); + int line = args[0].line; resolve(args[0]); + writeln("resolved the first argument"); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, line); + writeln("wrote the typecheck op"); // (+ n) always returns n if (args.length == 1) { @@ -57,20 +64,24 @@ class Compiler { for (int i = 1; i < args.length; i++) { resolve(args[i]); - func.chunk.writeOp(OpCode.OP_ADD, current.line); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, line); + func.chunk.writeOp(OpCode.OP_ADD, line); } } void compileNegate(Form arg) { resolve(arg); - func.chunk.writeOp(OpCode.OP_NEGATE, current.line); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); + func.chunk.writeOp(OpCode.OP_NEGATE, currentLine); } void compileSubtract(Form[] args) { resolve(args[0]); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); for (int i = 1; i < args.length; i++) { resolve(args[i]); - func.chunk.writeOp(OpCode.OP_SUBTRACT, current.line); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); + func.chunk.writeOp(OpCode.OP_SUBTRACT, currentLine); } } @@ -80,8 +91,10 @@ class Compiler { return; } resolve(args[0]); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); resolve(args[1]); - func.chunk.writeOp(OpCode.OP_LESS, current.line); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); + func.chunk.writeOp(OpCode.OP_LESS, currentLine); } //int parseVariable(Def def) { @@ -142,7 +155,7 @@ class Compiler { // are we setting a local? if (this.scopeDepth > 0) { - func.chunk.writeOp(OpCode.OP_DEF_LOCAL, current.line); + func.chunk.writeOp(OpCode.OP_DEF_LOCAL, def.line); } // define the variable @@ -195,9 +208,11 @@ class Compiler { } void compileIf(Form form) { + writeln("compiling if..."); If if_ = cast(If)form; resolve(if_.cond); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, if_.line); int thenJump = jump(OpCode.OP_JUMP_IF_FALSE); this.func.chunk.writeOp(OpCode.OP_POP, if_.line); @@ -221,12 +236,15 @@ class Compiler { And and_ = cast(And)form; resolve(and_.clauses[0]); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); + 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); + this.func.chunk.writeOp(OpCode.OP_POP, currentLine); resolve(and_.clauses[count]); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); jumps ~= jump(OpCode.OP_JUMP_IF_FALSE); count++; } @@ -241,12 +259,14 @@ class Compiler { Or or_ = cast(Or)form; resolve(or_.clauses[0]); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); int[] jumps; jumps ~= jump(OpCode.OP_JUMP_IF_TRUE); int count = 1; while (count < or_.clauses.length) { this.func.chunk.writeOp(OpCode.OP_POP, or_.line); resolve(or_.clauses[count]); + func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); jumps ~= jump(OpCode.OP_JUMP_IF_TRUE); count++; } @@ -261,7 +281,9 @@ class Compiler { Block block = cast(Block)form; beginScope(); foreach (Form inner; block.blockBody) { + writeln("about to compile an inner form"); resolve(inner); + writeln("finished compiling inner form"); } endScope(); } @@ -292,6 +314,7 @@ class Compiler { } void compileFunc(Form form) { + writeln("compiling func"); Func f = cast(Func)form; // name the function @@ -312,6 +335,8 @@ class Compiler { } } + writeln("got the arguments"); + /* // compile each inner Form foreach (Form inner; f.funcBody) { @@ -325,6 +350,8 @@ class Compiler { b.blockBody = f.funcBody; compiler.compileBlock(b); + writeln("compiled the body"); + // write the function as a Value Function outFunc = compiler.finish(); func.chunk.writeOp(OpCode.OP_CONSTANT, f.line); @@ -373,6 +400,7 @@ class Compiler { void resolve(Form form) { //printForm(form); + currentLine = form.line; switch(form.type) { case FormType.ATOM: this.compileAtom(form); @@ -423,7 +451,7 @@ class Compiler { Function finish() { this.func.chunk.writeOp(OpCode.OP_RETURN, current.line); - //disassembleChunk(func.chunk, to!string(func)); + disassembleChunk(func.chunk, to!string(func)); return this.func; } @@ -77,6 +77,8 @@ string printableValue(Value val) { } case ValueType.OBJ: return printableFunction(val.as.obj); + case ValueType.TYPE: + return val.as.type; default: return "! unknown value type !"; } @@ -171,7 +173,8 @@ int disassemble(Chunk chunk, int offset) { return jumpInstruction("OP_JUMP_IF_TRUE", 1, chunk, offset); case OpCode.OP_CALL: return byteInstruction("OP_CALL", chunk, offset); - + case OpCode.OP_TYPE_CHECK_NUMBER: + return simpleInstruction("OP_TYPE_CHECK_NUMBER", offset); default: writeln("unknown opcode?"); return offset + 1; @@ -262,6 +262,7 @@ enum ValueType { STRING, NUMBER, BOOLEAN, + TYPE, OBJ, } @@ -269,6 +270,7 @@ union As { bool boolean; double number; string str; + string type; Obj obj; } @@ -302,6 +304,12 @@ Value makeObjValue(Obj obj) { return val; } +Value makeTypeValue(string name) { + As as = { type: name }; + Value val = { ValueType.TYPE, as }; + return val; +} + class Parser { string source; @@ -139,7 +139,12 @@ class VM { } bool isString(Value value) { - return value.type == ValueType.STRING; + return value.type == ValueType.STRING || + value.type == ValueType.TYPE; + } + + bool isType(Value value) { + return value.type == ValueType.TYPE; } bool isBoolean(Value value) { @@ -159,7 +164,11 @@ class VM { } string asString(Value value) { - return value.as.str; + if (value.type == ValueType.TYPE) { + return value.as.type; + } else { + return value.as.str; + } } bool asBoolean(Value value) { @@ -292,47 +301,71 @@ class VM { double val = asNumber(popA()); pushA(makeNumberValue(val * -1)); break; + case OpCode.OP_TYPE_CHECK_NUMBER: + if (!isNumber(peekA(0))) { + writeln("VM type check: not a number!"); + return InterpretResult.RUNTIME_ERROR; // TODO error + } + break; + case OpCode.OP_TYPE_CHECK_BOOLEAN: + if (!isBoolean(peekA(0))) { + writeln("VM type check: not a boolean!"); + return InterpretResult.RUNTIME_ERROR; // TODO error + } + break; case OpCode.OP_ADD: Value b = popA(); + /* if (!isNumber(b)) { writeln("b is not a number!"); return InterpretResult.RUNTIME_ERROR; // TODO error } + */ Value a = popA(); + /* if (!isNumber(a)) { writeln("a is not a number!"); return InterpretResult.RUNTIME_ERROR; // TODO error } + */ double bnum = asNumber(b); double anum = asNumber(a); pushA(makeNumberValue(anum + bnum)); break; case OpCode.OP_SUBTRACT: Value b = popA(); + /* if (!isNumber(b)) { writeln("b is not a number!"); return InterpretResult.RUNTIME_ERROR; // TODO error } + */ Value a = popA(); + /* if (!isNumber(a)) { writeln("a is not a number!"); return InterpretResult.RUNTIME_ERROR; // TODO error } + */ double bnum = asNumber(b); double anum = asNumber(a); pushA(makeNumberValue(anum - bnum)); break; case OpCode.OP_LESS: Value b = popA(); + /* if (!isNumber(b)) { writeln("b is not a number!"); return InterpretResult.RUNTIME_ERROR; // TODO error } + */ Value a = popA(); + /* if (!isNumber(a)) { writeln("a is not a number!"); return InterpretResult.RUNTIME_ERROR; // TODO error } + */ double bnum = asNumber(b); double anum = asNumber(a); pushA(makeBooleanValue(anum < bnum)); @@ -342,10 +375,7 @@ class VM { Value ret = popA(); 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; } @@ -387,10 +417,12 @@ class VM { break; case OpCode.OP_JUMP_IF_TRUE: uint offset = readShort(); + /* if (!isBoolean(peekA(0))) { writeln("expecting a boolean condition"); return InterpretResult.RUNTIME_ERROR; // TODO error } + */ if (asBoolean(peekA(0))) { current.ip += offset; } @@ -432,6 +464,8 @@ InterpretResult interpret(string source) { VM vm = new VM(); +// vm.globals[":int"] = makeTypeValue(":int"); + vm.pushA(makeObjValue(func)); CallFrame frame = { func, 0, vm.bStack, 0 }; vm.pushFrame(frame); |
