ometa BSOMetaParser <: Parser { fromTo :x :y = seq(x) (~seq(y) char)* seq(y), space = ^space | fromTo('//', '\n') | fromTo('/*', '*/'), nameFirst = '_' | '$' | letter, nameRest = nameFirst | digit, tsName = firstAndRest(#nameFirst, #nameRest):xs -> xs.join(''), name = spaces tsName, eChar = '\\' char:c -> unescape('\\' +c) | char, tsString = '\'' (~'\'' eChar)*:xs '\'' -> xs.join(''), characters = '`' '`' (~('\'' '\'') eChar)*:xs '\'' '\'' -> [#App, #seq, xs.join('').toProgramString()], sCharacters = '"' (~'"' eChar)*:xs '"' -> [#App, #token, xs.join('').toProgramString()], string = (('#' | '`') tsName | tsString):xs -> [#App, #exactly, xs.toProgramString()], number = ('-' | empty -> ''):sign digit+:ds -> [#App, #exactly, sign + ds.join('')], keyword :xs = token(xs) ~letterOrDigit -> xs, args = '(' listOf(#hostExpr, ','):xs ")" -> xs | empty -> [], application = "^" name:rule args:as -> [#App, "super", "'" + rule + "'"].concat(as) | name:grm "." name:rule args:as -> [#App, "foreign", grm, "'" + rule + "'"].concat(as) | name:rule args:as -> [#App, rule].concat(as), hostExpr = BSJSParser.expr:r BSJSTranslator.trans(r), atomicHostExpr = BSJSParser.semAction:r BSJSTranslator.trans(r), curlyHostExpr = BSJSParser.curlySemAction:r BSJSTranslator.trans(r), semAction = ("!" | "->") atomicHostExpr:x -> [#Act, x] | curlyHostExpr:x -> [#Act, x], semPred = "?" atomicHostExpr:x -> [#Pred, x], expr = expr4ne:x ("|" expr4ne)+:xs -> [#Or, x].concat(xs) | expr4ne:x ("||" expr4ne)+:xs -> [#XOr, x].concat(xs) | expr4, expr4ne = expr3+:xs -> [#And].concat(xs), expr4 = expr3*:xs -> [#And].concat(xs), optIter :x = "*" -> [#Many, x] | "+" -> [#Many1, x] | empty -> x, expr3 = expr2:x optIter(x):x ( ':' name:n -> { this.locals.push(n); [#Set, n, x] } | empty -> x ) | ":" name:n -> { this.locals.push(n); [#Set, n, [#App, #anything]] }, expr2 = "~" expr2:x -> [#Not, x] | "&" expr1:x -> [#Lookahead, x] | expr1, expr1 = application | semAction | semPred | ( keyword('undefined') | keyword('nil') | keyword('true') | keyword('false') ):x -> [#App, #exactly, x] | spaces (characters | sCharacters | string | number) | "[" expr:x "]" -> [#Form, x] | "(" expr:x ")" -> x, ruleName = name | spaces tsString, rule = &(ruleName:n) !(this.locals = ['$elf=this']) rulePart(n):x ("," rulePart(n))*:xs -> [#Rule, n, this.locals, [#Or, x].concat(xs)], rulePart :rn = ruleName:n ?(n == rn) expr4:b1 ( "=" expr:b2 -> [#And, b1, b2] | empty -> b1 ), grammar = keyword('ometa') name:n ( "<:" name | empty -> 'OMeta' ):sn "{" listOf(#rule, ','):rs "}" BSOMetaOptimizer.optimizeGrammar( [#Grammar, n, sn].concat(rs) ) } // By dispatching on the head of a list, the following idiom allows translators to avoid doing a linear search. // (Note that the "=" in a rule definition is optional, which can be used to get the "ML feel".) ometa BSOMetaTranslator { trans [:t apply(t):ans] -> ans, App 'super' anything+:args -> [this.sName, '._superApplyWithArgs(this,', args.join(','), ')'].join(''), App :rule anything+:args -> ['this._applyWithArgs("', rule, '",', args.join(','), ')'].join(''), App :rule -> ['this._apply("', rule, '")'] .join(''), Act :expr -> expr, Pred :expr -> ['this._pred(', expr, ')'] .join(''), Or transFn*:xs -> ['this._or(', xs.join(','), ')'] .join(''), XOr transFn*:xs {xs.unshift((this.name + "." + this.rName).toProgramString())} -> ['this._xor(', xs.join(','), ')'] .join(''), And notLast(#trans)*:xs trans:y {xs.push('return ' + y)} -> ['(function(){', xs.join(';'), '}).call(this)'] .join(''), And -> 'undefined', Many transFn:x -> ['this._many(', x, ')'] .join(''), Many1 transFn:x -> ['this._many1(', x, ')'] .join(''), Set :n trans:v -> [n, '=', v].join(''), Not transFn:x -> ['this._not(', x, ')'] .join(''), Lookahead transFn:x -> ['this._lookahead(', x, ')'] .join(''), Form transFn:x -> ['this._form(', x, ')'] .join(''), JumpTable jtCase*:cases -> this.jumpTableCode(cases), Rule :name {this.rName = name} locals:ls trans:body -> ['\n"', name, '":function(){', ls, 'return ', body, '}'] .join(''), Grammar :name :sName {this.name = name} {this.sName = sName} trans*:rules -> [name, '=', 'Object.delegated(', sName, ',{', rules.join(','), '})'] .join(''), jtCase = [:x trans:e] -> [x.toProgramString(), e], locals = [string+:vs] -> ['var ', vs.join(','), ';'] .join('') | [] -> '', transFn = trans:x -> ['(function(){return ', x, '})'] .join('') } BSOMetaTranslator.jumpTableCode = function(cases) { var buf = new StringBuffer() buf.nextPutAll("(function(){switch(this._apply('anything')){") for (var i = 0; i < cases.length; i += 1) buf.nextPutAll("case " + cases[i][0] + ":return " + cases[i][1] + ";") buf.nextPutAll("default: throw fail}}).call(this)") return buf.contents() }