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[] aStack; int aTop = 0; Value[] bStack; int bTop = 0; //ObjFunction func; Function func; Value[string] globals; this(Function func) { this.func = func; } Value peekA(int offset) { if (offset >= aTop) { //if (offset > this.topOffset) { writefln("offset of %d greater than stack size %d", offset, aTop); } return this.aStack[aTop - offset - 1]; } Value popA() { if (aTop > 0) { aTop--; } else { writeln("popA() on an empty stack!!"); } return aStack[aTop]; } //InterpretResult push(Value value) { void pushA(Value value) { if (aStack.length > aTop) { aStack[aTop] = value; } else { aStack ~= value; } aTop++; } Value popB() { if (bTop > 0) { bTop--; } else { writeln("popB() on an empty stack!!"); } return bStack[bTop]; } void pushB(Value value) { if (bStack.length > bTop) { bStack[bTop] = value; } else { bStack ~= value; } bTop++; } 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(" A> "); for (int i = 0; i < aTop; i++) { writef("[ %s ]", printableValue(aStack[i])); } write("\n B> "); for (int i = 0; i < bTop; i++) { writef("[ %s ]", printableValue(bStack[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_DEF_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 = popA(); 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 } pushA(*member); // do i need to dereference? break; case OpCode.OP_DEF_LOCAL: //Value name = func.chunk.constants[readByte()]; writefln("in OP_DEF_LOCAL"); //Value val = peekA(0); Value val = popA(); pushB(val); break; case OpCode.OP_GET_LOCAL: ubyte slot = readByte(); writefln("getting local '%d' stackTop '%d'", slot, bTop); //push(stack[topOffset + slot - 2]); //push(stack[topOffset + slot]); pushA(bStack[bTop - slot - 1]); // NOTE change to bStack //push(stack[slot]); break; case OpCode.OP_SET_LOCAL: //writeln("in SET_LOCAL"); ubyte slot = readByte(); //stack[slot] = peek(0); bStack[bTop + slot - 1] = peekA(0); // NOTE change to bStack popA(); // NOTE changed to bStack //pop(); //stack[slot] = pop(); writefln("setting local '%d' stackTop '%d'", slot, bTop); break; case OpCode.OP_CONSTANT: Value constant = this.func.chunk.constants[this.readByte()]; pushA(constant); break; case OpCode.OP_NEGATE: if (!isNumber(peekA(0))) { writeln("negate operand must be a number"); return InterpretResult.RUNTIME_ERROR; } double val = asNumber(popA()); pushA(makeNumberValue(val * -1)); 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)); break; case OpCode.OP_RETURN: Value ret = popA(); writefln("returned %s", printableValue(ret)); return InterpretResult.OK; case OpCode.OP_POP: popA(); break; case OpCode.OP_POPB: popB(); break; case OpCode.OP_POP_SCOPE: // pop the n-1 position Value val = popA(); popA(); // throw this one away pushA(val); break; default: writeln("unknown opcode to run"); break; } } } } /* InterpretResult interpret(string source) { Parser parser = new Parser(source); */