aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Winston2023-05-20 15:12:30 -0400
committerBen Winston2023-05-20 15:12:30 -0400
commit0c70372774297272dd14133d48e40e9a3624420a (patch)
tree4e66fb1b97a0fd537a20dca4ddcd26003f83c94d
parent6902cc5abe09da9f6f2d86f22d06684d97cfa9f3 (diff)
initial commit of compiler/VM with a couple basic instructions
-rw-r--r--chunk.d2
-rw-r--r--compiler.d166
-rw-r--r--dbg.d55
-rw-r--r--main.d90
-rw-r--r--parser.d45
-rw-r--r--vm.d146
6 files changed, 434 insertions, 70 deletions
diff --git a/chunk.d b/chunk.d
index 56b960d..f6dce01 100644
--- a/chunk.d
+++ b/chunk.d
@@ -33,10 +33,12 @@ class Function : Obj {
enum OpCode {
OP_ADD,
+ OP_LESS,
OP_NEGATE,
OP_RETURN,
OP_CONSTANT,
OP_POP,
+ OP_SUBTRACT,
OP_NIL,
}
diff --git a/compiler.d b/compiler.d
new file mode 100644
index 0000000..51e45ba
--- /dev/null
+++ b/compiler.d
@@ -0,0 +1,166 @@
+import std.stdio;
+import std.string;
+
+import parser;
+import chunk;
+import dbg;
+
+enum FunctionType {
+ FUNCTION,
+ SCRIPT,
+}
+
+struct Local {
+ Symbol sym;
+ int depth;
+}
+
+class Compiler {
+
+ Function func;
+ FunctionType type;
+ Parser* parser;
+ Local[] locals;
+ int scopeDepth;
+ Form current;
+ Form previous;
+ Form outer;
+ int outerIdx;
+
+ void advance() {
+ previous = current;
+ /*
+ if (outer.type != FormType.EOF && outerIdx < outer) {
+ current = parser.parseForm();
+ } else {
+
+ }
+ */
+ current = parser.parseForm();
+ }
+
+ void compileNil(Form form) {
+ form.compile(func);
+ advance();
+ }
+
+ void compileAtom(Form form) {
+ form.compile(func);
+ advance();
+ }
+
+ void compileAdd(Form[] args) {
+ resolve(args[0]);
+
+ // (+ n) always returns n
+ if (args.length == 1) {
+ return;
+ }
+
+ for (int i = 1; i < args.length; i++) {
+ resolve(args[i]);
+ func.chunk.writeOp(OpCode.OP_ADD, current.line);
+ }
+ }
+
+ void compileNegate(Form arg) {
+ resolve(arg);
+ func.chunk.writeOp(OpCode.OP_NEGATE, current.line);
+ }
+
+ void compileSubtract(Form[] args) {
+ resolve(args[0]);
+ for (int i = 1; i < args.length; i++) {
+ resolve(args[i]);
+ func.chunk.writeOp(OpCode.OP_SUBTRACT, current.line);
+ }
+ }
+
+ void compileLess(Form[] args) {
+ if (args.length != 2) {
+ writeln("'<' requires 2 arguments");
+ return;
+ }
+ resolve(args[0]);
+ resolve(args[1]);
+ func.chunk.writeOp(OpCode.OP_LESS, current.line);
+ }
+
+ void compileCons(Form form) {
+ Cons cons = cast(Cons)form;
+ Form head = cons.head;
+ if (head.type != FormType.SYMBOL) {
+ writeln("cons must start with a symbol");
+ return;
+ }
+
+ Symbol sym = cast(Symbol)head;
+ switch (sym.name) {
+ case "+":
+ compileAdd(cons.tail);
+ break;
+ case "-":
+ if (cons.tail.length == 1) {
+ compileNegate(cons.tail[0]);
+ } else {
+ compileSubtract(cons.tail);
+ }
+ break;
+ case "<":
+ compileLess(cons.tail);
+ break;
+ default:
+ writefln("unsure how to compile %s", sym.name);
+ advance();
+ break;
+ }
+
+ }
+
+ void resolve(Form form) {
+ write("resolving: ");
+ printForm(form);
+ switch(form.type) {
+ case FormType.ATOM:
+ this.compileAtom(form);
+ break;
+ case FormType.CONS:
+ this.compileCons(form);
+ break;
+ case FormType.NIL:
+ this.compileNil(form);
+ break;
+ default:
+ write("not sure how to resolve: ");
+ printForm(form);
+ advance();
+ break;
+ }
+ }
+
+ Function compile() {
+ writeln("compiling");
+ advance();
+ while(current.type != FormType.EOF) {
+ resolve(current);
+ }
+
+ 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;
+ }
+
+ this(FunctionType type, Parser* parser) {
+ this.parser = parser;
+ this.func = new Function();
+ this.type = type;
+ locals ~= Local(new Symbol("", -1), 0);
+ }
+}
diff --git a/dbg.d b/dbg.d
index 4af02a6..dba6070 100644
--- a/dbg.d
+++ b/dbg.d
@@ -2,11 +2,58 @@ import std.stdio;
import std.string;
import chunk;
-import parser : Value, Atom, ValueType;
+import parser;
//import parser;
//import dbg;
//import value;
+void printForm(Form f, string prefix = "") {
+ switch (f.type) {
+ case FormType.EOF:
+ writeln("eof");
+ return;
+ case FormType.ATOM:
+ writefln("%s atom: %s", prefix, atomAsString(cast(Atom)f));
+ break;
+ case FormType.CONS:
+ Cons c = cast(Cons)f;
+ writef("%s cons <", prefix);
+ if (c.evaluate) {
+ writeln("true>");
+ } else {
+ writeln("false>");
+ }
+
+ printForm(c.head, format("%s>", prefix));
+ foreach (Form i ; c.tail) {
+ printForm(i, format("%s>", prefix));
+ }
+ break;
+ case FormType.NIL:
+ writefln("%s NIL", prefix);
+ break;
+ case FormType.PARSE_ERROR:
+ ParseError pe = cast(ParseError)f;
+ writefln("ERROR: %s", pe.message);
+ break;
+ case FormType.SYMBOL:
+ Symbol s = cast(Symbol)f;
+ writefln("%s sym: %s", prefix, s.name);
+ break;
+ case FormType.FUNC:
+ Func func = cast(Func)f;
+ writefln("%s <fn %s>", prefix, func.name.name);
+ printForm(func.args, format("%s -", prefix));
+ writefln("%s with %d body lines", prefix, func.funcBody.length);
+ writefln("%s <end fn %s>", prefix, func.name.name);
+ break;
+ default:
+ writeln("printFormDefault");
+ break;
+ }
+
+}
+
string printableValue(Value val) {
switch (val.type) {
case ValueType.STRING:
@@ -48,6 +95,10 @@ int disassemble(Chunk chunk, int offset) {
switch (inst) {
case OpCode.OP_ADD:
return simpleInstruction("OP_ADD", offset);
+ case OpCode.OP_LESS:
+ return simpleInstruction("OP_LESS", offset);
+ case OpCode.OP_SUBTRACT:
+ return simpleInstruction("OP_SUBTRACT", offset);
case OpCode.OP_CONSTANT:
return constantInstruction("OP_CONSTANT", chunk, offset);
case OpCode.OP_NEGATE:
@@ -56,6 +107,8 @@ int disassemble(Chunk chunk, int offset) {
return simpleInstruction("OP_POP", offset);
case OpCode.OP_RETURN:
return simpleInstruction("OP_RETURN", offset);
+ case OpCode.OP_NIL:
+ return simpleInstruction("OP_NIL", offset);
default:
writeln("unknown opcode?");
return offset + 1;
diff --git a/main.d b/main.d
index bd1d2b0..0bdc74b 100644
--- a/main.d
+++ b/main.d
@@ -3,6 +3,10 @@ import std.string;
import parser;
import dbg;
+import chunk;
+import compiler;
+import vm;
+
/*
import compiler;
import obj;
@@ -11,53 +15,6 @@ import chunk;
import vm;
*/
-void printForm(Form f, string prefix) {
- switch (f.type) {
- case FormType.EOF:
- writeln("eof");
- return;
- case FormType.ATOM:
- writefln("%s atom: %s", prefix, atomAsString(cast(Atom)f));
- break;
- case FormType.CONS:
- Cons c = cast(Cons)f;
- writef("%s cons <", prefix);
- if (c.evaluate) {
- writeln("true>");
- } else {
- writeln("false>");
- }
-
- printForm(c.head, format("%s>", prefix));
- foreach (Form i ; c.tail) {
- printForm(i, format("%s>", prefix));
- }
- break;
- case FormType.NIL:
- writefln("%s NIL", prefix);
- break;
- case FormType.PARSE_ERROR:
- ParseError pe = cast(ParseError)f;
- writefln("ERROR: %s", pe.message);
- break;
- case FormType.SYMBOL:
- Symbol s = cast(Symbol)f;
- writefln("%s sym: %s", prefix, s.name);
- break;
- case FormType.FUNC:
- Func func = cast(Func)f;
- writefln("%s <fn %s>", prefix, func.name.name);
- printForm(func.args, format("%s -", prefix));
- writefln("%s with %d body lines", prefix, func.funcBody.length);
- writefln("%s <end fn %s>", prefix, func.name.name);
- break;
- default:
- writeln("printFormDefault");
- break;
- }
-
-}
-
void repl() {
while(true) {
@@ -69,37 +26,36 @@ void repl() {
}
Parser parser = new Parser(input);
+
+ /*
+ Chunk chunk = new Chunk();
Form f;
while (true) {
f = parser.parseForm();
-
printForm(f, "");
+
+ f.compile(chunk);
+
+
if (f.type == FormType.EOF) {
break;
}
+ }
+ */
+ Compiler compiler = new Compiler(FunctionType.SCRIPT, &parser);
+ Function func = compiler.compile();
- /*
- if (is(typeof(f) == Eof)) {
- writeln("eof");
- break;
- } else if (is(typeof(f) == Atom)) {
- writeln("atom");
- } else {
- writeln("other");
- }
- */
+ VM vm = new VM(func);
+ vm.run();
- /*
- if (typeof(f) == EOF) {
- writeln("reached the end");
- break;
- } else if (typeof(f) == ParseError) {
- writefln("got a parse error: %s", f.message);
- break;
- }
- */
+ /*
+ writeln("== disassembling chunk ==");
+ int off = 0;
+ while (off < func.chunk.code.length) {
+ off = disassemble(func.chunk, off);
}
+ */
/*
Compiler compiler = new Compiler(lex);
diff --git a/parser.d b/parser.d
index 71b835b..20231c9 100644
--- a/parser.d
+++ b/parser.d
@@ -28,6 +28,20 @@ abstract class Form {
FormType type;
bool evaluate;
int line;
+
+ //ubyte compile(Chunk chunk);
+ //ubyte[] asBytecode();
+ //abstract void compile(Chunk chunk);
+ void compile(Chunk chunk) {
+ writeln("writing default op");
+ chunk.writeOp(OpCode.OP_NIL, line);
+ }
+
+ void compile(Function func) {
+ writeln("writing default op");
+ func.chunk.writeOp(OpCode.OP_NIL, line);
+ }
+
}
class Symbol : Form {
@@ -70,6 +84,21 @@ class Cons : Form {
Form[] tail;
int argCount;
+ override void compile(Chunk chunk) {
+ if (type == FormType.NIL) {
+ writeln("writing nil on purpose");
+ chunk.writeOp(OpCode.OP_NIL, line);
+ return;
+ }
+
+ // TODO writing head first is almost certainly wrong
+ // it will depend on what head is
+ head.compile(chunk);
+ foreach (Form f ; tail) {
+ f.compile(chunk);
+ }
+ }
+
this(int line) {
this.line = line;
this.type = FormType.NIL;
@@ -108,6 +137,18 @@ class Func : Form {
class Atom : Form {
Value value;
+ override void compile(Chunk chunk) {
+ chunk.writeOp(OpCode.OP_CONSTANT, line);
+ int idx = chunk.addConstant(value);
+ chunk.writeOp(to!ubyte(idx), line);
+ }
+
+ override void compile(Function func) {
+ func.chunk.writeOp(OpCode.OP_CONSTANT, line);
+ int idx = func.chunk.addConstant(value);
+ func.chunk.writeOp(to!ubyte(idx), line);
+ }
+
this(string value, int line) {
this.value = makeStringValue(value);
this.line = line;
@@ -165,13 +206,13 @@ Value makeNumberValue(double number) {
Value makeBooleanValue(bool boolean) {
As as = { boolean: boolean };
- Value val = { ValueType.BOOLEAN };
+ Value val = { ValueType.BOOLEAN, as };
return val;
}
Value makeObjValue(Obj obj) {
As as = { obj: obj };
- Value val = { ValueType.OBJ };
+ Value val = { ValueType.OBJ, as };
return val;
}
diff --git a/vm.d b/vm.d
new file mode 100644
index 0000000..6011928
--- /dev/null
+++ b/vm.d
@@ -0,0 +1,146 @@
+import std.stdio;
+import std.string;
+
+import chunk;
+import parser;
+//import value;
+//import obj;
+import dbg;
+
+enum InterpretResult {
+ OK,
+ COMPILE_ERROR,
+ RUNTIME_ERROR,
+}
+
+class VM {
+ int topOffset = 0;
+ ubyte ip = 0;
+ Value[] stack;
+ //ObjFunction func;
+ Function func;
+
+ this(Function func) {
+ this.func = func;
+ }
+
+ Value peek(int offset) {
+ if (offset >= this.topOffset) {
+ writefln("offset of %d greater than stack size %d", offset, topOffset);
+ }
+ return this.stack[topOffset - offset - 1];
+ }
+
+ Value pop() {
+ if (this.topOffset > 0) {
+ this.topOffset--;
+ } else {
+ writeln("pop() on an empty stack!!");
+ }
+ return this.stack[this.topOffset];
+ }
+
+ //InterpretResult push(Value value) {
+ void push(Value value) {
+ if (this.stack.length > this.topOffset) {
+ this.stack[topOffset] = value;
+ } else {
+ this.stack ~= value;
+ }
+ this.topOffset++;
+ }
+
+ ubyte readByte() {
+ return this.func.chunk.code[this.ip++];
+ }
+
+ bool isNumber(Value value) {
+ return value.type == ValueType.NUMBER;
+ }
+
+ double asNumber(Value value) {
+ return value.as.number;
+ }
+
+ InterpretResult run() {
+ //int ip = 0; // TODO this is wrong
+ while (true) {
+ write(" ");
+ //foreach (Value val; this.stack) {
+ for (int i = 0; i < this.topOffset; i++) {
+ writef("[ %s ]", printableValue(this.stack[i]));
+ }
+ writeln("\n--");
+ disassemble(this.func.chunk, this.ip);
+ ubyte inst;
+ switch (inst = this.readByte()) {
+ case OpCode.OP_CONSTANT:
+ Value constant = this.func.chunk.constants[this.readByte()];
+ this.push(constant);
+ break;
+ case OpCode.OP_NEGATE:
+ if (!isNumber(peek(0))) {
+ writeln("negate operand must be a number");
+ return InterpretResult.RUNTIME_ERROR;
+ }
+ double val = asNumber(pop());
+ push(makeNumberValue(val * -1));
+ break;
+ case OpCode.OP_ADD:
+ Value b = this.pop();
+ if (!isNumber(b)) {
+ writeln("b is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ Value a = this.pop();
+ if (!isNumber(a)) {
+ writeln("a is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ double bnum = asNumber(b);
+ double anum = asNumber(a);
+ push(makeNumberValue(anum + bnum));
+ break;
+ case OpCode.OP_SUBTRACT:
+ Value b = this.pop();
+ if (!isNumber(b)) {
+ writeln("b is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ Value a = this.pop();
+ if (!isNumber(a)) {
+ writeln("a is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ double bnum = asNumber(b);
+ double anum = asNumber(a);
+ push(makeNumberValue(anum - bnum));
+ break;
+ case OpCode.OP_LESS:
+ Value b = this.pop();
+ if (!isNumber(b)) {
+ writeln("b is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ Value a = this.pop();
+ if (!isNumber(a)) {
+ writeln("a is not a number!");
+ return InterpretResult.RUNTIME_ERROR; // TODO error
+ }
+ double bnum = asNumber(b);
+ double anum = asNumber(a);
+ push(makeBooleanValue(anum < bnum));
+ break;
+ case OpCode.OP_RETURN:
+ Value ret = this.pop();
+ writefln("returned %s", printableValue(ret));
+ return InterpretResult.OK;
+ default:
+ writeln("unknown opcode to run");
+ break;
+ }
+ }
+
+ }
+
+}