diff options
| author | Ben Winston | 2023-05-20 15:12:30 -0400 |
|---|---|---|
| committer | Ben Winston | 2023-05-20 15:12:30 -0400 |
| commit | 0c70372774297272dd14133d48e40e9a3624420a (patch) | |
| tree | 4e66fb1b97a0fd537a20dca4ddcd26003f83c94d /vm.d | |
| parent | 6902cc5abe09da9f6f2d86f22d06684d97cfa9f3 (diff) | |
initial commit of compiler/VM with a couple basic instructions
Diffstat (limited to 'vm.d')
| -rw-r--r-- | vm.d | 146 |
1 files changed, 146 insertions, 0 deletions
@@ -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; + } + } + + } + +} |
