aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Winston2023-05-22 20:12:40 -0400
committerBen Winston2023-05-22 20:12:40 -0400
commitb7fba62e9f1f9f7a5a67fd64d4aed55646d1b58e (patch)
tree42c192475ed3e17dd57203cab03b41f1cbca40a9
parent229859cde2f0dae4714b972cf18e923027db265c (diff)
runtime type checking
-rw-r--r--chunk.d3
-rw-r--r--compiler.d42
-rw-r--r--dbg.d5
-rw-r--r--parser.d8
-rw-r--r--vm.d44
5 files changed, 89 insertions, 13 deletions
diff --git a/chunk.d b/chunk.d
index 96e4f76..bf67d3c 100644
--- a/chunk.d
+++ b/chunk.d
@@ -58,6 +58,9 @@ enum OpCode {
OP_JUMP_IF_TRUE,
OP_CALL,
+
+ OP_TYPE_CHECK_NUMBER,
+ OP_TYPE_CHECK_BOOLEAN,
}
class Chunk {
diff --git a/compiler.d b/compiler.d
index 0c6611c..76f6c04 100644
--- a/compiler.d
+++ b/compiler.d
@@ -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;
}
diff --git a/dbg.d b/dbg.d
index f131226..af5c2b9 100644
--- a/dbg.d
+++ b/dbg.d
@@ -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;
diff --git a/parser.d b/parser.d
index 0b8f863..e219ec7 100644
--- a/parser.d
+++ b/parser.d
@@ -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;
diff --git a/vm.d b/vm.d
index ef6efbf..5b69a23 100644
--- a/vm.d
+++ b/vm.d
@@ -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);