A Virtual Machine 15

This commit is contained in:
nazrin 2025-06-03 19:04:25 +00:00
parent 7fa01b4fb9
commit aba643a88e
7 changed files with 139 additions and 28 deletions

View file

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

View file

@ -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{

View file

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

View file

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

25
src/clox/util.d Normal file
View file

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

View file

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

59
src/clox/vm.d Normal file
View file

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