Hash Tables 20
This commit is contained in:
parent
3b234814fa
commit
a7b7348f61
20 changed files with 868 additions and 837 deletions
178
src/clox/vm.d
178
src/clox/vm.d
|
|
@ -1,135 +1,137 @@
|
|||
module clox.vm;
|
||||
|
||||
import std.stdio;
|
||||
import core.stdc.stdio;
|
||||
|
||||
import clox.chunk;
|
||||
import clox.value;
|
||||
import clox.dbg;
|
||||
import clox.util;
|
||||
import clox.compiler;
|
||||
import clox.object;
|
||||
import clox.value;
|
||||
import clox.object;
|
||||
import clox.compiler;
|
||||
import clox.memory;
|
||||
import clox.container.stack;
|
||||
import clox.container.varint;
|
||||
import clox.container.int24;
|
||||
import clox.container.table;
|
||||
|
||||
enum stackMax = 256;
|
||||
VM vm;
|
||||
|
||||
struct VM{
|
||||
const(ubyte)* ip;
|
||||
Stack!(Value, stackMax) stack;
|
||||
Chunk* chunk;
|
||||
ubyte* ip;
|
||||
Stack!(Value, 256) stack;
|
||||
Table strings;
|
||||
Obj* objects;
|
||||
enum InterpretResult{ Ok, CompileError, RuntimeError }
|
||||
this(int _) @nogc nothrow {
|
||||
stack = typeof(stack)(0);
|
||||
void initialise(){
|
||||
stack.reset();
|
||||
strings.initialise();
|
||||
}
|
||||
~this(){
|
||||
void free(){
|
||||
freeObjects();
|
||||
strings.free();
|
||||
}
|
||||
InterpretResult interpret(string source){
|
||||
Chunk c = Chunk();
|
||||
InterpretResult interpret(const char* source){
|
||||
Chunk cnk;
|
||||
cnk.initialise();
|
||||
scope(exit)
|
||||
cnk.free();
|
||||
Compiler compiler;
|
||||
if(!compiler.compile(source, &c))
|
||||
if(!compiler.compile(source, &cnk))
|
||||
return InterpretResult.CompileError;
|
||||
chunk = &c;
|
||||
return interpret(chunk);
|
||||
}
|
||||
InterpretResult interpret(Chunk* chunk){
|
||||
this.chunk = chunk;
|
||||
ip = &chunk.code[0];
|
||||
this.chunk = &cnk;
|
||||
this.ip = cnk.code;
|
||||
return run();
|
||||
}
|
||||
private void runtimeError(Args...)(string format, Args args) nothrow {
|
||||
size_t instruction = ip - (&chunk.code[0]) - 1;
|
||||
uint line = chunk.lines[instruction].toUint;
|
||||
try{
|
||||
stderr.writef("[line %d] ", line);
|
||||
stderr.writefln(format, args);
|
||||
} catch(Exception){}
|
||||
/* stack.reset(); */
|
||||
void runtimeError(Args...)(const char* format, Args args){
|
||||
fprintf(stderr, format, args);
|
||||
fputs("\n", stderr);
|
||||
|
||||
size_t instruction = vm.ip - vm.chunk.code - 1;
|
||||
int line = vm.chunk.lines[instruction];
|
||||
fprintf(stderr, "[line %d] in script\n", line);
|
||||
stack.reset();
|
||||
}
|
||||
private InterpretResult run() nothrow {
|
||||
auto readByte() => *ip++;
|
||||
auto readIns() => cast(OpCode)readByte();
|
||||
Value readConstant(){
|
||||
VarUint constant = VarUint.read(ip[0 .. 4]);
|
||||
ip += constant.len;
|
||||
return chunk.constants[constant.i];
|
||||
}
|
||||
Value peek(int distance = 0){
|
||||
return stack.top[-1 - distance];
|
||||
InterpretResult run(){
|
||||
ubyte readByte() => *vm.ip++;
|
||||
auto readConstant() => chunk.constants.values[readByte()];
|
||||
Value peek(int distance) => stack.top[-1 - distance];
|
||||
bool checkBinaryType(alias type)() => peek(0).isType(type) && peek(1).isType(type);
|
||||
bool checkSameType()() => peek(0).type == peek(1).type;
|
||||
int binaryOp(string op, alias check, string checkMsg, alias pre)(){
|
||||
if(!check){
|
||||
runtimeError(checkMsg.ptr);
|
||||
return 1;
|
||||
}
|
||||
auto b = stack.pop().as!pre;
|
||||
auto a = stack.pop().as!pre;
|
||||
stack.push(Value.from(mixin("a", op, "b")));
|
||||
return 0;
|
||||
}
|
||||
while(true){
|
||||
debug(traceExec){
|
||||
stderr.writeln(" ", stack.live);
|
||||
disassembleInstruction(chunk, ip - &chunk.code[0]);
|
||||
printf(" ");
|
||||
foreach(slot; stack.live){
|
||||
printf("[ ");
|
||||
slot.print();
|
||||
printf(" ]");
|
||||
}
|
||||
printf("\n");
|
||||
disassembleInstruction(vm.chunk, cast(int)(vm.ip - vm.chunk.code));
|
||||
}
|
||||
OpCode instruction = readIns();
|
||||
with(OpCode) opSwitch: final switch(instruction){
|
||||
case Constant:
|
||||
OpCode instruction;
|
||||
opSwitch: final switch(instruction = cast(OpCode)readByte()){
|
||||
case OpCode.Constant:
|
||||
Value constant = readConstant();
|
||||
stack.push(constant);
|
||||
break;
|
||||
case True:
|
||||
stack.push(Value.bln(true));
|
||||
break;
|
||||
case False:
|
||||
stack.push(Value.bln(false));
|
||||
break;
|
||||
case Nil:
|
||||
stack.push(Value.nil());
|
||||
break;
|
||||
static foreach(k, op; [ Add: "+", Subtract: "-", Multiply: "*", Divide: "/" ]){
|
||||
case OpCode.Nil: stack.push(Value.nil); break;
|
||||
case OpCode.True: stack.push(Value.from(true)); break;
|
||||
case OpCode.False: stack.push(Value.from(false)); break;
|
||||
|
||||
static foreach(k, op; [ OpCode.Equal: "==", OpCode.NotEqual: "!=" ]){
|
||||
case k:
|
||||
static if(k == Add){
|
||||
if(peek(0).isStr && peek(1).isStr){
|
||||
const(Obj.String)* b = stack.pop().getStr;
|
||||
const(Obj.String)* a = stack.pop().getStr;
|
||||
Obj.String* newStr = Obj.String.concat(a, b, &this);
|
||||
stack.push(Value.str(newStr));
|
||||
binaryOp!(op, true, null, Value.Type.None);
|
||||
break opSwitch;
|
||||
}
|
||||
static foreach(k, op; [ OpCode.Greater: ">", OpCode.Less: "<", OpCode.GreaterEqual: ">=", OpCode.LessEqual: "<=" ]){
|
||||
case k:
|
||||
if(binaryOp!(op, checkSameType, "Operands must be of the same type.", Value.Type.None))
|
||||
return InterpretResult.RuntimeError;
|
||||
break opSwitch;
|
||||
}
|
||||
|
||||
static foreach(k, op; [ OpCode.Add: "+", OpCode.Subtract: "-", OpCode.Multiply: "*", OpCode.Divide: "-" ]){
|
||||
case k:
|
||||
static if(k == OpCode.Add){
|
||||
if(checkBinaryType!(Obj.Type.String)){
|
||||
Obj.String* b = stack.pop().asObj.asString;
|
||||
Obj.String* a = stack.pop().asObj.asString;
|
||||
Obj.String* result = Obj.String.concat(a, b);
|
||||
stack.push(Value.from(result));
|
||||
break opSwitch;
|
||||
}
|
||||
}
|
||||
if(!peek(0).isNum || !peek(1).isNum){
|
||||
runtimeError("Operands must be numbers.");
|
||||
if(binaryOp!(op, checkBinaryType!(Value.Type.Number), "Operands must be numbers.", Value.Type.Number)())
|
||||
return InterpretResult.RuntimeError;
|
||||
}
|
||||
double b = stack.pop().getNum;
|
||||
double a = stack.pop().getNum;
|
||||
stack.push(Value.num(mixin("a", op, "b")));
|
||||
break opSwitch;
|
||||
}
|
||||
static foreach(k, op; [ NotEqual: "!=", Equal: "==", Greater: ">", GreaterEqual: ">=", Less: "<", LessEqual: "<=" ]){
|
||||
case k:
|
||||
Value b = stack.pop();
|
||||
Value a = stack.pop();
|
||||
stack.push(Value.bln(compare!op(a, b)));
|
||||
break opSwitch;
|
||||
}
|
||||
case Not:
|
||||
stack.push(Value.bln(stack.pop().isFalsey));
|
||||
|
||||
case OpCode.Not:
|
||||
stack.push(Value.from(stack.pop().isFalsey));
|
||||
break;
|
||||
case Negate:
|
||||
if(!peek(0).isNum){
|
||||
case OpCode.Negate:
|
||||
if(!peek(0).isType(Value.Type.Number)){
|
||||
runtimeError("Operand must be a number.");
|
||||
return InterpretResult.RuntimeError;
|
||||
}
|
||||
stack.push(Value.num(-stack.pop().getNum));
|
||||
stack.push(Value.from(-stack.pop().asNumber));
|
||||
break;
|
||||
case Return:
|
||||
debug printValue(stack.pop());
|
||||
debug stderr.writeln();
|
||||
case OpCode.Return:
|
||||
stack.pop().print();
|
||||
printf("\n");
|
||||
return InterpretResult.Ok;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
void freeObjects(){
|
||||
for(Obj* obj = objects; obj !is null;){
|
||||
Obj* next = obj.next;
|
||||
obj.freeObject();
|
||||
obj = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue