A Virtual Machine 15
This commit is contained in:
parent
7fa01b4fb9
commit
aba643a88e
7 changed files with 139 additions and 28 deletions
7
dub.sdl
7
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"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
25
src/clox/util.d
Normal 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)];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
59
src/clox/vm.d
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue