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"
|
license "MPL-2.0"
|
||||||
dependency "commandr" version="~>1.1.0"
|
dependency "commandr" version="~>1.1.0"
|
||||||
targetType "executable"
|
targetType "executable"
|
||||||
buildRequirements "requireBoundsCheck" "requireContracts"
|
|
||||||
|
|
||||||
versions "LoxConcatNonStrings" "LoxExtraNativeFuncs" "LoxPrintMultiple"
|
|
||||||
|
|
||||||
sourcePaths
|
sourcePaths
|
||||||
configuration "clox" {
|
configuration "clox" {
|
||||||
|
debugVersions "traceExec"
|
||||||
|
buildRequirements "requireBoundsCheck" "requireContracts"
|
||||||
sourcePaths "src/clox" "src/common"
|
sourcePaths "src/clox" "src/common"
|
||||||
}
|
}
|
||||||
configuration "jlox" {
|
configuration "jlox" {
|
||||||
|
versions "LoxConcatNonStrings" "LoxExtraNativeFuncs" "LoxPrintMultiple"
|
||||||
|
buildRequirements "requireBoundsCheck" "requireContracts"
|
||||||
sourcePaths "src/jlox" "src/common"
|
sourcePaths "src/jlox" "src/common"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ import std.stdio;
|
||||||
import clox.value;
|
import clox.value;
|
||||||
|
|
||||||
enum OpCode : ubyte{
|
enum OpCode : ubyte{
|
||||||
OP_CONSTANT,
|
Constant,
|
||||||
OP_RETURN,
|
Add, Subtract, Multiply, Divide,
|
||||||
|
Negate,
|
||||||
|
Return,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Chunk{
|
struct Chunk{
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,47 @@
|
||||||
module clox.dbg;
|
module clox.dbg;
|
||||||
|
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
import std.conv;
|
||||||
|
import std.uni;
|
||||||
|
|
||||||
import clox.chunk;
|
import clox.chunk;
|
||||||
import clox.value;
|
import clox.value;
|
||||||
|
|
||||||
private ulong simpleInstruction(string name, ulong offset){
|
debug private ulong simpleInstruction(string name, ulong offset) @nogc nothrow{
|
||||||
writeln(name);
|
debug writeln(name);
|
||||||
return offset + 1;
|
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];
|
ubyte constant = chunk.code[offset + 1];
|
||||||
writef("%-16s %4d '", name, constant);
|
debug writef("%-16s %4d '", name, constant);
|
||||||
printValue(chunk.constants[constant]);
|
debug printValue(chunk.constants[constant]);
|
||||||
writeln("'");
|
debug writeln("'");
|
||||||
return offset + 2;
|
return offset + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disassembleChunk(in ref Chunk chunk, string name = "chunk"){
|
debug void disassembleChunk(Chunk* chunk, string name = "chunk") @nogc nothrow{
|
||||||
writefln("== %s ==", name);
|
debug writefln("== %s ==", name);
|
||||||
for(ulong offset = 0; offset < chunk.code.length;)
|
for(ulong offset = 0; offset < chunk.code.length;)
|
||||||
offset = disassembleInstruction(chunk, offset);
|
offset = disassembleInstruction(chunk, offset);
|
||||||
}
|
}
|
||||||
ulong disassembleInstruction(in ref Chunk chunk, const ulong offset){
|
debug ulong disassembleInstruction(Chunk* chunk, const ulong offset) @nogc nothrow{
|
||||||
writef(" %04d ", offset);
|
debug writef(" %04d ", offset);
|
||||||
if(offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1])
|
if(offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1]){
|
||||||
write(" | ");
|
debug write(" | ");
|
||||||
else
|
} else {
|
||||||
writef(" %4d ", chunk.lines[offset]);
|
debug writef(" %4d ", chunk.lines[offset]);
|
||||||
|
}
|
||||||
ubyte instruction = chunk.code[offset];
|
ubyte instruction = chunk.code[offset];
|
||||||
with(OpCode) switch(instruction){
|
with(OpCode) switch(instruction){
|
||||||
case OP_CONSTANT:
|
case Constant:
|
||||||
return constantInstruction("OP_CONSTANT", chunk, offset);
|
return constantInstruction("OP_CONSTANT", chunk, offset);
|
||||||
case OP_RETURN:
|
static foreach(k; [ Return, Negate, Add, Subtract, Multiply, Divide ]){
|
||||||
return simpleInstruction("OP_RETURN", offset);
|
case k:
|
||||||
|
static name = "OP_" ~ (k.to!string).toUpper;
|
||||||
|
return simpleInstruction(name, offset);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
writefln("Unknown opcode %d", instruction);
|
debug writefln("Unknown opcode %d", instruction);
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,35 @@ import std.stdio;
|
||||||
|
|
||||||
import clox.chunk;
|
import clox.chunk;
|
||||||
import clox.dbg;
|
import clox.dbg;
|
||||||
|
import clox.vm;
|
||||||
|
|
||||||
int main(string[] argv){
|
int main(string[] argv){
|
||||||
Chunk chunk;
|
Chunk chunk;
|
||||||
|
|
||||||
ubyte constant = chunk.addConstant(1.2);
|
ubyte constant = chunk.addConstant(1.2);
|
||||||
chunk.write(OpCode.OP_CONSTANT);
|
chunk.write(OpCode.Constant);
|
||||||
chunk.write(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;
|
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;
|
alias Value = double;
|
||||||
|
|
||||||
void printValue(Value value){
|
debug void printValue(Value value) @nogc nothrow{
|
||||||
writef("%g", value);
|
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