Local Variables 22
This commit is contained in:
parent
4f2211eb72
commit
72a41e81e6
18 changed files with 1335 additions and 115 deletions
|
|
@ -4,6 +4,7 @@ import std.algorithm.searching;
|
|||
|
||||
import clox.memory;
|
||||
import clox.value;
|
||||
import clox.container.dynarray;
|
||||
|
||||
struct OpColour{
|
||||
string r, g, b;
|
||||
|
|
@ -16,8 +17,10 @@ enum OpCode : ubyte{
|
|||
@(OpColour("255", "200", "100")) False,
|
||||
|
||||
@(OpColour("000", "200", "100")) Pop,
|
||||
@(OpColour("060", "200", "150")) GetLocal,
|
||||
@(OpColour("000", "200", "150")) GetGlobal,
|
||||
@(OpColour("000", "200", "150")) DefineGlobal,
|
||||
@(OpColour("060", "200", "150")) SetLocal,
|
||||
@(OpColour("000", "200", "150")) SetGlobal,
|
||||
|
||||
@(OpColour("255", "100", "100")) Equal,
|
||||
|
|
@ -39,40 +42,32 @@ enum OpCode : ubyte{
|
|||
}
|
||||
|
||||
struct Chunk{
|
||||
uint count;
|
||||
uint capacity;
|
||||
ubyte* code;
|
||||
uint* lines;
|
||||
ValueArray constants;
|
||||
DynArray!ubyte code;
|
||||
DynArray!uint lines;
|
||||
DynArray!Value constants;
|
||||
void initialise(){
|
||||
count = 0;
|
||||
capacity = 0;
|
||||
code = null;
|
||||
lines = null;
|
||||
code.initialise();
|
||||
lines.initialise();
|
||||
constants.initialise();
|
||||
}
|
||||
void write(ubyte b, uint line = 0){
|
||||
if(capacity < count + 1){
|
||||
uint oldCapacity = capacity;
|
||||
capacity = GROW_CAPACITY(oldCapacity);
|
||||
code = GROW_ARRAY!ubyte(code, oldCapacity, capacity);
|
||||
lines = GROW_ARRAY!uint(lines, oldCapacity, capacity);
|
||||
}
|
||||
code[count] = b;
|
||||
lines[count] = line;
|
||||
count++;
|
||||
void write(in ubyte[] bytes, uint line = 0){
|
||||
foreach(b; bytes)
|
||||
write(b, line);
|
||||
}
|
||||
int addConstant(Value value){
|
||||
int index = cast(int)constants.values[0 .. constants.count].countUntil(value);
|
||||
void write(ubyte b, uint line = 0){
|
||||
code ~= b;
|
||||
lines ~= line;
|
||||
}
|
||||
size_t addConstant(Value value){
|
||||
long index = constants[].countUntil(value);
|
||||
if(index >= 0)
|
||||
return index;
|
||||
assert(constants.count <= ubyte.max);
|
||||
constants.write(value);
|
||||
constants ~= value;
|
||||
return constants.count - 1;
|
||||
}
|
||||
void free(){
|
||||
FREE_ARRAY!ubyte(code, capacity);
|
||||
FREE_ARRAY!uint(lines, capacity);
|
||||
code.free();
|
||||
lines.free();
|
||||
constants.free();
|
||||
initialise();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,17 +7,26 @@ import clox.parser;
|
|||
import clox.chunk;
|
||||
import clox.emitter;
|
||||
import clox.dbg;
|
||||
import clox.container.dynarray;
|
||||
|
||||
struct Compiler{
|
||||
Scanner scanner;
|
||||
Parser parser;
|
||||
Emitter emitter;
|
||||
struct Local{
|
||||
enum Uninitialised = -1;
|
||||
Token name;
|
||||
int depth;
|
||||
}
|
||||
DynArray!Local locals;
|
||||
int scopeDepth;
|
||||
private Chunk* compilingChunk;
|
||||
Chunk* currentChunk() => compilingChunk;
|
||||
bool compile(const(char)* source, Chunk* chunk){
|
||||
scanner.initialise(source);
|
||||
parser.initialise(&this);
|
||||
emitter.initialise(&this);
|
||||
locals.initialise();
|
||||
compilingChunk = chunk;
|
||||
|
||||
parser.advance();
|
||||
|
|
@ -33,6 +42,7 @@ struct Compiler{
|
|||
if(!parser.hadError)
|
||||
currentChunk.disassembleChunk();
|
||||
}
|
||||
locals.free();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
45
src/clox/container/dynarray.d
Normal file
45
src/clox/container/dynarray.d
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
module clox.container.dynarray;
|
||||
|
||||
import clox.memory;
|
||||
|
||||
struct DynArray(T){
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
T* ptr;
|
||||
void initialise(){
|
||||
count = 0;
|
||||
capacity = 0;
|
||||
ptr = null;
|
||||
}
|
||||
void opOpAssign(string op: "~")(in T value){
|
||||
if(capacity < count + 1){
|
||||
size_t oldCapacity = capacity;
|
||||
capacity = GROW_CAPACITY(oldCapacity);
|
||||
ptr = GROW_ARRAY!T(ptr, oldCapacity, capacity);
|
||||
}
|
||||
ptr[count] = value;
|
||||
count++;
|
||||
}
|
||||
auto opSlice(){
|
||||
assert(ptr || !count);
|
||||
return ptr[0 .. count];
|
||||
}
|
||||
ref auto opIndex(size_t i){
|
||||
assert(ptr && count);
|
||||
return ptr[i];
|
||||
}
|
||||
size_t opDollar(size_t pos: 0)(){
|
||||
return count;
|
||||
}
|
||||
void free(){
|
||||
if(ptr)
|
||||
FREE_ARRAY!T(ptr, capacity);
|
||||
initialise();
|
||||
}
|
||||
auto pop(){
|
||||
assert(count);
|
||||
count--;
|
||||
return this[count-1];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,26 +1,30 @@
|
|||
module clox.container.stack;
|
||||
|
||||
struct Stack(T, size_t N){
|
||||
@nogc: nothrow:
|
||||
import clox.container.dynarray;
|
||||
|
||||
struct Stack(T){
|
||||
T* top;
|
||||
T[N] data;
|
||||
/* invariant{ assert(top <= data.ptr + N); assert(top >= data.ptr); } */
|
||||
void reset(){
|
||||
private DynArray!T data;
|
||||
void initialise(){
|
||||
data.initialise();
|
||||
top = data.ptr;
|
||||
}
|
||||
void push(T value){
|
||||
assert(top < data.ptr + N);
|
||||
debug assert(*top is T.init);
|
||||
*(top++) = value;
|
||||
data ~= value;
|
||||
top = data.ptr + data.count;
|
||||
}
|
||||
T pop(){
|
||||
assert(top > data.ptr);
|
||||
T t = *(--top);
|
||||
debug *(top) = T.init;
|
||||
assert(data.count-- >= 0);
|
||||
return t;
|
||||
}
|
||||
const(T)[] live() const @safe{
|
||||
return data[0 .. (top - data.ptr)];
|
||||
ref T opIndex(size_t i) => data[i];
|
||||
const(T)[] live(){
|
||||
return data[];
|
||||
}
|
||||
void free(){
|
||||
data.free();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import clox.memory;
|
|||
enum TABLE_MAX_LOAD = 0.75;
|
||||
|
||||
struct Table{
|
||||
uint count, capacity;
|
||||
size_t count, capacity;
|
||||
Entry* entries;
|
||||
struct Entry{
|
||||
Obj.String* key;
|
||||
|
|
@ -25,7 +25,7 @@ struct Table{
|
|||
FREE_ARRAY!Entry(entries, capacity);
|
||||
initialise();
|
||||
}
|
||||
private void adjustCapacity(uint cap){
|
||||
private void adjustCapacity(size_t cap){
|
||||
Entry* ent = ALLOCATE!Entry(cap);
|
||||
for(int i = 0; i < cap; i++){
|
||||
ent[i].key = null;
|
||||
|
|
@ -46,7 +46,7 @@ struct Table{
|
|||
entries = ent;
|
||||
capacity = cap;
|
||||
}
|
||||
private Entry* findEntry(Entry* entries, int capacity, in Obj.String* key){
|
||||
private Entry* findEntry(Entry* entries, size_t capacity, in Obj.String* key){
|
||||
uint index = key.hash % capacity;
|
||||
Entry* tombstone = null;
|
||||
while(true){
|
||||
|
|
@ -66,7 +66,7 @@ struct Table{
|
|||
}
|
||||
bool set(Obj.String* key, Value value){
|
||||
if(count + 1 > capacity * TABLE_MAX_LOAD){
|
||||
uint cap = GROW_CAPACITY(capacity);
|
||||
size_t cap = GROW_CAPACITY(capacity);
|
||||
adjustCapacity(cap);
|
||||
}
|
||||
Entry* entry = findEntry(entries, capacity, key);
|
||||
|
|
@ -121,12 +121,13 @@ struct Table{
|
|||
}
|
||||
|
||||
unittest{
|
||||
import clox.vm : vm;
|
||||
Table tbl;
|
||||
tbl.initialise();
|
||||
scope(exit)
|
||||
scope(exit){
|
||||
tbl.free();
|
||||
scope(exit)
|
||||
freeObjects();
|
||||
vm.free();
|
||||
}
|
||||
|
||||
assert(tbl.count == 0);
|
||||
|
||||
|
|
@ -146,7 +147,7 @@ unittest{
|
|||
assert(tbl.count == 1);
|
||||
|
||||
foreach(i; 0..25){
|
||||
Obj.String* str2 = Obj.String.copy("hello");
|
||||
Obj.String* str2 = Obj.String.copy("hi 2");
|
||||
tbl.set(str2, Value.from(i));
|
||||
assert(tbl.get(str2, val));
|
||||
assert(val.asNumber == i);
|
||||
|
|
|
|||
73
src/clox/container/varint.d
Normal file
73
src/clox/container/varint.d
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
module clox.container.varint;
|
||||
|
||||
struct VarUint{
|
||||
import std.bitmanip;
|
||||
nothrow: @nogc:
|
||||
uint i;
|
||||
ubyte len;
|
||||
private ubyte[4] data;
|
||||
this(size_t l) @safe {
|
||||
if(l < 0b1000_0000){
|
||||
len = 1;
|
||||
data[0] = (cast(ubyte)l);
|
||||
return;
|
||||
}
|
||||
if(l < 0b0100_0000__0000_0000){
|
||||
len = 2;
|
||||
data[0 .. 2] = nativeToBigEndian(cast(ushort)l);
|
||||
data[0] |= 0b1000_0000;
|
||||
return;
|
||||
}
|
||||
if(l < 0b0010_0000__0000_0000__0000_0000__0000_0000){
|
||||
len = 4;
|
||||
data[0 .. 4] = nativeToBigEndian(cast(uint)l);
|
||||
data[0] |= 0b1100_0000;
|
||||
return;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
static VarUint read(in ubyte* data){
|
||||
VarUint v;
|
||||
ubyte a = data[0];
|
||||
if((data[0] & 0b1000_0000) == 0){
|
||||
v.i = a;
|
||||
v.len = 1;
|
||||
return v;
|
||||
}
|
||||
if((a & 0b0100_0000) == 0){
|
||||
ubyte[2] d = data[0 .. 2];
|
||||
d[0] &= 0b0111_1111;
|
||||
v.i = bigEndianToNative!ushort(d);
|
||||
v.len = 2;
|
||||
return v;
|
||||
}
|
||||
if((a & 0b0010_0000) == 0){
|
||||
ubyte[4] d = data[0 .. 4];
|
||||
d[0] &= 0b0011_1111;
|
||||
v.i = bigEndianToNative!uint(d);
|
||||
v.len = 4;
|
||||
return v;
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
ubyte[] bytes(){
|
||||
return data[0 .. len];
|
||||
}
|
||||
}
|
||||
unittest{
|
||||
import std.range;
|
||||
assert(VarUint(5).bytes.length == 1);
|
||||
assert(VarUint(127).bytes.length == 1);
|
||||
assert(VarUint(128).bytes.length == 2);
|
||||
assert(VarUint(536_870_911).bytes.length == 4);
|
||||
foreach(i; [
|
||||
0, 1, 2, 5,
|
||||
150, 127, 128,
|
||||
536_870_911,
|
||||
ushort.max * 100
|
||||
]){
|
||||
auto vi = VarUint(i);
|
||||
assert(i == VarUint.read(vi.bytes.ptr).i);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7,32 +7,35 @@ import std.traits : EnumMembers, hasUDA, getUDAs;
|
|||
import clox.chunk;
|
||||
import clox.value;
|
||||
import clox.util;
|
||||
import clox.container.varint;
|
||||
|
||||
private int simpleInstruction(alias OpCode op)(const char* name, int offset){
|
||||
import std.stdio;
|
||||
|
||||
private long simpleInstruction(alias OpCode op)(const char* name, long offset){
|
||||
enum c = getUDAs!(op, OpColour)[0];
|
||||
printf(colour!("%-27s ", c.r, c.g, c.b).ptr, name);
|
||||
return offset + 1;
|
||||
}
|
||||
private int constantInstruction(alias OpCode op)(const char* name, Chunk* chunk, int offset){
|
||||
private long constantInstruction(alias OpCode op)(const char* name, Chunk* chunk, long offset){
|
||||
VarUint constant = VarUint.read(&chunk.code[offset + 1]);
|
||||
enum c = getUDAs!(op, OpColour)[0];
|
||||
ubyte constant = chunk.code[offset + 1];
|
||||
printf(ctEval!(colour!("%-14s", c.r, c.g, c.b) ~ colour!(" %4d ", Colour.Black)).ptr, name, constant);
|
||||
int pl = chunk.constants.values[constant].print();
|
||||
printf(ctEval!(colour!("%-14s", c.r, c.g, c.b) ~ colour!(" %4d ", Colour.Black)).ptr, name, constant.i);
|
||||
long pl = chunk.constants[constant.i].print();
|
||||
foreach(i; 0 .. 16-pl)
|
||||
printf(" ");
|
||||
return offset + 2;
|
||||
return offset + 1 + constant.len;
|
||||
}
|
||||
|
||||
void disassembleChunk(Chunk* chunk, const char* name = "chunk"){
|
||||
printf(" == %s ==\n", name);
|
||||
for(int offset = 0; offset < chunk.count;){
|
||||
for(long offset = 0; offset < chunk.code.count;){
|
||||
offset = disassembleInstruction(chunk, offset);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int disassembleInstruction(Chunk* chunk, int offset){
|
||||
printf("%5d ", offset);
|
||||
long disassembleInstruction(Chunk* chunk, long offset){
|
||||
printf("%5ld ", offset);
|
||||
if(offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1]){
|
||||
printf(colour!(" | ", Colour.Black).ptr);
|
||||
} else {
|
||||
|
|
@ -48,7 +51,7 @@ int disassembleInstruction(Chunk* chunk, int offset){
|
|||
return simpleInstruction!e(e.stringof, offset);
|
||||
}
|
||||
}
|
||||
default: printf("Unknown opcode %d", instruction);
|
||||
default: printf("(Unknown opcode %d)", instruction);
|
||||
return offset + 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import core.stdc.stdio;
|
|||
import clox.chunk;
|
||||
import clox.compiler;
|
||||
import clox.value;
|
||||
import clox.container.varint;
|
||||
|
||||
struct Emitter{
|
||||
Compiler* compiler;
|
||||
|
|
@ -13,18 +14,19 @@ struct Emitter{
|
|||
void initialise(Compiler* compiler){
|
||||
this.compiler = compiler;
|
||||
}
|
||||
void emit(ubyte[] bytes...){
|
||||
foreach(b; bytes)
|
||||
void emit(Args...)(Args a){
|
||||
static foreach(b; a)
|
||||
compiler.currentChunk.write(b, compiler.parser.previous.line);
|
||||
}
|
||||
void emitReturn(){
|
||||
emit(OpCode.Return);
|
||||
}
|
||||
void emitConstant(Value value){
|
||||
emit(OpCode.Constant, cast(ubyte)makeConstant(value));
|
||||
size_t c = makeConstant(value);
|
||||
emit(OpCode.Constant, VarUint(c).bytes);
|
||||
}
|
||||
uint makeConstant(Value value){
|
||||
uint constant = compiler.currentChunk.addConstant(value);
|
||||
size_t makeConstant(Value value){
|
||||
size_t constant = compiler.currentChunk.addConstant(value);
|
||||
return constant;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ int interpretResult(VM.InterpretResult result){
|
|||
}
|
||||
|
||||
int runPrompt(){
|
||||
vm.isREPL = true;
|
||||
char[1024 * 4] line;
|
||||
while(true){
|
||||
printf(colour!("lox> ", Colour.Green).ptr);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ module clox.memory;
|
|||
|
||||
import core.stdc.stdlib;
|
||||
|
||||
auto GROW_CAPACITY(uint capacity) => capacity < 8 ? 8 : capacity * 2;
|
||||
auto GROW_CAPACITY(size_t capacity) => capacity < 8 ? 8 : capacity * 2;
|
||||
auto GROW_ARRAY(T)(T* ptr, size_t oldCount, size_t newCount) => cast(T*)reallocate(ptr, T.sizeof * oldCount, T.sizeof * newCount);
|
||||
auto FREE_ARRAY(T)(T* ptr, size_t oldCount) => reallocate!T(ptr, T.sizeof * oldCount, 0);
|
||||
auto FREE(T)(T* ptr) => reallocate(ptr, T.sizeof, 0);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import clox.parserules;
|
|||
import clox.util;
|
||||
import clox.object;
|
||||
import clox.value;
|
||||
import clox.vm;
|
||||
import clox.container.varint;
|
||||
|
||||
struct Parser{
|
||||
Compiler* compiler;
|
||||
|
|
@ -48,11 +50,16 @@ struct Parser{
|
|||
}
|
||||
void expressionStatement(){
|
||||
expression();
|
||||
consume(Token.Type.Semicolon, "Expect ';' after expression.");
|
||||
compiler.emitter.emit(OpCode.Pop);
|
||||
if(vm.isREPL){
|
||||
match(Token.Type.Semicolon);
|
||||
compiler.emitter.emit(OpCode.Print);
|
||||
} else {
|
||||
consume(Token.Type.Semicolon, "Expect ';' after expression.");
|
||||
compiler.emitter.emit(OpCode.Pop);
|
||||
}
|
||||
}
|
||||
void varDeclaration(){
|
||||
ubyte global = parseVariable("Expect variable name.");
|
||||
long global = parseVariable("Expect variable name.");
|
||||
if(match(Token.Type.Equal))
|
||||
expression();
|
||||
else
|
||||
|
|
@ -66,12 +73,16 @@ struct Parser{
|
|||
else
|
||||
statement();
|
||||
if(compiler.parser.panicMode)
|
||||
synchronize();
|
||||
synchronise();
|
||||
}
|
||||
void statement(){
|
||||
if(match(Token.Type.Print))
|
||||
if(match(Token.Type.Print)){
|
||||
printStatement();
|
||||
else
|
||||
} else if(match(Token.Type.LeftBrace)){
|
||||
beginScope();
|
||||
block();
|
||||
endScope();
|
||||
} else
|
||||
expressionStatement();
|
||||
}
|
||||
void printStatement(){
|
||||
|
|
@ -79,25 +90,87 @@ struct Parser{
|
|||
consume(Token.Type.Semicolon, "Expect ';' after value.");
|
||||
compiler.emitter.emit(OpCode.Print);
|
||||
}
|
||||
|
||||
void defineVariable(ubyte global){
|
||||
compiler.emitter.emit(OpCode.DefineGlobal, global);
|
||||
void block(){
|
||||
while(!check(Token.Type.RightBrace) && !check(Token.Type.EOF))
|
||||
declaration();
|
||||
consume(Token.Type.RightBrace, "Expect '}' after block.");
|
||||
}
|
||||
ubyte parseVariable(const char* errorMessage){
|
||||
|
||||
void defineVariable(long global){
|
||||
if(compiler.scopeDepth > 0){
|
||||
markInitialised();
|
||||
return;
|
||||
}
|
||||
compiler.emitter.emit(OpCode.DefineGlobal, VarUint(global).bytes);
|
||||
}
|
||||
void markInitialised(){
|
||||
compiler.locals[compiler.locals.count - 1].depth = compiler.scopeDepth;
|
||||
}
|
||||
void declareVariable(){
|
||||
if(compiler.scopeDepth == 0)
|
||||
return;
|
||||
Token* name = &compiler.parser.previous;
|
||||
for(long i = compiler.locals.count - 1; i >= 0; i--){
|
||||
Compiler.Local* local = &compiler.locals[i];
|
||||
if(local.depth != Compiler.Local.Uninitialised && local.depth < compiler.scopeDepth)
|
||||
break;
|
||||
if(name.lexeme == local.name.lexeme)
|
||||
error("Already a variable with this name in this scope.");
|
||||
}
|
||||
addLocal(*name);
|
||||
}
|
||||
long parseVariable(const char* errorMessage){
|
||||
consume(Token.Type.Identifier, errorMessage);
|
||||
declareVariable();
|
||||
if(compiler.scopeDepth > 0)
|
||||
return 0;
|
||||
return identifierConstant(&previous);
|
||||
}
|
||||
ubyte identifierConstant(Token* name){
|
||||
long identifierConstant(Token* name){
|
||||
Value nameVal = Value.from(Obj.String.copy(name.lexeme));
|
||||
return cast(ubyte)compiler.emitter.makeConstant(nameVal);
|
||||
return compiler.emitter.makeConstant(nameVal);
|
||||
}
|
||||
void namedVariable(Token name, bool canAssign){
|
||||
ubyte arg = compiler.parser.identifierConstant(&name);
|
||||
OpCode getOp, setOp;
|
||||
long arg = resolveLocal(&name);
|
||||
if(arg != -1){
|
||||
getOp = OpCode.GetLocal;
|
||||
setOp = OpCode.SetLocal;
|
||||
} else {
|
||||
arg = identifierConstant(&name);
|
||||
getOp = OpCode.GetGlobal;
|
||||
setOp = OpCode.SetGlobal;
|
||||
}
|
||||
if(canAssign && match(Token.Type.Equal)){
|
||||
expression();
|
||||
compiler.emitter.emit(OpCode.SetGlobal, arg);
|
||||
compiler.emitter.emit(setOp, VarUint(arg).bytes);
|
||||
} else {
|
||||
compiler.emitter.emit(OpCode.GetGlobal, arg);
|
||||
compiler.emitter.emit(getOp, VarUint(arg).bytes);
|
||||
}
|
||||
}
|
||||
|
||||
long resolveLocal(Token* name){
|
||||
for(long i = compiler.locals.count - 1; i >= 0; i--){
|
||||
Compiler.Local* local = &compiler.locals[i];
|
||||
if(name.lexeme == local.name.lexeme){
|
||||
if(local.depth == Compiler.Local.Uninitialised)
|
||||
error("Can't read local variable in its own initialiser.");
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
void addLocal(Token name){
|
||||
compiler.locals ~= Compiler.Local(name, Compiler.Local.Uninitialised);
|
||||
}
|
||||
void beginScope(){
|
||||
compiler.scopeDepth++;
|
||||
}
|
||||
void endScope(){
|
||||
assert(--compiler.scopeDepth >= 0);
|
||||
while(compiler.locals.count > 0 && compiler.locals[compiler.locals.count - 1].depth > compiler.scopeDepth){
|
||||
compiler.emitter.emit(OpCode.Pop);
|
||||
compiler.locals.count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +192,7 @@ struct Parser{
|
|||
error("Invalid assignment target.");
|
||||
}
|
||||
|
||||
void synchronize(){
|
||||
void synchronise(){
|
||||
panicMode = false;
|
||||
while(current.type != Token.Type.EOF){
|
||||
if(previous.type == Token.Type.Semicolon)
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ struct Scanner{
|
|||
while(true){
|
||||
char c = peek();
|
||||
switch(c){
|
||||
case ' ', '\r':
|
||||
case ' ', '\r', '\t':
|
||||
advance();
|
||||
break;
|
||||
case '\n':
|
||||
|
|
|
|||
|
|
@ -71,29 +71,3 @@ struct Value{
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct ValueArray{
|
||||
uint count;
|
||||
uint capacity;
|
||||
Value* values;
|
||||
void initialise(){
|
||||
count = 0;
|
||||
capacity = 0;
|
||||
values = null;
|
||||
}
|
||||
void write(Value value){
|
||||
if(capacity < count + 1){
|
||||
int oldCapacity = capacity;
|
||||
capacity = GROW_CAPACITY(oldCapacity);
|
||||
values = GROW_ARRAY!Value(values, oldCapacity, capacity);
|
||||
}
|
||||
values[count] = value;
|
||||
count++;
|
||||
}
|
||||
void free(){
|
||||
if(values)
|
||||
FREE_ARRAY!Value(values, capacity);
|
||||
initialise();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,19 +12,21 @@ import clox.compiler;
|
|||
import clox.memory;
|
||||
import clox.container.stack;
|
||||
import clox.container.table;
|
||||
import clox.container.varint;
|
||||
|
||||
VM vm;
|
||||
|
||||
struct VM{
|
||||
Chunk* chunk;
|
||||
ubyte* ip;
|
||||
Stack!(Value, 256) stack;
|
||||
Stack!Value stack;
|
||||
Table globals;
|
||||
Table strings;
|
||||
Obj* objects;
|
||||
bool isREPL;
|
||||
enum InterpretResult{ Ok, CompileError, RuntimeError }
|
||||
void initialise(){
|
||||
stack.reset();
|
||||
stack.initialise();
|
||||
strings.initialise();
|
||||
globals.initialise();
|
||||
}
|
||||
|
|
@ -32,6 +34,7 @@ struct VM{
|
|||
freeObjects();
|
||||
strings.free();
|
||||
globals.free();
|
||||
stack.free();
|
||||
}
|
||||
InterpretResult interpret(const char* source){
|
||||
Chunk cnk;
|
||||
|
|
@ -42,21 +45,27 @@ struct VM{
|
|||
if(!compiler.compile(source, &cnk))
|
||||
return InterpretResult.CompileError;
|
||||
this.chunk = &cnk;
|
||||
this.ip = cnk.code;
|
||||
this.ip = cnk.code.ptr;
|
||||
return run();
|
||||
}
|
||||
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];
|
||||
size_t instruction = vm.ip - vm.chunk.code.ptr - 1;
|
||||
uint line = vm.chunk.lines[instruction];
|
||||
fprintf(stderr, "[line %d] in script\n", line);
|
||||
stack.reset();
|
||||
}
|
||||
InterpretResult run(){
|
||||
ubyte readByte() => *vm.ip++;
|
||||
auto readConstant() => chunk.constants.values[readByte()];
|
||||
ubyte readByte() => *ip++;
|
||||
long readVarUint(){
|
||||
VarUint vi = VarUint.read(ip);
|
||||
ip += vi.len;
|
||||
return vi.i;
|
||||
}
|
||||
Value readConstant(){
|
||||
return chunk.constants[readVarUint()];
|
||||
}
|
||||
Obj.String* readString() => readConstant().asObj.asString;
|
||||
Value peek(int distance = 0) => stack.top[-1 - distance];
|
||||
bool checkBinaryType(alias type)() => peek(0).isType(type) && peek(1).isType(type);
|
||||
|
|
@ -73,7 +82,7 @@ struct VM{
|
|||
}
|
||||
while(true){
|
||||
debug(traceExec){
|
||||
disassembleInstruction(vm.chunk, cast(int)(vm.ip - vm.chunk.code));
|
||||
disassembleInstruction(vm.chunk, vm.ip - vm.chunk.code.ptr);
|
||||
printf(" ");
|
||||
foreach(slot; stack.live){
|
||||
printf("");
|
||||
|
|
@ -93,6 +102,14 @@ struct VM{
|
|||
case OpCode.False: stack.push(Value.from(false)); break;
|
||||
case OpCode.Pop: stack.pop(); break;
|
||||
|
||||
case OpCode.GetLocal:
|
||||
long slot = readVarUint();
|
||||
stack.push(stack[slot]);
|
||||
break;
|
||||
case OpCode.SetLocal:
|
||||
long slot = readVarUint();
|
||||
stack[slot] = peek(0);
|
||||
break;
|
||||
case OpCode.GetGlobal:
|
||||
Obj.String* name = readString();
|
||||
Value value;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue