import std.stdio; import std.string; import std.conv; 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; Value[string] globals; this(Function func) { this.func = func; } Value peek(int offset) { if (offset >= this.topOffset) { //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; } bool isString(Value value) { return value.type == ValueType.STRING; } double asNumber(Value value) { return value.as.number; } string asString(Value value) { return value.as.str; } InterpretResult run() { //int ip = 0; // TODO this is wrong while (true) { write(" "); 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_DEFINE_GLOBAL: Value name = func.chunk.constants[readByte()]; if (!isString(name)) { writefln("variables must be strings, got %s", printableValue(name)); return InterpretResult.RUNTIME_ERROR; // TODO error } Value val = pop(); globals[asString(name)] = val; break; case OpCode.OP_GET_GLOBAL: writeln("in OP_GET_GLOBAL"); Value name = func.chunk.constants[readByte()]; writefln(asString(name)); if (!isString(name)) { writefln("variables must be strings, got %s", printableValue(name)); return InterpretResult.RUNTIME_ERROR; // TODO error } writefln(asString(name)); Value* member = asString(name) in globals; if (member is null) { writefln("no such variable: %s", printableValue(name)); return InterpretResult.RUNTIME_ERROR; // TODO error } push(*member); // do i need to dereference? break; 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; } } } } /* InterpretResult interpret(string source) { Parser parser = new Parser(source); */