import std.stdio; import std.string; import std.conv; import std.algorithm : canFind; 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 localCount; 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); } int parseVariable(Def def) { declareVariable(def.name); if (scopeDepth > 0) { //return 0; return localCount - 1; //return localCount; } int addr = func.chunk.addConstant(makeStringValue(def.name.name)); return addr; } void defineVariable(int addr) { if (scopeDepth > 0) { return; } func.chunk.writeOp(OpCode.OP_DEFINE_GLOBAL, current.line); func.chunk.writeOp(to!ubyte(addr), current.line); } void declareVariable(Symbol sym) { if (scopeDepth == 0) { return; } for (int i = localCount - 1; i >= 0; i--) { Local local = locals[i]; if (local.depth != -1 && local.depth < scopeDepth) { break; } if (sym.name == local.sym.name) { writefln("ERROR: already a variable named '%s' in this scope", sym.name); return; } } writefln("> > > creating a local at %d", scopeDepth); Local loc = Local(sym, scopeDepth); locals ~= loc; localCount++; writefln("localcount is now %d", 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); // are we setting a local? if (scopeDepth > 0) { func.chunk.writeOp(OpCode.OP_SET_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)", local.sym.name, i, local.depth); if (local.sym.name == sym.name) { return i; } } return -1; } 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; 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; 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(to!ubyte(getOp), sym.line); func.chunk.writeOp(to!ubyte(arg), sym.line); //advance(); } void compileBlock(Form form) { Block block = cast(Block)form; beginScope(); foreach (Form inner; block.blockBody) { resolve(inner); } endScope(); } void beginScope() { scopeDepth++; } 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); localCount--; } } 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); 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; case FormType.DEF: this.compileDef(form); break; case FormType.SYMBOL: this.compileSymbol(form); break; case FormType.BLOCK: this.compileBlock(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; localCount = 0; //locals ~= Local(new Symbol("", -1), 0); //localCount++; } }