diff --git a/dub.sdl b/dub.sdl index 8248879..9d1f085 100644 --- a/dub.sdl +++ b/dub.sdl @@ -5,15 +5,16 @@ copyright "Copyright © 2025, tanya" license "MPL-2.0" dependency "commandr" version="~>1.1.0" targetType "executable" -buildRequirements "requireBoundsCheck" "requireContracts" - -versions "LoxConcatNonStrings" "LoxExtraNativeFuncs" "LoxPrintMultiple" sourcePaths configuration "clox" { + debugVersions "traceExec" + buildRequirements "requireBoundsCheck" "requireContracts" sourcePaths "src/clox" "src/common" } configuration "jlox" { + versions "LoxConcatNonStrings" "LoxExtraNativeFuncs" "LoxPrintMultiple" + buildRequirements "requireBoundsCheck" "requireContracts" sourcePaths "src/jlox" "src/common" } diff --git a/src/clox/chunk.d b/src/clox/chunk.d index d6199eb..20ce7ef 100644 --- a/src/clox/chunk.d +++ b/src/clox/chunk.d @@ -6,8 +6,10 @@ import std.stdio; import clox.value; enum OpCode : ubyte{ - OP_CONSTANT, - OP_RETURN, + Constant, + Add, Subtract, Multiply, Divide, + Negate, + Return, } struct Chunk{ diff --git a/src/clox/dbg.d b/src/clox/dbg.d index 9895399..fe63856 100644 --- a/src/clox/dbg.d +++ b/src/clox/dbg.d @@ -1,41 +1,47 @@ module clox.dbg; import std.stdio; +import std.conv; +import std.uni; import clox.chunk; import clox.value; -private ulong simpleInstruction(string name, ulong offset){ - writeln(name); +debug private ulong simpleInstruction(string name, ulong offset) @nogc nothrow{ + debug writeln(name); return offset + 1; } -private ulong constantInstruction(string name, in ref Chunk chunk, ulong offset){ +debug private ulong constantInstruction(string name, Chunk* chunk, ulong offset) @nogc nothrow{ ubyte constant = chunk.code[offset + 1]; - writef("%-16s %4d '", name, constant); - printValue(chunk.constants[constant]); - writeln("'"); + debug writef("%-16s %4d '", name, constant); + debug printValue(chunk.constants[constant]); + debug writeln("'"); return offset + 2; } -void disassembleChunk(in ref Chunk chunk, string name = "chunk"){ - writefln("== %s ==", name); +debug void disassembleChunk(Chunk* chunk, string name = "chunk") @nogc nothrow{ + debug writefln("== %s ==", name); for(ulong offset = 0; offset < chunk.code.length;) offset = disassembleInstruction(chunk, offset); } -ulong disassembleInstruction(in ref Chunk chunk, const ulong offset){ - writef(" %04d ", offset); - if(offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1]) - write(" | "); - else - writef(" %4d ", chunk.lines[offset]); +debug ulong disassembleInstruction(Chunk* chunk, const ulong offset) @nogc nothrow{ + debug writef(" %04d ", offset); + if(offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1]){ + debug write(" | "); + } else { + debug writef(" %4d ", chunk.lines[offset]); + } ubyte instruction = chunk.code[offset]; with(OpCode) switch(instruction){ - case OP_CONSTANT: + case Constant: return constantInstruction("OP_CONSTANT", chunk, offset); - case OP_RETURN: - return simpleInstruction("OP_RETURN", offset); + static foreach(k; [ Return, Negate, Add, Subtract, Multiply, Divide ]){ + case k: + static name = "OP_" ~ (k.to!string).toUpper; + return simpleInstruction(name, offset); + } default: - writefln("Unknown opcode %d", instruction); + debug writefln("Unknown opcode %d", instruction); return offset + 1; } } diff --git a/src/clox/main.d b/src/clox/main.d index cc07501..f5dd3be 100644 --- a/src/clox/main.d +++ b/src/clox/main.d @@ -4,17 +4,35 @@ import std.stdio; import clox.chunk; import clox.dbg; +import clox.vm; int main(string[] argv){ Chunk chunk; ubyte constant = chunk.addConstant(1.2); - chunk.write(OpCode.OP_CONSTANT); + chunk.write(OpCode.Constant); chunk.write(constant); - chunk.write(OpCode.OP_RETURN); + constant = chunk.addConstant(3.4); + chunk.write(OpCode.Constant); + chunk.write(constant); + + chunk.write(OpCode.Add); + + constant = chunk.addConstant(5.6); + chunk.write(OpCode.Constant); + chunk.write(constant); + + chunk.write(OpCode.Divide); + + chunk.write(OpCode.Negate); + + chunk.write(OpCode.Return); + + VM vm = VM(0); + vm.interpret(&chunk); + - disassembleChunk(chunk); return 0; } diff --git a/src/clox/util.d b/src/clox/util.d new file mode 100644 index 0000000..081e6b9 --- /dev/null +++ b/src/clox/util.d @@ -0,0 +1,25 @@ +module clox.util; + +struct Stack(T, size_t N){ + T* top; + T[N] data; + invariant{ assert(top <= data.ptr + N); assert(top >= data.ptr); } + this(int _) @nogc nothrow{ + top = data.ptr; + } + void push(T value) @nogc nothrow{ + assert(top < data.ptr + N); + debug assert(*top is T.init); + *(top++) = value; + } + T pop() @nogc nothrow{ + assert(top > data.ptr); + T t = *(--top); + debug *(top) = T.init; + return t; + } + const(T)[] live() @nogc nothrow const{ + return data[0 .. (top - data.ptr)]; + } +} + diff --git a/src/clox/value.d b/src/clox/value.d index c9b298c..eb74daa 100644 --- a/src/clox/value.d +++ b/src/clox/value.d @@ -4,7 +4,7 @@ import std.stdio; alias Value = double; -void printValue(Value value){ - writef("%g", value); +debug void printValue(Value value) @nogc nothrow{ + debug writef("%g", value); } diff --git a/src/clox/vm.d b/src/clox/vm.d new file mode 100644 index 0000000..b9c9cfe --- /dev/null +++ b/src/clox/vm.d @@ -0,0 +1,59 @@ +module clox.vm; + +import std.stdio; + +import clox.chunk; +import clox.value; +import clox.dbg; +import clox.util; + +enum stackMax = 256; + +struct VM{ + const(ubyte)* ip; + Stack!(Value, stackMax) stack; + Chunk* chunk; + enum InterpretResult{ Ok, CompileError, RunetimeError } + this(int _) @nogc nothrow { + stack = typeof(stack)(0); + } + InterpretResult interpret(Chunk* chunk) @nogc nothrow { + this.chunk = chunk; + ip = &chunk.code[0]; + return run(); + } + private InterpretResult run() @nogc nothrow { + auto readByte() => *ip++; + auto readIns() => cast(OpCode)readByte(); + auto readConstant() => chunk.constants[readByte()]; + while(true){ + debug(traceExec){ + writeln(" ", stack.live); + debug disassembleInstruction(chunk, ip - &chunk.code[0]); + } + OpCode instruction = readIns(); + with(OpCode) opSwitch: final switch(instruction){ + case Constant: + Value constant = readConstant(); + stack.push(constant); + break; + static foreach(k, op; [ Add: "+", Subtract: "-", Multiply: "*", Divide: "/" ]){ + case k: + Value b = stack.pop(); + Value a = stack.pop(); + stack.push(mixin("a", op, "b")); + break opSwitch; + } + case Negate: + stack.push(-stack.pop()); + break; + case Return: + debug printValue(stack.pop()); + debug writeln(); + return InterpretResult.Ok; + } + } + assert(0); + } +} +