ometa OMetaParser <: Parser { fromTo :x :y = seq(x) (~seq(y) char)* seq(y), space = super(#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, hostExpr = foreign(BSJSParser, `expr):r foreign(BSJSTranslator, `trans, r), atomicHostExpr = foreign(BSJSParser, `semAction):r foreign(BSJSTranslator, `trans, r), args = "(" listOf(`hostExpr, ','):xs ")" -> xs | empty -> [], application = name:rule args:as -> [`App, rule].concat(as), semAction = ("!" | "->") atomicHostExpr:x -> [`Act, x], semPred = "?" atomicHostExpr:x -> [`Pred, x], expr = listOf(#expr4, '|'):xs -> [`Or].concat(xs), expr4 = expr3*:xs -> [`And].concat(xs), optIter :x = "*" -> [`Many, x] | "+" -> [`Many1, x] | empty -> x, expr3 = expr2:x optIter(x):x ( ':' name:n -> { self.locals.push(n); [`Set, n, x] } | empty -> x ) | ":" name:n -> { self.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) !(self.locals = ['$elf=this']) rulePart(n):x ("," rulePart(n))*:xs -> [`Rule, n, self.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 "}" -> [`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 an ML "feel".) ometa OMetaTranslator { trans [:t apply(t):ans] -> ans, App 'super' anything+:args -> [self.sName, '._superApplyWithArgs($elf,', args.join(','), ')'].join(''), App :rule anything+:args -> ['$elf._applyWithArgs("', rule, '",', args.join(','), ')'].join(''), App :rule -> ['$elf._apply("', rule, '")'] .join(''), Act :expr -> expr, Pred :expr -> ['$elf._pred(', expr, ')'] .join(''), Or transFn*:xs -> ['$elf._or(', xs.join(','), ')'] .join(''), And notLast(#trans)*:xs trans:y -> { xs.push('return ' + y) ['(function(){', xs.join(';'), '})()'].join('') }, And -> '(function(){})', Many trans:x -> ['$elf._many(function(){return ', x, '})'] .join(''), Many1 trans:x -> ['$elf._many1(function(){return ', x, '})'] .join(''), Set :n trans:v -> [n, '=', v].join(''), Not trans:x -> ['$elf._not(function(){return ', x, '})'] .join(''), Lookahead trans:x -> ['$elf._lookahead(function(){return ', x, '})'] .join(''), Form trans:x -> ['$elf._form(function(){return ', x, '})'] .join(''), Rule :name locals:ls trans:body -> ['"', name, '":function(){', ls, 'return ', body, '}'] .join(''), Grammar :name :sName !(self.sName = sName) trans*:rules -> [name, '=', sName, '.delegated({', rules.join(','), '})'] .join(''), locals = [string+:vs] -> ['var ', vs.join(','), ';'] .join('') | [] -> '', transFn = trans:x -> ['(function(){return ', x, '})'] .join('') } parseAndCompileGrammar = function(g) { return OMetaTranslator.match(OMetaParser.matchAll(g, "grammar"), "trans") } // a little example parseAndCompileGrammar("ometa Duh { ones = 1* }") // SO FAR WE HAVE BEEN RUNNING ON STAGE 0 // a bigger example thisFile = readFile("OMeta_Compiler") ometaGrammar = thisFile.substring(0, thisFile.indexOf("}\n\n//") + 1) parseAndCompileGrammar(ometaGrammar) // now we'll make sure that our translator actually worked by replacing // the original OMeta parser and compiler with the ones defined here BSOMetaParser = OMetaParser BSOMetaTranslator = OMetaTranslator // STAGE 1 INSTALLED // drumroll, please... ometa Duh { ones = 1* } Duh.matchAll([1, 1, 1, 1, 1], "ones") // Now we try to generate a working STAGE 2 using STAGE 1 // This will succeed, BUT the STAGE 2 will break when run parseAndCompileGrammar(ometaGrammar) BSOMetaParser = OMetaParser BSOMetaTranslator = OMetaTranslator // STAGE 2 INSTALLED - BUT THIS WILL FAIL when run // drumroll, please... ometa Duh { ones = 1* } // OOPs