load("lexer.js"); // This should only contain hat definition attributes, not runtime things. function Hat(name, hatid) { this.name = name; this.hatid = hatid; this.magic = []; } function Place(type, id) { this.type = type; this.id = id; this.toString = function() { return "<" + type + " " + id + ">"; } } function Move(source, dest) { this.source = source; this.dest = dest; this.toString = function() { return source + " to " + dest; } } function Program(text) { this.hats = []; this.ids = []; this.next_hatid = 42; this.next_gensym = 0; this.stream = new Stream(text); this.intern_hat = function(name) { if (!this.hats[name]) { this.hats[name] = new Hat(name, this.next_hatid); this.ids[this.next_hatid] = this.hats[name]; this.next_hatid++; } return this.hats[name]; } this.get_by_hatid = function(id) { if (this.ids[id]) return this.ids[id]; else throw 'unknown hatid' + id; } this.next = function(constraints) { var token = this.stream.next(); for (var i in constraints) { if (constraints[i] != token[i]) this.stream.error("Expected token with " + i + " '" + constraints[i] + "', but found " + token); } return token; } this.back = function() { this.stream.back(); } this.gensym = function() { return this.next_gensym++; } this.parse_thyself = function() { var token; while (1) { token = this.next(); if (token.text == "hat") { var name = this.next({type: "IDENTIFIER"}).text; this.next({text: ":"}); var hat = this.intern_hat(name); while (1) { token = this.next(); if (token.text=="init" || token.text=="in" || token.text=="out") hat.magic[token.text] = this.parse_magic().magic; else { this.back(); break; } } } else if (token.type == "EOF") return; else this.stream.error("Unexpected token " + token + " at toplevel"); } } this.parse_magic = function() { var left=null, op=null, right=null; var leftmost=null, magic=[], leftmagic, rightmagic; var token; while (1) { token = this.next(); rightmagic = []; if (token.text == "[") { submagic = this.parse_magic(); right = submagic.leftmost; rightmagic = submagic.magic; this.next({text: "]"}); } else if (token.text=="apply") { right = new Place("apply", this.gensym()); } else if (token.type=="IDENTIFIER") { right = new Place("hat", token.text); } else if (token.type=="NUMBER") { right = new Place("number", Number(token.text)); } else if (token.text=="~") { right = new Place("number", -Number(this.next({type: "NUMBER"}).text)); } else if (token.text=="@") { token = this.next(); if (token.type == "NUMBER") right = new Place("stack", Number(token.text)) else { right = new Place("stack", 0); this.back(); } } else if (token.text=="\\") { right = new Place("hatid", this.next({type: "IDENTIFIER"}).text); } else { this.stream.error("Unexpected end of magic; expecting identifier or grouping, found " + token); } if (op) { if (op == "->") { magic.push(new Move(left, right)); magic = magic.concat(rightmagic); } else { magic = magic.concat(rightmagic); magic.push(new Move(right, left)); } } else { leftmost = right; magic = rightmagic; } left = right; token = this.next(); if (token.type != "ARROW") { this.back(); if (op) { return {leftmost: leftmost, magic: magic}; } else { this.stream.error("Unexpected end of magic; expecting arrow, found " + token); } } op = token.text; } } }