import std.stdio; import std.string; import std.conv; import std.algorithm : canFind; import parser; import chunk; import dbg; struct Local { Symbol sym; int depth; } class Compiler { Function func; Parser* parser; Local[] locals; int localCount; int scopeDepth; Form current; Form previous; int currentLine; 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) { int line = args[0].line; resolve(args[0]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, line); // (+ 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_TYPE_CHECK_NUMBER, line); func.chunk.writeOp(OpCode.OP_ADD, line); } } void compileNegate(Form arg) { resolve(arg); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); func.chunk.writeOp(OpCode.OP_NEGATE, currentLine); } void compileSubtract(Form[] args) { resolve(args[0]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); for (int i = 1; i < args.length; i++) { resolve(args[i]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); func.chunk.writeOp(OpCode.OP_SUBTRACT, currentLine); } } void compileLess(Form[] args) { if (args.length != 2) { writeln("'<' requires 2 arguments"); return; } resolve(args[0]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); resolve(args[1]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_NUMBER, currentLine); func.chunk.writeOp(OpCode.OP_LESS, currentLine); } //int parseVariable(Def def) { int parseVariable(Symbol sym) { declareVariable(sym); if (this.scopeDepth > 0) { return 0; } int addr = func.chunk.addConstant(makeStringValue(sym.name)); return addr; } void defineVariable(int addr) { if (this.scopeDepth > 0) { return; } func.chunk.writeOp(OpCode.OP_DEF_GLOBAL, current.line); func.chunk.writeOp(to!ubyte(addr), current.line); } void declareVariable(Symbol sym) { if (this.scopeDepth == 0) { return; } for (int i = localCount - 1; i >= 0; i--) { Local local = locals[i]; if (local.depth != -1 && local.depth < this.scopeDepth) { break; } if (sym.name == local.sym.name) { writefln("ERROR: already a variable named '%s' in this scope", sym.name); return; } } Local loc = Local(sym, this.scopeDepth); if (localCount == locals.length) { locals ~= loc; } else { locals[localCount] = loc; } localCount++; } void compileDef(Form form) { Def def = cast(Def)form; // resolve the value resolve(def.val); // add the variable name to the chunk (if applicable) int addr = parseVariable(def.name); // are we setting a local? if (this.scopeDepth > 0) { func.chunk.writeOp(OpCode.OP_DEF_LOCAL, def.line); } // define the variable defineVariable(addr); } int resolveLocal(Symbol sym) { for (int i = localCount - 1; i >= 0; i--) { Local local = locals[i]; if (local.sym.name == sym.name) { return i; } } return -1; } void compileSymbol(Form form) { Symbol sym = cast(Symbol)form; int arg = resolveLocal(sym); if (arg != -1) { func.chunk.writeOp(OpCode.OP_GET_LOCAL, sym.line); } else { arg = func.chunk.addConstant(makeStringValue(sym.name)); func.chunk.writeOp(OpCode.OP_GET_GLOBAL, sym.line); } // get the variable 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); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, if_.line); 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]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); int[] jumps; jumps ~= jump(OpCode.OP_JUMP_IF_FALSE); int count = 1; while (count < and_.clauses.length) { this.func.chunk.writeOp(OpCode.OP_POP, currentLine); resolve(and_.clauses[count]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); jumps ~= jump(OpCode.OP_JUMP_IF_FALSE); count++; } // patch all the jumps foreach (int jmp; jumps) { patchJump(jmp); } } void compileOr(Form form) { Or or_ = cast(Or)form; resolve(or_.clauses[0]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); int[] jumps; jumps ~= jump(OpCode.OP_JUMP_IF_TRUE); int count = 1; while (count < or_.clauses.length) { this.func.chunk.writeOp(OpCode.OP_POP, or_.line); resolve(or_.clauses[count]); func.chunk.writeOp(OpCode.OP_TYPE_CHECK_BOOLEAN, currentLine); jumps ~= jump(OpCode.OP_JUMP_IF_TRUE); count++; } // patch all the jumps foreach (int jmp; jumps) { patchJump(jmp); } } void compileBlock(Form form) { Block block = cast(Block)form; beginScope(); foreach (Form inner; block.blockBody) { resolve(inner); } endScope(); } void beginScope() { this.scopeDepth++; } void endScope() { this.scopeDepth--; while (localCount > 0 && locals[localCount - 1].depth > this.scopeDepth) { func.chunk.writeOp(OpCode.OP_POPB, -1); localCount--; } } void call(Form[] args) { //ubyte argCount = argumentList(); foreach (Form f ; args) { resolve(f); } func.chunk.writeOp(OpCode.OP_CALL, -1); //func.chunk.writeOp(argCount, -1); func.chunk.writeOp(to!ubyte(args.length), -1); } void compileFunc(Form form) { Func f = cast(Func)form; // name the function int global = parseVariable(f.name); Compiler compiler = new Compiler(ObjType.FUNCTION, parser, f.name.name); compiler.beginScope(); if (f.args.type != FormType.NIL) { Symbol sym = cast(Symbol)f.args.head; int constant = compiler.parseVariable(sym); compiler.defineVariable(constant); foreach (Form inner ; f.args.tail) { sym = cast(Symbol)inner; constant = compiler.parseVariable(sym); compiler.defineVariable(constant); } } /* // compile each inner Form foreach (Form inner; f.funcBody) { compiler.resolve(inner); } */ advance(); // ?? Block b = new Block(f.line); b.blockBody = f.funcBody; compiler.compileBlock(b); // write the function as a Value Function outFunc = compiler.finish(); func.chunk.writeOp(OpCode.OP_CONSTANT, f.line); int funcAddr = func.chunk.addConstant(makeObjValue(outFunc)); func.chunk.writeOp(to!ubyte(funcAddr), f.line); // define the global variable defineVariable(global); } 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(); */ resolve(head); call(cons.tail); advance(); break; } } void resolve(Form form) { //printForm(form); currentLine = form.line; switch(form.type) { case FormType.ATOM: this.compileAtom(form); break; case FormType.CONS: this.compileCons(form); break; case FormType.NIL: this.compileNil(form); break; case FormType.DEF: this.compileDef(form); break; case FormType.SYMBOL: this.compileSymbol(form); break; case FormType.BLOCK: this.compileBlock(form); break; case FormType.IF: this.compileIf(form); break; case FormType.AND: this.compileAnd(form); break; case FormType.OR: this.compileOr(form); break; case FormType.FUNC: this.compileFunc(form); break; default: write("not sure how to resolve: "); printForm(form); advance(); break; } } Function compile() { advance(); while(current.type != FormType.EOF) { resolve(current); } return finish(); } Function finish() { this.func.chunk.writeOp(OpCode.OP_RETURN, current.line); disassembleChunk(func.chunk, to!string(func)); return this.func; } this(ObjType type, Parser* parser, string name = "") { this.parser = parser; this.func = new Function(type, name); //localCount = 0; locals ~= Local(new Symbol("", -1), 0); localCount++; } }