aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Winston2023-05-21 16:20:41 -0400
committerBen Winston2023-05-21 16:20:41 -0400
commit618de4c70d8916f64781997f3ae538e3e6109d00 (patch)
treebcd8ee70d82402407399a49c9cf6914858bd9449
parentb1183af95f45ba0162a91f7a308a4846418f03be (diff)
if/else, 'and' control flow
-rw-r--r--chunk.d3
-rw-r--r--compiler.d116
-rw-r--r--dbg.d13
-rw-r--r--parser.d94
-rw-r--r--vm.d66
5 files changed, 213 insertions, 79 deletions
diff --git a/chunk.d b/chunk.d
index d6a9b5a..b3209a8 100644
--- a/chunk.d
+++ b/chunk.d
@@ -48,6 +48,9 @@ enum OpCode {
OP_POP_SCOPE,
OP_SUBTRACT,
OP_NIL,
+
+ OP_JUMP,
+ OP_JUMP_IF_FALSE,
}
class Chunk {
diff --git a/compiler.d b/compiler.d
index a7a5bfa..c05aeaa 100644
--- a/compiler.d
+++ b/compiler.d
@@ -92,13 +92,9 @@ class Compiler {
int parseVariable(Def def) {
- writefln("in parseVariable(), scopeDepth is %d", scopeDepth);
-
declareVariable(def.name);
if (scopeDepth > 0) {
return 0;
- //return localCount - 1;
- //return localCount;
}
int addr = func.chunk.addConstant(makeStringValue(def.name.name));
@@ -131,7 +127,6 @@ class Compiler {
}
}
- writefln("> > > creating a local '%s' at %d", sym.name, scopeDepth);
Local loc = Local(sym, scopeDepth);
if (localCount == locals.length) {
locals ~= loc;
@@ -139,7 +134,6 @@ class Compiler {
locals[localCount] = loc;
}
localCount++;
- writefln("localcount is now %d", localCount);
}
void compileDef(Form form) {
@@ -153,27 +147,16 @@ class Compiler {
// are we setting a local?
if (scopeDepth > 0) {
- //func.chunk.writeOp(OpCode.OP_SET_LOCAL, current.line);
func.chunk.writeOp(OpCode.OP_DEF_LOCAL, current.line);
- //func.chunk.writeOp(to!ubyte(addr), current.line);
}
// define the variable
defineVariable(addr);
- /*
- func.chunk.writeOp(OpCode.OP_DEFINE_GLOBAL, current.line);
- func.chunk.writeOp(to!ubyte(addr), current.line);
- */
-
- // is this right?
- //func.chunk.writeOp(OpCode.OP_POP, def.line);
}
int resolveLocal(Symbol sym) {
- //for (int i = localCount - 1; i >= 0; i--) {
for (int i = localCount - 1; i >= 0; i--) {
Local local = locals[i];
- writefln(" > > looping, looking at '%s' (%d) (depth: %d) (resolving: %s)", local.sym.name, i, local.depth, sym.name);
if (local.sym.name == sym.name) {
return i;
}
@@ -185,32 +168,80 @@ class Compiler {
void compileSymbol(Form form) {
Symbol sym = cast(Symbol)form;
- //OpCode getOp;
- //OpCode setOp;
int arg = resolveLocal(sym);
if (arg != -1) {
- writeln("compiling a LOCAL symbol");
- //getOp = OpCode.OP_GET_LOCAL;
func.chunk.writeOp(OpCode.OP_GET_LOCAL, sym.line);
- //setOp = OpCode.OP_SET_LOCAL; // do we need this here
} else {
- writeln("compiling a GLOBAL symbol");
arg = func.chunk.addConstant(makeStringValue(sym.name));
- //getOp = OpCode.OP_GET_GLOBAL;
func.chunk.writeOp(OpCode.OP_GET_GLOBAL, sym.line);
- //setOp = OpCode.OP_SET_GLOBAL; // do we need this here
}
- // add the symbol name to the chunk
- //int addr = func.chunk.addConstant(makeStringValue(sym.name));
-
// get the variable
- //func.chunk.writeOp(getOp, sym.line);
func.chunk.writeOp(to!ubyte(arg), sym.line);
//advance();
}
+ int jump(OpCode type) {
+ func.chunk.writeOp(to!ubyte(type), current.line);
+ func.chunk.writeOp(0xff, current.line);
+ func.chunk.writeOp(0xff, current.line);
+ return to!int(func.chunk.code.length) - 2;
+ }
+
+ void patchJump(int offset) {
+ // additional -2 to account for the 2 byte jump itself
+ int jmp = to!int(func.chunk.code.length) - offset - 2;
+
+ // TODO check to make sure we didn't jump too far?
+
+ this.func.chunk.code[offset] = (jmp >> 8) & 0xff;
+ this.func.chunk.code[offset + 1] = jmp & 0xff;
+ }
+
+ void compileIf(Form form) {
+ If if_ = cast(If)form;
+
+ resolve(if_.cond);
+
+ int thenJump = jump(OpCode.OP_JUMP_IF_FALSE);
+ this.func.chunk.writeOp(OpCode.OP_POP, if_.line);
+
+ resolve(if_.thenForm);
+
+ int elseJump = jump(OpCode.OP_JUMP);
+
+ patchJump(thenJump);
+ this.func.chunk.writeOp(OpCode.OP_POP, if_.line);
+
+ if (if_.hasElse) {
+ resolve(if_.elseForm);
+ }
+
+ patchJump(elseJump);
+
+ }
+
+ void compileAnd(Form form) {
+ And and_ = cast(And)form;
+
+ resolve(and_.clauses[0]);
+ int[] jumps;
+ jumps ~= jump(OpCode.OP_JUMP_IF_FALSE);
+ int count = 1;
+ while (count < and_.clauses.length) {
+ this.func.chunk.writeOp(OpCode.OP_POP, and_.line);
+ resolve(and_.clauses[count]);
+ jumps ~= jump(OpCode.OP_JUMP_IF_FALSE);
+ count++;
+ }
+
+ // patch all the jumps
+ foreach (int jmp; jumps) {
+ patchJump(jmp);
+ }
+ }
+
void compileBlock(Form form) {
Block block = cast(Block)form;
beginScope();
@@ -226,14 +257,10 @@ class Compiler {
void endScope() {
- writefln("> > ending scope at depth %d", scopeDepth);
scopeDepth--;
while (localCount > 0 &&
locals[localCount - 1].depth > scopeDepth) {
- writeln("> > looping, gonna pop");
- //func.chunk.writeOp(to!ubyte(OpCode.OP_POP_SCOPE), -1);
- //func.chunk.writeOp(to!ubyte(OpCode.OP_POP), -1);
func.chunk.writeOp(OpCode.OP_POPB, -1);
localCount--;
}
@@ -274,8 +301,7 @@ class Compiler {
}
void resolve(Form form) {
- write("resolving: ");
- printForm(form);
+ //printForm(form);
switch(form.type) {
case FormType.ATOM:
this.compileAtom(form);
@@ -288,15 +314,19 @@ class Compiler {
break;
case FormType.DEF:
this.compileDef(form);
- writeln("DONE COMPILING DEF");
break;
case FormType.SYMBOL:
this.compileSymbol(form);
break;
case FormType.BLOCK:
- writeln("COMPILING BLOCK");
this.compileBlock(form);
break;
+ case FormType.IF:
+ this.compileIf(form);
+ break;
+ case FormType.AND:
+ this.compileAnd(form);
+ break;
default:
write("not sure how to resolve: ");
printForm(form);
@@ -306,28 +336,16 @@ class Compiler {
}
Function compile() {
- writeln("compiling");
advance();
while(current.type != FormType.EOF) {
- writeln("IN COMPILE LOOP");
resolve(current);
- writeln("BOTTOM OF COMPILE LOOP");
- if (current.type == FormType.EOF) {
- writeln("got an EOF");
- }
}
- writeln("outside compile loop");
-
return finish();
}
Function finish() {
- //emitReturn();
- writeln("finishing the compiler!");
-
this.func.chunk.writeOp(OpCode.OP_RETURN, current.line);
- writeln("wrote a return code");
return func;
}
diff --git a/dbg.d b/dbg.d
index 89ae8c1..e14e9fe 100644
--- a/dbg.d
+++ b/dbg.d
@@ -1,5 +1,6 @@
import std.stdio;
import std.string;
+import std.conv;
import chunk;
import parser;
@@ -83,6 +84,13 @@ string atomAsString(Atom a) {
return printableValue(a.value);
}
+int jumpInstruction(string message, int sign, Chunk chunk, int offset) {
+ uint jump = to!uint(chunk.code[offset + 1] << 8);
+ jump |= chunk.code[offset + 2];
+ writefln("%-16s %4d -> %d", message, offset, offset + 3 + sign * jump);
+ return offset + 3;
+}
+
int byteInstruction(string message, Chunk chunk, int offset) {
ubyte slot = chunk.code[offset + 1];
writefln("%-16s %4d", message, slot);
@@ -135,6 +143,11 @@ int disassemble(Chunk chunk, int offset) {
return simpleInstruction("OP_RETURN", offset);
case OpCode.OP_NIL:
return simpleInstruction("OP_NIL", offset);
+ case OpCode.OP_JUMP:
+ return jumpInstruction("OP_JUMP", 1, chunk, offset);
+ case OpCode.OP_JUMP_IF_FALSE:
+ return jumpInstruction("OP_JUMP_IF_FALSE", 1, chunk, offset);
+
default:
writeln("unknown opcode?");
return offset + 1;
diff --git a/parser.d b/parser.d
index 80e1626..3dd32ef 100644
--- a/parser.d
+++ b/parser.d
@@ -14,6 +14,8 @@ enum FormType {
FUNC,
DEF,
BLOCK,
+ IF,
+ AND,
EOF,
PARSE_ERROR
@@ -134,6 +136,46 @@ class Cons : Form {
}
}
+class If : Form {
+
+ Form cond;
+ Form thenForm;
+ Form elseForm;
+ bool hasElse;
+
+ this(int line, Form cond, Form then) {
+ this.line = line;
+ this.type = FormType.IF;
+ this.cond = cond;
+ this.thenForm = then;
+ this.hasElse = false;
+ }
+
+ this(int line, Form cond, Form then, Form else_) {
+ this.line = line;
+ this.type = FormType.IF;
+ this.cond = cond;
+ this.thenForm = then;
+ this.elseForm = else_;
+ this.hasElse = true;
+ }
+
+}
+
+class And : Form {
+
+ Form[] clauses;
+
+ this(int line) {
+ this.line = line;
+ this.type = FormType.AND;
+ }
+
+ void addClause(Form clause) {
+ clauses ~= clause;
+ }
+}
+
class Func : Form {
Symbol name;
@@ -252,7 +294,6 @@ class Parser {
int line;
bool peekable() {
- writefln("pos %d source %d", pos, source.length);
return (pos < source.length);
}
@@ -260,14 +301,14 @@ class Parser {
if (pos < source.length) {
return source[pos];
} else {
- writeln("peek returns null");
+ //writeln("peek returns null");
return '\0';
}
}
char advance() {
if (!peekable()) {
- writeln("trying to advance() beyond the end!!!");
+ //writeln("trying to advance() beyond the end!!!");
return '\0';
} else {
char ret = peek();
@@ -288,7 +329,6 @@ class Parser {
if (peek() == '\n') {
line++;
}
- //pos++;
advance();
}
}
@@ -365,8 +405,6 @@ class Parser {
advance(); // closing paren
- writefln("ending DEF pos %d", pos);
-
return def;
}
@@ -405,6 +443,43 @@ class Parser {
return func;
}
+ Form parseIf() {
+ If if_;
+ Form cond = parseForm();
+ Form then = parseForm();
+ if(peekable() && peek() == ')') {
+ if_ = new If(cond.line, cond, then);
+ } else {
+ Form else_ = parseForm();
+ if_ = new If(cond.line, cond, then, else_);
+ }
+
+ advance(); // consume closing paren
+
+ return if_;
+ }
+
+ Form parseAnd() {
+ And and_ = new And(line);
+ char next;
+ while(peekable()) {
+ next = peek();
+ if (next == ')') {
+ break;
+ }
+ and_.addClause(parseForm());
+ }
+
+ if (!peekable()) {
+ return new ParseError("unterminated and", line);
+ }
+
+ advance(); // consume closing paren
+
+ return and_;
+
+ }
+
Form parseBlock() {
Block block = new Block(line);
char next;
@@ -420,7 +495,6 @@ class Parser {
return new ParseError("unterminated block", line);
}
- writefln("ending block pos %d", pos);
advance(); // consume closing paren
return block;
@@ -448,6 +522,10 @@ class Parser {
return parseDef();
case "block":
return parseBlock();
+ case "if":
+ return parseIf();
+ case "and":
+ return parseAnd();
default:
break;
}
@@ -496,8 +574,6 @@ class Parser {
return parseSymbol();
}
- writeln("FELL THROUGH ON PARSE FORM");
-
advance();
return new Atom(0, -1);
diff --git a/vm.d b/vm.d
index 99016f2..45f3742 100644
--- a/vm.d
+++ b/vm.d
@@ -31,7 +31,6 @@ class VM {
Value peekA(int offset) {
if (offset >= aTop) {
- //if (offset > this.topOffset) {
writefln("offset of %d greater than stack size %d", offset, aTop);
}
return this.aStack[aTop - offset - 1];
@@ -79,6 +78,16 @@ class VM {
return this.func.chunk.code[this.ip++];
}
+ uint readShort() {
+ ip += 2;
+ uint high = this.func.chunk.code[ip - 2] << 8;
+ uint low = this.func.chunk.code[ip - 1];
+ return high | low;
+
+
+ //return 0;
+ }
+
bool isNumber(Value value) {
return value.type == ValueType.NUMBER;
}
@@ -87,6 +96,10 @@ class VM {
return value.type == ValueType.STRING;
}
+ bool isBoolean(Value value) {
+ return value.type == ValueType.BOOLEAN;
+ }
+
double asNumber(Value value) {
return value.as.number;
}
@@ -95,9 +108,20 @@ class VM {
return value.as.str;
}
+ bool asBoolean(Value value) {
+ return value.as.boolean;
+ }
+
+ /*
+ bool isFalsey(Value val) {
+ return isBoolean(val) && val.as.boolean;
+ }
+ */
+
InterpretResult run() {
- //int ip = 0; // TODO this is wrong
+ writeln("== VM running ==");
while (true) {
+ writeln(" Stacks:");
write(" A> ");
for (int i = 0; i < aTop; i++) {
writef("[ %s ]", printableValue(aStack[i]));
@@ -130,14 +154,12 @@ class VM {
globals[asString(name)] = val;
break;
case OpCode.OP_GET_GLOBAL:
- writeln("in OP_GET_GLOBAL");
Value name = func.chunk.constants[readByte()];
writefln(asString(name));
if (!isString(name)) {
writefln("variables must be strings, got %s", printableValue(name));
return InterpretResult.RUNTIME_ERROR; // TODO error
}
- writefln(asString(name));
Value* member = asString(name) in globals;
if (member is null) {
writefln("no such variable: %s", printableValue(name));
@@ -146,31 +168,17 @@ class VM {
pushA(*member); // do i need to dereference?
break;
case OpCode.OP_DEF_LOCAL:
- //Value name = func.chunk.constants[readByte()];
- writefln("in OP_DEF_LOCAL");
- //Value val = peekA(0);
Value val = popA();
pushB(val);
break;
-
-
case OpCode.OP_GET_LOCAL:
ubyte slot = readByte();
- writefln("getting local '%d' stackTop '%d'", slot, bTop);
- //push(stack[topOffset + slot - 2]);
- //push(stack[topOffset + slot]);
- pushA(bStack[bTop - slot - 1]); // NOTE change to bStack
- //push(stack[slot]);
+ pushA(bStack[bTop - slot - 1]);
break;
case OpCode.OP_SET_LOCAL:
- //writeln("in SET_LOCAL");
ubyte slot = readByte();
- //stack[slot] = peek(0);
- bStack[bTop + slot - 1] = peekA(0); // NOTE change to bStack
- popA(); // NOTE changed to bStack
- //pop();
- //stack[slot] = pop();
- writefln("setting local '%d' stackTop '%d'", slot, bTop);
+ bStack[bTop + slot - 1] = peekA(0);
+ popA();
break;
case OpCode.OP_CONSTANT:
Value constant = this.func.chunk.constants[this.readByte()];
@@ -244,6 +252,22 @@ class VM {
popA(); // throw this one away
pushA(val);
break;
+
+ case OpCode.OP_JUMP:
+ uint offset = readShort();
+ ip += offset;
+ break;
+
+ case OpCode.OP_JUMP_IF_FALSE:
+ uint offset = readShort();
+ if (!isBoolean(peekA(0))) {
+ writeln("if expects a boolean condition");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ if (!asBoolean(peekA(0))) {
+ ip += offset;
+ }
+ break;
default:
writeln("unknown opcode to run");
break;