diff options
| author | Ben Winston | 2023-05-20 15:12:30 -0400 |
|---|---|---|
| committer | Ben Winston | 2023-05-20 15:12:30 -0400 |
| commit | 0c70372774297272dd14133d48e40e9a3624420a (patch) | |
| tree | 4e66fb1b97a0fd537a20dca4ddcd26003f83c94d | |
| parent | 6902cc5abe09da9f6f2d86f22d06684d97cfa9f3 (diff) | |
initial commit of compiler/VM with a couple basic instructions
| -rw-r--r-- | chunk.d | 2 | ||||
| -rw-r--r-- | compiler.d | 166 | ||||
| -rw-r--r-- | dbg.d | 55 | ||||
| -rw-r--r-- | main.d | 90 | ||||
| -rw-r--r-- | parser.d | 45 | ||||
| -rw-r--r-- | vm.d | 146 |
6 files changed, 434 insertions, 70 deletions
@@ -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); + } +} @@ -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; @@ -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); @@ -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; } @@ -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; + } + } + + } + +} |
