// this is all of the machinery required for parsing MetaToo = { char: function() { if (this._input.length > this._pos) return this._input[this._pos++] throw "[fail]" }, _exactly: function(x) { if (this.char() == x) return x throw "[fail]" }, _not: function(r) { var p = this._pos try { this[r].apply(this) } catch (_) { this._pos = p; return true } throw "[fail]" }, _many: function(r) { var ans = [] while (true) { var p = this._pos try { ans.push(this[r].apply(this)); alert("last ans is " + ans[ans.length - 1]) } catch (_) { this._pos = p; return ans } } }, _many1: function(r) { var ans = [this[r].apply(this)] while (true) { var p = this._pos try { ans.push(this[r].apply(this)) } catch (_) { this._pos = p; return ans } } }, _pred: function(x) { if (!x) throw "[fail]" }, matchAll: function(cs, r) { var m = this.delegated({_input: cs, _pos: 0}) return m[r].apply(m) } } // an OMeta-like front-end ometa MetaTooTranslator <: Parser { name = spaces firstAndRest(#letter, #letterOrDigit):cs -> cs.join(''), grammar = listOf(#rule, ',') spaces end -> self.ans, rule = name:n "=" choices:cs -> { var f = 'function() { var ls = {}, btPos = this._pos;' + cs + '}' self.ans[n] = eval(f) n }, choices = expr:x "|" choices:y -> ('try { ' + x + ' } catch (_) { this._pos = btPos; ' + y + ' }') | expr:x -> x, expr0 = "'" (~'\'' oneChar)*:cs '\'' -> cs.join('; ') | name:x -> ('this.' + x + '()') | "(" expr:x ")" -> x, oneChar = '\\' char:c -> ('this._exactly(' + unescape('\\' + c).toProgramString() + ')') | char:c -> ('this._exactly(' + c.toProgramString() + ')'), expr0Rule = expr0:x -> { var r = tempnam() self.ans[r] = eval('function() { return ' + x + ' }') r }, expr1 = "~" expr0Rule:r -> ('this._not("' + r + '")') | "?" jsExpr:e -> ('this._pred(' + e + ')') | expr0Rule:r "*" -> ('this._many("' + r + '")') | expr0Rule:r "+" -> ('this._many1("' + r + '")') | expr0, expr2 = expr1:v ":" name:n -> ('ls["' + n + '"] = ' + v) | expr1, expr = expr2*:es ( "->" jsExpr:a -> { es.push("return " + a); es.join(';') } | -> { es.push("return undefined"); es.join(';') } ), jsExpr = "@" (~'@' char)*:xs '@' -> ('(function() { var _; with (ls) _=' + xs.join('') + '; return _ }).apply(this)') } MetaTooTranslator.initialize = function() { this.ans = MetaToo.delegated() } g = MetaTooTranslator.matchAll( ["end = ~char", "space = char:s ?@s.isSpace()@ -> @s@", "spaces = space*:ss -> @ss@", "digit = char:d ?@d.isDigit()@ -> @d@", "number = spaces digit+:ds -> @['number', parseInt(ds.join(''))]@", "addExpr = mulExpr:x (spaces '+' mulExpr:y -> @y@)*:ys -> @['plus'].concat([x], ys)@", "mulExpr = primExpr:x (spaces '*' primExpr:y -> @y@)*:ys -> @['times'].concat([x], ys)@", "primExpr = number:x -> @x@\n",// + // " | spaces '(' spaces expr:x ')' -> @x@", "expr = addExpr:x -> @x@", "x = 'x':a -> @a@", "xs = 'x'*:a -> @a@" ].join(',\n'), "grammar" ) g.mulExpr g._tmpnam_191 g.matchAll("xxxxx", "xs") g.matchAll("6*(4+3)", "primExpr")