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--"); /* write(" globals >"); //for (int i = 0; i < this.topOffset; i++) { foreach (Value val; globals) { writef("[ %s ]", printableValue(val)); } 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_LOCAL: ubyte slot = readByte(); writefln("getting local '%d' stackTop '%d'", slot, topOffset); //push(stack[topOffset + slot - 2]); //push(stack[topOffset + slot]); push(stack[topOffset - 2 + slot]); //push(stack[slot]); break; case OpCode.OP_SET_LOCAL: //writeln("in SET_LOCAL"); ubyte slot = readByte(); //stack[slot] = peek(0); stack[topOffset + slot - 1] = peek(0); //pop(); //stack[slot] = pop(); writefln("setting local '%d' stackTop '%d'", slot, topOffset); 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; case OpCode.OP_POP: pop(); break; case OpCode.OP_POP_SCOPE: // pop the n-1 position Value val = pop(); pop(); // throw this one away push(val); break; default: writeln("unknown opcode to run"); break; } } } } /* InterpretResult interpret(string source) { Parser parser = new Parser(source); */