load("parser.js") Program.prototype.compile = function() { for (var h in this.hats) { this.hats[h].compile(); this.hats[h].program = this; } } function Context() { this.position = 0; this.applys = []; } function Code() { this.indentation = ""; this.text = ""; this.fresh_line = true; this.indent = function() { this.indentation += " "; } this.unindent = function() { this.indentation = this.indentation.substring(0, this.indentation.length - 4); } this.put = function(x) { if (this.fresh_line) this.text += this.indentation; this.text += x; this.fresh_line = false; } this.putl = function(x) { this.put(x + "\n"); this.fresh_line = true; } } Hat.prototype.compile = function() { var nop = function() {}; this.stacks = [[]]; this.suspended_contexts = []; this.compiled_magic = {init: nop, in: nop, out: nop}; this.compiled_magic_code = []; this.program = null; this.push = function(x) { this.stacks[0].unshift(x); this.compiled_magic["in"].call(this); } this.pop = function(x) { this.compiled_magic["out"].call(this); if (this.stacks[0].length > 0) return this.stacks[0].shift(); else throw 'empty argument stack'; } for (var m in this.magic) { var code = new Code(); var position = 0; code.putl("(function() {"); code.indent(); if (m == "in") { code.putl("if (this.suspended_contexts.length > 0)"); code.putl(" var context = this.suspended_contexts.pop();"); code.putl("else"); code.put(" "); } code.putl("var context = new Context();"); code.putl(""); if (m == "in") { code.putl("switch (context.position) {"); code.indent(); code.putl("case 0:"); code.indent(); } for (var n in this.magic[m]) { var move = this.magic[m][n]; var sourcecode, destcode; code.putl("// " + move); switch (move.source.type) { case "stack": this.stacks[move.source.id] = []; if (move.source.id=="0" && m=="in") { // This is a suspension point. position++; code.putl("context.position = " + position + ";"); code.unindent(); code.putl(""); code.putl("case " + position + ":"); code.indent(); code.putl("if (this.stacks[0].length == 0) {"); code.putl(" this.suspended_contexts.push(context);"); code.putl(" return;"); code.putl("}"); } else { code.putl("if (this.stacks[" + move.source.id + "].length == 0)"); code.putl(" throw 'Empty stack!';"); } sourcecode = "this.stacks[" + move.source.id + "].pop()"; break; case "hat": sourcecode = "this.program.hats['" + move.source.id + "'].pop()"; break; case "apply": code.putl("if (!context.applys[" + move.source.id + "])"); code.putl(" throw 'Using uninitialized apply!';"); sourcecode = "context.applys[" + move.source.id + "].pop()"; break; case "number": //if (!inbounds(move.source.id)) // throw "Compile error: number " + move.source.id + " out of range"; sourcecode = "wraparound(" + move.source.id + ")"; break; case "hatid": //sourcecode = this.program.hats[move.source.id].hatid; // Late binding, just because. // (Makes it possible to add the environment after compilation.) sourcecode = "this.program.hats['" + move.source.id + "'].hatid"; break; default: throw 'Hwaet!'; } var outcode; switch (move.dest.type) { case "hat": code.putl("this.program.hats['" + move.dest.id + "'].push(" + sourcecode + ");"); break; case "stack": code.putl("this.stacks[" + move.dest.id + "].push(" + sourcecode + ");"); this.stacks[move.dest.id] = []; break; case "apply": code.putl("if (!context.applys[" + move.dest.id + "]) {"); code.putl(" context.applys[" + move.dest.id + "] = this.program.get_by_hatid(" + sourcecode + ");"); code.putl(" if (context.applys[" + move.dest.id + "].name == 'apply')"); code.putl(" context.applys[" + move.dest.id + "] = null;"); code.putl("}"); code.putl("else"); code.putl(" context.applys[" + move.dest.id + "].push(" + sourcecode + ");"); break; case "number": case "hatid": code.putl(sourcecode + ";"); break; default: throw "Argh!"; } code.putl(""); } if (m == "in") { code.putl("break;"); code.unindent(); code.putl("default:"); code.putl(" throw 'Jumping to non-existent label!' + context.position;"); code.unindent(); code.putl("}"); } code.unindent(); code.putl("})"); this.compiled_magic_code[m] = code.text; this.compiled_magic[m] = eval(code.text); } this.compiled_magic['init'].call(this); }