Hash Tables 20

This commit is contained in:
nazrin 2025-06-07 04:09:48 +00:00
parent 3b234814fa
commit a7b7348f61
20 changed files with 868 additions and 837 deletions

View file

@ -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;
}
}
}