aboutsummaryrefslogtreecommitdiff
path: root/vm.d
diff options
context:
space:
mode:
authorBen Winston2023-05-20 15:12:30 -0400
committerBen Winston2023-05-20 15:12:30 -0400
commit0c70372774297272dd14133d48e40e9a3624420a (patch)
tree4e66fb1b97a0fd537a20dca4ddcd26003f83c94d /vm.d
parent6902cc5abe09da9f6f2d86f22d06684d97cfa9f3 (diff)
initial commit of compiler/VM with a couple basic instructions
Diffstat (limited to 'vm.d')
-rw-r--r--vm.d146
1 files changed, 146 insertions, 0 deletions
diff --git a/vm.d b/vm.d
new file mode 100644
index 0000000..6011928
--- /dev/null
+++ b/vm.d
@@ -0,0 +1,146 @@
+import std.stdio;
+import std.string;
+
+import chunk;
+import parser;
+//import value;
+//import obj;
+import dbg;
+
+enum InterpretResult {
+ OK,
+ COMPILE_ERROR,
+ RUNTIME_ERROR,
+}
+
+class VM {
+ int topOffset = 0;
+ ubyte ip = 0;
+ Value[] stack;
+ //ObjFunction func;
+ Function func;
+
+ this(Function func) {
+ this.func = func;
+ }
+
+ Value peek(int offset) {
+ if (offset >= this.topOffset) {
+ writefln("offset of %d greater than stack size %d", offset, topOffset);
+ }
+ return this.stack[topOffset - offset - 1];
+ }
+
+ Value pop() {
+ if (this.topOffset > 0) {
+ this.topOffset--;
+ } else {
+ writeln("pop() on an empty stack!!");
+ }
+ return this.stack[this.topOffset];
+ }
+
+ //InterpretResult push(Value value) {
+ void push(Value value) {
+ if (this.stack.length > this.topOffset) {
+ this.stack[topOffset] = value;
+ } else {
+ this.stack ~= value;
+ }
+ this.topOffset++;
+ }
+
+ ubyte readByte() {
+ return this.func.chunk.code[this.ip++];
+ }
+
+ bool isNumber(Value value) {
+ return value.type == ValueType.NUMBER;
+ }
+
+ double asNumber(Value value) {
+ return value.as.number;
+ }
+
+ InterpretResult run() {
+ //int ip = 0; // TODO this is wrong
+ while (true) {
+ write(" ");
+ //foreach (Value val; this.stack) {
+ for (int i = 0; i < this.topOffset; i++) {
+ writef("[ %s ]", printableValue(this.stack[i]));
+ }
+ writeln("\n--");
+ disassemble(this.func.chunk, this.ip);
+ ubyte inst;
+ switch (inst = this.readByte()) {
+ case OpCode.OP_CONSTANT:
+ Value constant = this.func.chunk.constants[this.readByte()];
+ this.push(constant);
+ break;
+ case OpCode.OP_NEGATE:
+ if (!isNumber(peek(0))) {
+ writeln("negate operand must be a number");
+ return InterpretResult.RUNTIME_ERROR;
+ }
+ double val = asNumber(pop());
+ push(makeNumberValue(val * -1));
+ break;
+ case OpCode.OP_ADD:
+ Value b = this.pop();
+ if (!isNumber(b)) {
+ writeln("b is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ Value a = this.pop();
+ if (!isNumber(a)) {
+ writeln("a is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ double bnum = asNumber(b);
+ double anum = asNumber(a);
+ push(makeNumberValue(anum + bnum));
+ break;
+ case OpCode.OP_SUBTRACT:
+ Value b = this.pop();
+ if (!isNumber(b)) {
+ writeln("b is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ Value a = this.pop();
+ if (!isNumber(a)) {
+ writeln("a is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ double bnum = asNumber(b);
+ double anum = asNumber(a);
+ push(makeNumberValue(anum - bnum));
+ break;
+ case OpCode.OP_LESS:
+ Value b = this.pop();
+ if (!isNumber(b)) {
+ writeln("b is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ Value a = this.pop();
+ if (!isNumber(a)) {
+ writeln("a is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ double bnum = asNumber(b);
+ double anum = asNumber(a);
+ push(makeBooleanValue(anum < bnum));
+ break;
+ case OpCode.OP_RETURN:
+ Value ret = this.pop();
+ writefln("returned %s", printableValue(ret));
+ return InterpretResult.OK;
+ default:
+ writeln("unknown opcode to run");
+ break;
+ }
+ }
+
+ }
+
+}