Global Variables 21
This commit is contained in:
parent
a7b7348f61
commit
4f2211eb72
9 changed files with 178 additions and 36 deletions
10
dub.sdl
10
dub.sdl
|
|
@ -7,17 +7,23 @@ dependency "commandr" version="~>1.1.0"
|
||||||
dependency "colored" version="~>0.0.33"
|
dependency "colored" version="~>0.0.33"
|
||||||
targetType "executable"
|
targetType "executable"
|
||||||
sourcePaths
|
sourcePaths
|
||||||
|
|
||||||
configuration "clox" {
|
configuration "clox" {
|
||||||
|
buildOptions "betterC"
|
||||||
|
/* debugVersions "memTrace" */
|
||||||
debugVersions "traceExec"
|
debugVersions "traceExec"
|
||||||
|
/* debugVersions "printCode" */
|
||||||
|
dflags "-checkaction=C"
|
||||||
libs "libbacktrace"
|
libs "libbacktrace"
|
||||||
debugVersions "printCode"
|
|
||||||
targetType "executable"
|
targetType "executable"
|
||||||
sourcePaths "src/clox" "src/common"
|
sourcePaths "src/clox"
|
||||||
buildRequirements "requireBoundsCheck" "requireContracts"
|
buildRequirements "requireBoundsCheck" "requireContracts"
|
||||||
}
|
}
|
||||||
|
|
||||||
configuration "jlox" {
|
configuration "jlox" {
|
||||||
targetType "executable"
|
targetType "executable"
|
||||||
sourcePaths "src/jlox" "src/common"
|
sourcePaths "src/jlox" "src/common"
|
||||||
versions "LoxConcatNonStrings" "LoxExtraNativeFuncs" "LoxPrintMultiple"
|
versions "LoxConcatNonStrings" "LoxExtraNativeFuncs" "LoxPrintMultiple"
|
||||||
buildRequirements "requireBoundsCheck" "requireContracts"
|
buildRequirements "requireBoundsCheck" "requireContracts"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
module clox.chunk;
|
module clox.chunk;
|
||||||
|
|
||||||
|
import std.algorithm.searching;
|
||||||
|
|
||||||
import clox.memory;
|
import clox.memory;
|
||||||
import clox.value;
|
import clox.value;
|
||||||
|
|
||||||
|
|
@ -13,6 +15,11 @@ enum OpCode : ubyte{
|
||||||
@(OpColour("255", "200", "100")) True,
|
@(OpColour("255", "200", "100")) True,
|
||||||
@(OpColour("255", "200", "100")) False,
|
@(OpColour("255", "200", "100")) False,
|
||||||
|
|
||||||
|
@(OpColour("000", "200", "100")) Pop,
|
||||||
|
@(OpColour("000", "200", "150")) GetGlobal,
|
||||||
|
@(OpColour("000", "200", "150")) DefineGlobal,
|
||||||
|
@(OpColour("000", "200", "150")) SetGlobal,
|
||||||
|
|
||||||
@(OpColour("255", "100", "100")) Equal,
|
@(OpColour("255", "100", "100")) Equal,
|
||||||
@(OpColour("255", "100", "100")) Greater,
|
@(OpColour("255", "100", "100")) Greater,
|
||||||
@(OpColour("255", "100", "100")) Less,
|
@(OpColour("255", "100", "100")) Less,
|
||||||
|
|
@ -27,6 +34,7 @@ enum OpCode : ubyte{
|
||||||
|
|
||||||
@(OpColour("200", "100", "100")) Not,
|
@(OpColour("200", "100", "100")) Not,
|
||||||
@(OpColour("100", "100", "200")) Negate,
|
@(OpColour("100", "100", "200")) Negate,
|
||||||
|
@(OpColour("000", "200", "100")) Print,
|
||||||
@(OpColour("000", "200", "100")) Return,
|
@(OpColour("000", "200", "100")) Return,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +63,10 @@ struct Chunk{
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
int addConstant(Value value){
|
int addConstant(Value value){
|
||||||
|
int index = cast(int)constants.values[0 .. constants.count].countUntil(value);
|
||||||
|
if(index >= 0)
|
||||||
|
return index;
|
||||||
|
assert(constants.count <= ubyte.max);
|
||||||
constants.write(value);
|
constants.write(value);
|
||||||
return constants.count - 1;
|
return constants.count - 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,9 @@ struct Compiler{
|
||||||
compilingChunk = chunk;
|
compilingChunk = chunk;
|
||||||
|
|
||||||
parser.advance();
|
parser.advance();
|
||||||
parser.expression();
|
while(!parser.match(Token.Type.EOF)){
|
||||||
parser.consume(Token.Type.EOF, "Expect end of expression.");
|
parser.declaration();
|
||||||
|
}
|
||||||
end();
|
end();
|
||||||
return !parser.hadError;
|
return !parser.hadError;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,16 @@ import clox.util;
|
||||||
|
|
||||||
private int simpleInstruction(alias OpCode op)(const char* name, int offset){
|
private int simpleInstruction(alias OpCode op)(const char* name, int offset){
|
||||||
enum c = getUDAs!(op, OpColour)[0];
|
enum c = getUDAs!(op, OpColour)[0];
|
||||||
printf(colour!("%s\n", c.r, c.g, c.b).ptr, name);
|
printf(colour!("%-27s ", c.r, c.g, c.b).ptr, name);
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
}
|
}
|
||||||
private int constantInstruction(alias OpCode op)(const char* name, Chunk* chunk, int offset){
|
private int constantInstruction(alias OpCode op)(const char* name, Chunk* chunk, int offset){
|
||||||
enum c = getUDAs!(op, OpColour)[0];
|
enum c = getUDAs!(op, OpColour)[0];
|
||||||
ubyte constant = chunk.code[offset + 1];
|
ubyte constant = chunk.code[offset + 1];
|
||||||
printf(ctEval!(colour!("%-16s", c.r, c.g, c.b) ~ " %4d '").ptr, name, constant);
|
printf(ctEval!(colour!("%-14s", c.r, c.g, c.b) ~ colour!(" %4d ", Colour.Black)).ptr, name, constant);
|
||||||
chunk.constants.values[constant].print();
|
int pl = chunk.constants.values[constant].print();
|
||||||
printf("'\n");
|
foreach(i; 0 .. 16-pl)
|
||||||
|
printf(" ");
|
||||||
return offset + 2;
|
return offset + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,11 +27,12 @@ void disassembleChunk(Chunk* chunk, const char* name = "chunk"){
|
||||||
printf(" == %s ==\n", name);
|
printf(" == %s ==\n", name);
|
||||||
for(int offset = 0; offset < chunk.count;){
|
for(int offset = 0; offset < chunk.count;){
|
||||||
offset = disassembleInstruction(chunk, offset);
|
offset = disassembleInstruction(chunk, offset);
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int disassembleInstruction(Chunk* chunk, int offset){
|
int disassembleInstruction(Chunk* chunk, int offset){
|
||||||
printf("%04d ", offset);
|
printf("%5d ", offset);
|
||||||
if(offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1]){
|
if(offset > 0 && chunk.lines[offset] == chunk.lines[offset - 1]){
|
||||||
printf(colour!(" | ", Colour.Black).ptr);
|
printf(colour!(" | ", Colour.Black).ptr);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -39,14 +41,14 @@ int disassembleInstruction(Chunk* chunk, int offset){
|
||||||
ubyte instruction = chunk.code[offset];
|
ubyte instruction = chunk.code[offset];
|
||||||
switch(instruction){
|
switch(instruction){
|
||||||
static foreach(e; EnumMembers!OpCode){
|
static foreach(e; EnumMembers!OpCode){
|
||||||
static if(e == OpCode.Constant){
|
static if(e == OpCode.Constant || e == OpCode.DefineGlobal || e == OpCode.GetGlobal){
|
||||||
case OpCode.Constant: return constantInstruction!(OpCode.Constant)("OP_CONSTANT", chunk, offset);
|
case e: return constantInstruction!(e)(e.stringof, chunk, offset);
|
||||||
} else {
|
} else {
|
||||||
case e:
|
case e:
|
||||||
return simpleInstruction!e(e.stringof, offset);
|
return simpleInstruction!e(e.stringof, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default: printf("Unknown opcode %d\n", instruction);
|
default: printf("Unknown opcode %d", instruction);
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ struct Obj{
|
||||||
static if(type == Type.None) return &this;
|
static if(type == Type.None) return &this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print() const{
|
int print() const{
|
||||||
final switch(type){
|
final switch(type){
|
||||||
case Type.String: printf(`"%s"`, asString.chars.ptr); break;
|
case Type.String: return printf(`"%s"`, asString.chars.ptr); break;
|
||||||
case Type.None: assert(0);
|
case Type.None: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import clox.compiler;
|
||||||
import clox.chunk;
|
import clox.chunk;
|
||||||
import clox.parserules;
|
import clox.parserules;
|
||||||
import clox.util;
|
import clox.util;
|
||||||
|
import clox.object;
|
||||||
|
import clox.value;
|
||||||
|
|
||||||
struct Parser{
|
struct Parser{
|
||||||
Compiler* compiler;
|
Compiler* compiler;
|
||||||
|
|
@ -33,10 +35,72 @@ struct Parser{
|
||||||
}
|
}
|
||||||
errorAtCurrent(message);
|
errorAtCurrent(message);
|
||||||
}
|
}
|
||||||
|
bool check(Token.Type type) => current.type == type;
|
||||||
|
bool match(Token.Type type){
|
||||||
|
if(!check(type))
|
||||||
|
return false;
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void expression(){
|
void expression(){
|
||||||
parsePrecedence(Precedence.Assignment);
|
parsePrecedence(Precedence.Assignment);
|
||||||
}
|
}
|
||||||
|
void expressionStatement(){
|
||||||
|
expression();
|
||||||
|
consume(Token.Type.Semicolon, "Expect ';' after expression.");
|
||||||
|
compiler.emitter.emit(OpCode.Pop);
|
||||||
|
}
|
||||||
|
void varDeclaration(){
|
||||||
|
ubyte global = parseVariable("Expect variable name.");
|
||||||
|
if(match(Token.Type.Equal))
|
||||||
|
expression();
|
||||||
|
else
|
||||||
|
compiler.emitter.emit(OpCode.Nil);
|
||||||
|
consume(Token.Type.Semicolon, "Expect ';' after variable declaration.");
|
||||||
|
defineVariable(global);
|
||||||
|
}
|
||||||
|
void declaration(){
|
||||||
|
if(match(Token.Type.Var))
|
||||||
|
varDeclaration();
|
||||||
|
else
|
||||||
|
statement();
|
||||||
|
if(compiler.parser.panicMode)
|
||||||
|
synchronize();
|
||||||
|
}
|
||||||
|
void statement(){
|
||||||
|
if(match(Token.Type.Print))
|
||||||
|
printStatement();
|
||||||
|
else
|
||||||
|
expressionStatement();
|
||||||
|
}
|
||||||
|
void printStatement(){
|
||||||
|
expression();
|
||||||
|
consume(Token.Type.Semicolon, "Expect ';' after value.");
|
||||||
|
compiler.emitter.emit(OpCode.Print);
|
||||||
|
}
|
||||||
|
|
||||||
|
void defineVariable(ubyte global){
|
||||||
|
compiler.emitter.emit(OpCode.DefineGlobal, global);
|
||||||
|
}
|
||||||
|
ubyte parseVariable(const char* errorMessage){
|
||||||
|
consume(Token.Type.Identifier, errorMessage);
|
||||||
|
return identifierConstant(&previous);
|
||||||
|
}
|
||||||
|
ubyte identifierConstant(Token* name){
|
||||||
|
Value nameVal = Value.from(Obj.String.copy(name.lexeme));
|
||||||
|
return cast(ubyte)compiler.emitter.makeConstant(nameVal);
|
||||||
|
}
|
||||||
|
void namedVariable(Token name, bool canAssign){
|
||||||
|
ubyte arg = compiler.parser.identifierConstant(&name);
|
||||||
|
if(canAssign && match(Token.Type.Equal)){
|
||||||
|
expression();
|
||||||
|
compiler.emitter.emit(OpCode.SetGlobal, arg);
|
||||||
|
} else {
|
||||||
|
compiler.emitter.emit(OpCode.GetGlobal, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void parsePrecedence(Precedence precedence){
|
void parsePrecedence(Precedence precedence){
|
||||||
advance();
|
advance();
|
||||||
ParseFn prefixRule = ParseRule.get(previous.type).prefix;
|
ParseFn prefixRule = ParseRule.get(previous.type).prefix;
|
||||||
|
|
@ -44,14 +108,37 @@ struct Parser{
|
||||||
error("Expect expression.");
|
error("Expect expression.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
prefixRule(compiler);
|
bool canAssign = precedence <= Precedence.Assignment;
|
||||||
|
prefixRule(compiler, canAssign);
|
||||||
while(precedence <= ParseRule.get(current.type).precedence){
|
while(precedence <= ParseRule.get(current.type).precedence){
|
||||||
advance();
|
advance();
|
||||||
ParseFn infixRule = ParseRule.get(previous.type).infix;
|
ParseFn infixRule = ParseRule.get(previous.type).infix;
|
||||||
infixRule(compiler);
|
infixRule(compiler);
|
||||||
}
|
}
|
||||||
|
if(canAssign && match(Token.Type.Equal))
|
||||||
|
error("Invalid assignment target.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void synchronize(){
|
||||||
|
panicMode = false;
|
||||||
|
while(current.type != Token.Type.EOF){
|
||||||
|
if(previous.type == Token.Type.Semicolon)
|
||||||
|
return;
|
||||||
|
switch(current.type){
|
||||||
|
case Token.Type.Class:
|
||||||
|
case Token.Type.Fun:
|
||||||
|
case Token.Type.Var:
|
||||||
|
case Token.Type.For:
|
||||||
|
case Token.Type.If:
|
||||||
|
case Token.Type.While:
|
||||||
|
case Token.Type.Print:
|
||||||
|
case Token.Type.Return:
|
||||||
|
return;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
void errorAtCurrent(in char* message){
|
void errorAtCurrent(in char* message){
|
||||||
errorAt(current, message);
|
errorAt(current, message);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ import clox.emitter;
|
||||||
import clox.value;
|
import clox.value;
|
||||||
import clox.object;
|
import clox.object;
|
||||||
|
|
||||||
alias ParseFn = void function(Compiler* compiler);
|
alias ParseFn = void function(Compiler* compiler, bool canAssign = false);
|
||||||
|
|
||||||
private void grouping(Compiler* compiler){
|
private void grouping(Compiler* compiler, bool _){
|
||||||
compiler.parser.expression();
|
compiler.parser.expression();
|
||||||
compiler.parser.consume(Token.Type.RightParen, "Expect ')' after expression.");
|
compiler.parser.consume(Token.Type.RightParen, "Expect ')' after expression.");
|
||||||
}
|
}
|
||||||
private void unary(Compiler* compiler){
|
private void unary(Compiler* compiler, bool _){
|
||||||
Token operator = compiler.parser.previous;
|
Token operator = compiler.parser.previous;
|
||||||
compiler.parser.parsePrecedence(Precedence.Unary);
|
compiler.parser.parsePrecedence(Precedence.Unary);
|
||||||
compiler.emitter.setLine(operator.line);
|
compiler.emitter.setLine(operator.line);
|
||||||
|
|
@ -24,7 +24,7 @@ private void unary(Compiler* compiler){
|
||||||
default: assert(0);
|
default: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void binary(Compiler* compiler){
|
private void binary(Compiler* compiler, bool _){
|
||||||
Token operator = compiler.parser.previous;
|
Token operator = compiler.parser.previous;
|
||||||
immutable(ParseRule)* rule = ParseRule.get(operator.type);
|
immutable(ParseRule)* rule = ParseRule.get(operator.type);
|
||||||
compiler.parser.parsePrecedence(cast(Precedence)(rule.precedence + 1));
|
compiler.parser.parsePrecedence(cast(Precedence)(rule.precedence + 1));
|
||||||
|
|
@ -48,14 +48,14 @@ private void binary(Compiler* compiler){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void number(Compiler* compiler){
|
private void number(Compiler* compiler, bool _){
|
||||||
import core.stdc.stdlib : strtod;
|
import core.stdc.stdlib : strtod;
|
||||||
Token token = compiler.parser.previous;
|
Token token = compiler.parser.previous;
|
||||||
double value = strtod(token.lexeme.ptr, null);
|
double value = strtod(token.lexeme.ptr, null);
|
||||||
compiler.emitter.setLine(token.line);
|
compiler.emitter.setLine(token.line);
|
||||||
compiler.emitter.emitConstant(Value.from(value));
|
compiler.emitter.emitConstant(Value.from(value));
|
||||||
}
|
}
|
||||||
private void literal(Compiler* compiler){
|
private void literal(Compiler* compiler, bool _){
|
||||||
Token token = compiler.parser.previous;
|
Token token = compiler.parser.previous;
|
||||||
compiler.emitter.setLine(token.line);
|
compiler.emitter.setLine(token.line);
|
||||||
switch(token.type){
|
switch(token.type){
|
||||||
|
|
@ -65,13 +65,16 @@ private void literal(Compiler* compiler){
|
||||||
default: assert(0);
|
default: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void strlit(Compiler* compiler){
|
private void strlit(Compiler* compiler, bool _){
|
||||||
Token token = compiler.parser.previous;
|
Token token = compiler.parser.previous;
|
||||||
const char[] str = token.lexeme[1 .. $-1];
|
const char[] str = token.lexeme[1 .. $-1];
|
||||||
Obj.String* strObj = Obj.String.copy(str);
|
Obj.String* strObj = Obj.String.copy(str);
|
||||||
compiler.emitter.setLine(token.line);
|
compiler.emitter.setLine(token.line);
|
||||||
compiler.emitter.emitConstant(Value.from(strObj));
|
compiler.emitter.emitConstant(Value.from(strObj));
|
||||||
}
|
}
|
||||||
|
private void variable(Compiler* compiler, bool canAssign){
|
||||||
|
compiler.parser.namedVariable(compiler.parser.previous, canAssign);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct ParseRule{
|
struct ParseRule{
|
||||||
|
|
@ -117,7 +120,7 @@ immutable ParseRule[Token.Type.max+1] rules = [
|
||||||
Token.Type.GreaterEqual : ParseRule(null, &binary, Precedence.Comparison),
|
Token.Type.GreaterEqual : ParseRule(null, &binary, Precedence.Comparison),
|
||||||
Token.Type.Less : ParseRule(null, &binary, Precedence.Comparison),
|
Token.Type.Less : ParseRule(null, &binary, Precedence.Comparison),
|
||||||
Token.Type.LessEqual : ParseRule(null, &binary, Precedence.Comparison),
|
Token.Type.LessEqual : ParseRule(null, &binary, Precedence.Comparison),
|
||||||
Token.Type.Identifier : ParseRule(null, null, Precedence.None),
|
Token.Type.Identifier : ParseRule(&variable, null, Precedence.None),
|
||||||
Token.Type.String : ParseRule(&strlit, null, Precedence.None),
|
Token.Type.String : ParseRule(&strlit, null, Precedence.None),
|
||||||
Token.Type.Number : ParseRule(&number, null, Precedence.None),
|
Token.Type.Number : ParseRule(&number, null, Precedence.None),
|
||||||
Token.Type.And : ParseRule(null, null, Precedence.None),
|
Token.Type.And : ParseRule(null, null, Precedence.None),
|
||||||
|
|
|
||||||
|
|
@ -60,12 +60,12 @@ struct Value{
|
||||||
case Type.None: assert(0);
|
case Type.None: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void print() const{
|
int print() const{
|
||||||
final switch(type){
|
final switch(type){
|
||||||
case Type.Number: printf("%g", asNumber); break;
|
case Type.Number: return printf("%g", asNumber);
|
||||||
case Type.Bool: printf(asBoolean ? "true" : "false"); break;
|
case Type.Bool: return printf(asBoolean ? "true" : "false");
|
||||||
case Type.Nil: printf("nil"); break;
|
case Type.Nil: return printf("nil");
|
||||||
case Type.Obj: asObj.print(); break;
|
case Type.Obj: return asObj.print();
|
||||||
case Type.None: assert(0);
|
case Type.None: assert(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ module clox.vm;
|
||||||
import core.stdc.stdio;
|
import core.stdc.stdio;
|
||||||
|
|
||||||
import clox.chunk;
|
import clox.chunk;
|
||||||
|
import clox.util;
|
||||||
import clox.dbg;
|
import clox.dbg;
|
||||||
import clox.object;
|
import clox.object;
|
||||||
import clox.value;
|
import clox.value;
|
||||||
|
|
@ -18,16 +19,19 @@ struct VM{
|
||||||
Chunk* chunk;
|
Chunk* chunk;
|
||||||
ubyte* ip;
|
ubyte* ip;
|
||||||
Stack!(Value, 256) stack;
|
Stack!(Value, 256) stack;
|
||||||
|
Table globals;
|
||||||
Table strings;
|
Table strings;
|
||||||
Obj* objects;
|
Obj* objects;
|
||||||
enum InterpretResult{ Ok, CompileError, RuntimeError }
|
enum InterpretResult{ Ok, CompileError, RuntimeError }
|
||||||
void initialise(){
|
void initialise(){
|
||||||
stack.reset();
|
stack.reset();
|
||||||
strings.initialise();
|
strings.initialise();
|
||||||
|
globals.initialise();
|
||||||
}
|
}
|
||||||
void free(){
|
void free(){
|
||||||
freeObjects();
|
freeObjects();
|
||||||
strings.free();
|
strings.free();
|
||||||
|
globals.free();
|
||||||
}
|
}
|
||||||
InterpretResult interpret(const char* source){
|
InterpretResult interpret(const char* source){
|
||||||
Chunk cnk;
|
Chunk cnk;
|
||||||
|
|
@ -53,7 +57,8 @@ struct VM{
|
||||||
InterpretResult run(){
|
InterpretResult run(){
|
||||||
ubyte readByte() => *vm.ip++;
|
ubyte readByte() => *vm.ip++;
|
||||||
auto readConstant() => chunk.constants.values[readByte()];
|
auto readConstant() => chunk.constants.values[readByte()];
|
||||||
Value peek(int distance) => stack.top[-1 - distance];
|
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);
|
bool checkBinaryType(alias type)() => peek(0).isType(type) && peek(1).isType(type);
|
||||||
bool checkSameType()() => peek(0).type == peek(1).type;
|
bool checkSameType()() => peek(0).type == peek(1).type;
|
||||||
int binaryOp(string op, alias check, string checkMsg, alias pre)(){
|
int binaryOp(string op, alias check, string checkMsg, alias pre)(){
|
||||||
|
|
@ -68,14 +73,14 @@ struct VM{
|
||||||
}
|
}
|
||||||
while(true){
|
while(true){
|
||||||
debug(traceExec){
|
debug(traceExec){
|
||||||
|
disassembleInstruction(vm.chunk, cast(int)(vm.ip - vm.chunk.code));
|
||||||
printf(" ");
|
printf(" ");
|
||||||
foreach(slot; stack.live){
|
foreach(slot; stack.live){
|
||||||
printf("[ ");
|
printf("");
|
||||||
slot.print();
|
slot.print();
|
||||||
printf(" ]");
|
printf(colour!(", ", Colour.Black).ptr);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
disassembleInstruction(vm.chunk, cast(int)(vm.ip - vm.chunk.code));
|
|
||||||
}
|
}
|
||||||
OpCode instruction;
|
OpCode instruction;
|
||||||
opSwitch: final switch(instruction = cast(OpCode)readByte()){
|
opSwitch: final switch(instruction = cast(OpCode)readByte()){
|
||||||
|
|
@ -86,6 +91,30 @@ struct VM{
|
||||||
case OpCode.Nil: stack.push(Value.nil); break;
|
case OpCode.Nil: stack.push(Value.nil); break;
|
||||||
case OpCode.True: stack.push(Value.from(true)); break;
|
case OpCode.True: stack.push(Value.from(true)); break;
|
||||||
case OpCode.False: stack.push(Value.from(false)); break;
|
case OpCode.False: stack.push(Value.from(false)); break;
|
||||||
|
case OpCode.Pop: stack.pop(); break;
|
||||||
|
|
||||||
|
case OpCode.GetGlobal:
|
||||||
|
Obj.String* name = readString();
|
||||||
|
Value value;
|
||||||
|
if(!globals.get(name, value)){
|
||||||
|
runtimeError("Undefined variable '%s'.", name.chars.ptr);
|
||||||
|
return InterpretResult.RuntimeError;
|
||||||
|
}
|
||||||
|
stack.push(value);
|
||||||
|
break;
|
||||||
|
case OpCode.DefineGlobal:
|
||||||
|
Obj.String* name = readString();
|
||||||
|
globals.set(name, peek(0));
|
||||||
|
stack.pop();
|
||||||
|
break;
|
||||||
|
case OpCode.SetGlobal:
|
||||||
|
Obj.String* name = readString();
|
||||||
|
if(globals.set(name, peek(0))){
|
||||||
|
globals.del(name);
|
||||||
|
runtimeError("Undefined variable '%s'.", name.chars.ptr);
|
||||||
|
return InterpretResult.RuntimeError;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
static foreach(k, op; [ OpCode.Equal: "==", OpCode.NotEqual: "!=" ]){
|
static foreach(k, op; [ OpCode.Equal: "==", OpCode.NotEqual: "!=" ]){
|
||||||
case k:
|
case k:
|
||||||
|
|
@ -125,9 +154,11 @@ struct VM{
|
||||||
}
|
}
|
||||||
stack.push(Value.from(-stack.pop().asNumber));
|
stack.push(Value.from(-stack.pop().asNumber));
|
||||||
break;
|
break;
|
||||||
case OpCode.Return:
|
case OpCode.Print:
|
||||||
stack.pop().print();
|
stack.pop().print();
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
break;
|
||||||
|
case OpCode.Return:
|
||||||
return InterpretResult.Ok;
|
return InterpretResult.Ok;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue