// This is a backup of http://tinlizzie.org/ometa-js/#bcpl_ometa // TODO: // * eventually impl. JumpTable, Interleave // -------------------------------------------------------------------------------- // a few things to make bootstrapping easier BSOMetaParser0 = BSOMetaParser ometa BSOMetaParser2 <: BSOMetaParser0 { nameFirst = ~'$' ^nameFirst, tsString = '$' eChar | ^tsString } BSOMetaParser = BSOMetaParser2 ms = function(x) { return x } ma = function() { var r = [] for (var i = 0; i < arguments.length; i++) r.push(arguments[i]) return r } cat = function() { var r = [] for (var i = 0; i < arguments.length; i++) r = r.concat(arguments[i]) return r } join = function(ss, s) { return ss.join(s) } OMeta.chr = OMeta.char // -------------------------------------------------------------------------------- // now back to the OMeta compiler ometa NullOptimization { initialize = !(this._didSomething = false), setHelped = { this._didSomething = true }, helped = ?this._didSomething, trans = [:t ?(this[t] != undefined) apply(t):ans] -> ans, optimize = trans:x helped -> x, App :rule anything*:args -> cat(ma("App", rule), args), SuperApp :rule anything*:args -> cat(ma("SuperApp", rule), args), Act :expr -> ma("Act", expr), Ans :expr -> ma("Ans", expr), Pred :expr -> ma("Pred", expr), oOr trans*:xs -> cat(ma("oOr"), xs), oXOr trans*:xs -> cat(ma("oXOr"), xs), oAnd trans*:xs -> cat(ma("oAnd"), xs), Opt trans:x -> ma("Opt", x), Many trans:x -> ma("Many", x), Many1 trans:x -> ma("Many1", x), Set :n trans:v -> ma("Set", n, v), oNot trans:x -> ma("oNot", x), Lookahead trans:x -> ma("Lookahead", x), Form trans:x -> ma("Form", x), ConsBy trans:x -> ma("ConsBy", x), IdxConsBy trans:x -> ma("IdxConsBy", x), JumpTable ([:c trans:e] -> ma(c, e))*:ces -> cat(ma("JumpTable"), ces), Interleave ([:m trans:p] -> ma(m, p))*:xs -> cat(ma("Interleave"), xs), Rule :name :locals trans:body -> ma("Rule", name, locals, body) } ometa AssociativeOptimization <: NullOptimization { oAnd trans:x end setHelped -> x, oAnd transInside("oAnd"):xs -> cat(ma("oAnd"), xs), oOr trans:x end setHelped -> x, oOr transInside("oOr"):xs -> cat(ma("oOr"), xs), oXOr trans:x end setHelped -> x, oXOr transInside("oXOr"):xs -> cat(ma("oXOr"), xs), transInside :t = [exactly(t) transInside(t):xs] transInside(t):ys setHelped -> cat(xs, ys) | trans:x transInside(t):xs -> cat(ma(x), xs) | -> [] } ometa OMetaOptimizer { optimizeGrammar = ['Grammar' :n :sn optimizeRule*:rs] -> cat(ma("Grammar", n, sn), rs), optimizeRule = :r (AssociativeOptimization.optimize(r):r)* -> r } ometa SAParser { expr :inside = $( expr(true)* $) | $[ expr(true)* $] | ${ expr(true)* $} | $" (~$" ($* chr | $\\ chr | chr))* $" | ?inside ~($) | $] | $} | $") chr | letter letterOrDigit* ~($( | $.) | letter letterOrDigit* &$( expr(false) | letter letterOrDigit* $. letter letterOrDigit* (&$( expr(false) | empty) | letter letterOrDigit* $. &$( expr(false) | digit+ | $., semAction = , semActionsHelper = <(~$, expr(true))+>, semActions = listOf("semActionsHelper", ","), start = semAction } ometa BCPLOMetaParser { fromTo :x :y = seq(x) (~seq(y) chr)* seq(y), space = ^space | fromTo("//", "\n") | fromTo("/*", "*/"), nameFirst = $_ | letter, nameRest = nameFirst | digit, tsName = , name = spaces tsName, eChr = $\\ chr:c -> unescape(join(ma("\\", c), "")) | chr, tsString = $' (~$' eChr)*:xs $' -> join(xs, ""), characters = $` $` (~($' $') eChr)*:xs $' $' -> [#App, #seq, xs.join('').toProgramString()], sCharacters = $" (~$" eChr)*:xs $" -> [#App, #token, xs.join('').toProgramString()], string = (($# | $`) tsName | tsString):xs -> [#App, #exactly, xs.toProgramString()], character = $$ eChr:x -> [#App, #exactly, x.charCodeAt(0).toString()], number = <($- | empty) digit+>:x -> ma("App", "exactly", x), keyword :xs = token(xs) ~letterOrDigit -> xs, args = $( hostExprs:xs ")" -> xs | empty -> ma(), application = "^" name:rule args:aa -> cat(ma("SuperApp", rule), aa) | name:grm "." name:rule args:aa -> cat(ma("App", "foreign", join(ma("\"", grm, "\""), ""), join(ma("\"", rule, "\""), "")), as) | name:rule args:as -> cat(ma("App", rule), aa), hostExpr = spaces SAParser.semAction, hostExprs = spaces SAParser.semActions, semAction = hostExpr:x -> ma("Act", x) | "!" hostExpr:x -> ma("Act", x), arrSemAction = "->" hostExpr:x -> ma("Ans", x), semPred = "?" hostExpr:x -> ma("Pred", x), expr = expr5(true):x ("|" expr5(true))+:xs -> cat(ma("oOr", x), xs) | expr5(true):x ("||" expr5(true))+:xs -> cat(ma("oXOr", x), xs) | expr5(false), expr5 :ne = interleavePart:x ("&&" interleavePart)+:xs -> cat(ma("Interleave", x), xs) | expr4(ne), interleavePart = "(" expr4(true):part ")" -> ma("1", part) | expr4(true):part modedIPart(part), modedIPart = [#And [#Many :part]] -> ma("*", part) | [#And [#Many1 :part]] -> ma("+", part) | [#And [#Opt :part]] -> ma("?", part) | :part -> ma("1", part), expr4 :ne = expr3*:xs arrSemAction:act -> cat(ma("oAnd"), xs, ma(act)) | ?ne expr3+:xs -> cat(ma("oAnd"), xs) | ?(ne == false) expr3*:xs -> cat(ma("oAnd"), xs), optIter :x = $* -> ma("Many", x) | $+ -> ma("Many1", x) | $? -> ma("Opt", x) | empty -> x, optBind :x = $: name:n -> { this.locals.push(n); [#Set, n, x] } | empty -> x, expr3 = ":" name:n -> { this.locals.push(n); [#Set, n, [#App, #anything]] } | (expr2:x optIter(x) | semAction):e optBind(e) | semPred, expr2 = "~" expr2:x -> ma("oNot", x) | "&" expr1:x -> ma("Lookahead", x) | expr1, expr1 = application | ( keyword("undefined") | keyword("nil") | keyword("true") | keyword("false") ):x -> ma("App", "exactly", x) | spaces (characters | sCharacters | string | character | number) | "[" expr:x "]" -> ma("Form", x) | "<" expr:x ">" -> ma("ConsBy", x) | "@<" expr:x ">" -> ma("IdxConsBy", x) | "(" expr:x ")" -> x, ruleName = name | spaces tsString, rule = &(ruleName:n) !(this.locals = ['_ans = true']) rulePart(n):x ("," rulePart(n))*:xs -> ma("Rule", n, this.locals, cat(ma("oOr", x), xs)), rulePart :rn = ruleName:n ?(n == rn) expr4(false):b1 ( "=" expr:b2 -> ma("oAnd", b1, b2) | empty -> b1 ), grammar = keyword("ometa") name:n ( "<:" name | empty -> "ometa" ):sn "{" listOf("rule", ","):rs "}" OMetaOptimizer.optimizeGrammar( cat(ma("Grammar", n, sn), 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, so you can give your rules an "ML feel".) ometa BCPLOMetaTranslator { App :rule anything*:args -> this.emitApp(rule, args), SuperApp :rule anything*:args -> this.emitSuperApp(rule, args), Ans :expr -> this.emitAns(expr), Act :expr -> this.emitAct(expr), Pred :expr -> this.emitPred(expr), oOr trans*:xs -> this.emitOr(xs), oXOr trans*:xs -> this.emitXOr(xs), oAnd trans*:xs -> this.emitAnd(xs), Opt trans:x -> this.emitOpt(x), Many trans:x -> this.emitMany(x), Many1 trans:x -> this.emitMany1(x), Set :n trans:v -> this.emitSet(n, v), oNot trans:x -> this.emitNot(x), Lookahead trans:x -> this.emitLookahead(x), Form trans:x -> this.emitForm(x), ConsBy trans:x -> this.emitConsBy(x), IdxConsBy trans:x -> this.emitIdxConsBy(x), JumpTable jtCase*:cases -> this.emitJumpTable(cases), Interleave intPart*:xs -> this.emitInterleave(xs), Rule :name {this.rName = name} locals:locs trans:body -> this.emitRule(locs, body), Grammar :name :sName {this.name = name} {this.sName = sName} trans*:rules -> this.emitGrammar(rules), intPart = [:mode trans:part] -> (mode.toProgramString() + "," + part), jtCase = [:x trans:e] -> [x.toProgramString(), e], locals = [string+:vs] -> this.emitLocals(vs), trans = [:t apply(t):ans] -> ans } BCPLOMetaTranslator.nestWithDecls = function(decls, x) { return [ ' {\n', ' let ', decls, '\n', x, ' }\n' ].join('') } BCPLOMetaTranslator.emitMany = function(x) { return this.nestWithDecls('_ans2 = vector::class..new()', [ ' while true do\n', ' { let _old_input = this._input\n', x, ' test _ans \\= undefined then\n', ' { _ans2..push(_ans)\n', ' _old_input := this._input }\n', ' or\n', ' { _ans := _ans2..to_array()\n', ' this._input := _old_input\n', ' break } }\n' ].join('')) } BCPLOMetaTranslator.emitMany1 = function(x) { return [ ' {\n', x, ' unless _ans = undefined do {\n', this.nestWithDecls('_ans2 = vector::class..new()', [ ' _ans2..push(_ans)\n', ' while true do\n', ' { let _old_input = this._input\n', x, ' test _ans \\= undefined then\n', ' { _ans2..push(_ans)\n', ' _old_input := this._input }\n', ' or\n', ' { _ans := _ans2..to_array()\n', ' this._input := _old_input\n', ' break } }\n' ].join('')), ' }\n', ' }\n' ].join('') } BCPLOMetaTranslator.emitOpt = function(x) { return this.nestWithDecls('_old_input = this._input', [ x, ' if _ans = undefined then\n', ' { _ans := nil\n', ' this._input := _old_input }\n' ].join('')) } BCPLOMetaTranslator.emitConsBy = function(x) { return this.nestWithDecls('_old_input = this._input', [ x, ' if _ans \\= undefined then\n', ' _ans := this._str_from_to(_old_input, this._input)\n' ].join('')) } BCPLOMetaTranslator.emitIdxConsBy = function(x) { return this.nestWithDecls('_old_input = this._input', [ x, ' if _ans \\= undefined then\n', ' _ans := this._idx_from_to(_old_input, this._input)\n' ].join('')) } BCPLOMetaTranslator.emitNot = function(x) { return this.nestWithDecls('_old_input = this._input', [ x, ' test _ans = undefined then\n', ' { this._input := _old_input\n', ' _ans := true }\n', ' or\n', ' _ans := undefined\n' ].join('')) } BCPLOMetaTranslator.emitLookahead = function(x) { return this.nestWithDecls('_old_input = this._input', [ x, ' this._input := _old_input\n' ].join('')) } BCPLOMetaTranslator.emitForm = function(x) { return [ ' {\n', ' let _obj = this._apply(thisar, \"anything\")\n', ' test _obj \\= undefined /\\ typeof _obj = $vec do\n', ' { let _old_input = this._input\n', ' this._input := vecinputstream.constructor~(_obj, 0)\n', x, ' unless _ans = undefined do\n', ' { _ans := this._apply(thisar, \"end\")\n', ' unless _ans = undefined do\n', ' { _ans := _obj\n', ' this._input := _old_input } }\n', ' }\n', ' or _ans := undefined\n', ' }\n' ].join('') } BCPLOMetaTranslator.emitPred = function(x) { return [ ' {\n', ' _ans := ', x, '\n', ' if not _ans then\n', ' _ans := undefined\n', ' }\n' ].join('') } BCPLOMetaTranslator.emitAct = function(x) { return [ ' { ', x, ' }\n' ].join('') } BCPLOMetaTranslator.emitAns = function(x) { return [ ' _ans := ', x, '\n' ].join('') } BCPLOMetaTranslator.emitOr = function(xs) { return this.nestWithDecls('_old_input = this._input', [ xs[0], this.emitOrHelper(xs.splice(1)) ].join('')) } BCPLOMetaTranslator.emitOrHelper = function(xs) { if (xs.length == 0) return '' return [ ' if _ans = undefined do {\n', ' this._input := _old_input\n', xs[0], this.emitOrHelper(xs.splice(1)), ' }\n' ].join('') } BCPLOMetaTranslator.emitXOr = function(xs) { return this.nestWithDecls('_old_input, _the_ans, _the_input, _failed = this._input, undefined, undefined, false', this.emitXOrHelper(xs)) } BCPLOMetaTranslator.emitXOrHelper = function(xs) { if (xs.length == 0) return [ ' test _failed then\n', ' _ans := undefined\n', ' or\n', ' { _ans := _the_ans\n', ' this._input := _the_input }\n' ].join('') else return [ ' if not _failed then\n', ' { this._input := _old_input\n', xs[0], ' if _ans \\= undefined then\n', ' { test _the_ans = undefined then\n', ' { _the_ans := _ans\n', ' _the_input := this._input }\n', ' or\n', ' _failed := true } }\n', this.emitXOrHelper(xs.splice(1)) ].join('') } BCPLOMetaTranslator.emitAnd = function(xs) { if (xs.length == 0) // this case is only used by empty Ands return ' _ans = undefined\n' if (xs.length == 1) return xs[0] return [ ' {\n', xs[0], ' unless _ans = undefined do\n', this.emitAnd(xs.splice(1)), ' }\n' ].join('') } BCPLOMetaTranslator.emitSet = function(name, value) { return [ ' {\n', value, ' if _ans \\= undefined then \n', ' ', name, ' := _ans\n', ' }\n' ].join('') } BCPLOMetaTranslator.emitApp = function(rule, args) { return [ ' _ans := this._apply(', ['thisar', '"' + rule + '"'].concat(args).join(', '), ')\n' ].join('') } BCPLOMetaTranslator.emitSuperApp = function(rule, args) { return [ ' _ans := this._super_apply(', ['thisar', '"' + rule + '"'].concat(args).join(', '), ')\n' ].join('') } BCPLOMetaTranslator.emitLocals = function(varNames) { var uniqueVarNames = {} varNames.map(function(varName) { uniqueVarNames[varName] = true }) var varNames = [] for (v in uniqueVarNames) if (uniqueVarNames.hasOwnProperty(v)) varNames.push(v) return varNames.map(function(varName) { return ' let ' + varName }).join('\n') + '\n' } BCPLOMetaTranslator.emitRule = function(locals, body) { return [ ' { let ', this.rName, '(this, failar) = valof {\n', locals, body, '\n', /* // The following is commented out because it would be useless ' if _ans = undefined do\n', ' returnto failar\n', */ ' resultis _ans }\n', ' ', this.name, '::Matcher.', this.rName, ' := ', this.rName, ' }\n' ].join('') } BCPLOMetaTranslator.emitGrammar = function(rules) { return [ '// exports\n', 'module ', this.name, ' { Matcher }\n\n', '// imports\n', 'module vector { class }\n', 'module lib { ms, ma, cat, join, pprint, print, typestring, programstring, unescape, strtonum, map }\n', 'module ometa { Matcher, InputStream, StrInputStream, VecInputStream, InputStreamProxy }\n', (this.sName != 'ometa' ? 'module ' + this.sName + ' { Matcher }\n' : ''), '\n', 'let StrInputStream, VecInputStream,\n', ' ms, ma, cat, join, pprint, print, typestring, programstring, unescape, strtonum, map\n\n', 'let onload() be {\n', ' $create_module(', this.name, ')\n', ' load vector\n', ' load lib\n', ' load ometa\n', (this.sName != 'ometa' ? ' load ' + this.sName + '\n' : ''), '\n', ' StrInputStream := ometa::StrInputStream\n', ' VecInputStream := ometa::VecInputStream\n', ' ms := lib::ms\n', ' ma := lib::ma\n', ' cat := lib::cat\n', ' join := lib::join\n', ' pprint := lib::pprint\n', ' print := lib::print\n', ' typestring := lib::typestring\n', ' programstring := lib::programstring\n', ' unescape := lib::unescape\n', ' strtonum := lib::strtonum\n', ' map := lib::map\n\n', ' ', this.name, '::Matcher := $newhtfluis(0, ', this.sName, '::Matcher)\n', ' ', this.name, '::Matcher._super := ', this.sName, '::Matcher\n\n', rules.join('\n'), '}\n' ].join('') } // -------------------------------------------------------------------------------- tree = BCPLOMetaParser.matchAll(""" ometa og { start = digit } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa og { start = calc.expr } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa og { digit = :x ?(x >= '0' /\ x <= '9') -> (x - '0'), digits = digit*:ds -> ds, five = :x ?(x = '5') -> "yay" | digit, start = $a $b $c }""", "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa ocalc { digit = ^digit:x -> (x - '0'), number = number:n digit:d -> (n * 10 + d) | digit, primExpr = spaces number | "(" expr:x ")" -> x, mulExpr = mulExpr:x "**" primExpr:y -> (x * y) | mulExpr:x "/" primExpr:y -> (x / y) | primExpr, addExpr = addExpr:x "+" mulExpr:y -> (x + y) | addExpr:x "-" mulExpr:y -> (x - y) | mulExpr, expr = addExpr, start = expr }""", "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa ocalccompiler { space = fromTo("/**", "**/") | ^space, number = :n -> n, primExpr = spaces number | "(" expr:x ")" -> x, mulExpr = mulExpr:x "**" primExpr:y -> ma("**", x, y) | mulExpr:x "/" primExpr:y -> ma("/", x, y) | primExpr, addExpr = addExpr:x "+" mulExpr:y -> ma("+", x, y) | addExpr:x "-" mulExpr:y -> ma("-", x, y) | mulExpr, expr = addExpr, parse = expr, compile = :x ?(typeof x = $string) -> x | [:op compile:x compile:y] -> join(ma("(", x, op, y, ")"), ""), start = parse:x ocalccompiler.compile(x) }""", "grammar") code = BCPLOMetaTranslator.match(tree, "trans") // -------------------------------------------------------------------------------- tree = BCPLOMetaParser.matchAll(""" ometa oNullOptimization { initialize = { this.didSomething := false }, setHelped = { this.didSomething := true }, helped = ?this.didSomething, trans = [:t ?(this.(t) \= undefined) apply(t):ans] -> ans, optimize = trans:x helped -> x, App :rule anything*:args -> cat(ma("App", rule), args), SuperApp :rule anything*:args -> cat(ma("SuperApp", rule), args), Act :expr -> ma("Act", expr), Ans :expr -> ma("Ans", expr), Pred :expr -> ma("Pred", expr), oOr trans*:xs -> cat(ma("oOr"), xs), oXOr trans*:xs -> cat(ma("oXOr"), xs), oAnd trans*:xs -> cat(ma("oAnd"), xs), Opt trans:x -> ma("Opt", x), Many trans:x -> ma("Many", x), Many1 trans:x -> ma("Many1", x), Set :n trans:v -> ma("Set", n, v), oNot trans:x -> ma("oNot", x), Lookahead trans:x -> ma("Lookahead", x), Form trans:x -> ma("Form", x), ConsBy trans:x -> ma("ConsBy", x), IdxConsBy trans:x -> ma("IdxConsBy", x), JumpTable ([:c trans:e] -> ma(c, e))*:ces -> cat(ma("JumpTable"), ces), Interleave ([:m trans:p] -> ma(m, p))*:xs -> cat(ma("Interleave"), xs), Rule :name :locals trans:body -> ma("Rule", name, locals, body) } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa oAssociativeOptimization <: oNullOptimization { oAnd trans:x end setHelped -> x, oAnd transInside("oAnd"):xs -> cat(ma("oAnd"), xs), oOr trans:x end setHelped -> x, oOr transInside("oOr"):xs -> cat(ma("oOr"), xs), oXOr trans:x end setHelped -> x, oXOr transInside("oXOr"):xs -> cat(ma("oXOr"), xs), transInside :t = [exactly(t) transInside(t):xs] transInside(t):ys setHelped -> cat(xs, ys) | trans:x transInside(t):xs -> cat(ma(x), xs) | -> ma() } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa oOMetaOptimizer { optimizeGrammar = ['Grammar' :n :sn optimizeRule*:rs] -> cat(ma("Grammar", n, sn), rs), optimizeRule = :r (oAssociativeOptimization.optimize(r):r)* -> r } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa oSAParser { expr :inside = $( expr(true)* $) | $[ expr(true)* $] | ${ expr(true)* $} | $" (~$" ($* chr | $\\ chr | chr))* $" | ?inside ~($) | $] | $} | $") chr | letter letterOrDigit* ~($( | $.) | letter letterOrDigit* &$( expr(false) | letter letterOrDigit* $. letter letterOrDigit* (&$( expr(false) | empty) | letter letterOrDigit* $. &$( expr(false) | digit+ | $., semAction = , semActionsHelper = <(~$, expr(true))+>, semActions = listOf("semActionsHelper", ","), start = semAction } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" ometa oBCPLOMetaParser { space = ^space | fromTo("//", "\n") | fromTo("/**", "**/"), nameFirst = $_ | letter, nameRest = nameFirst | digit, tsName = , name = spaces tsName, eChr = $\\ chr:c -> unescape(join(ma("\\", c))) | chr, tsString = $' (~$' eChr)*:xs $' -> join(xs), characters = $` $` (~($' $') eChr)*:xs $' $' -> ma("App", "seq", programstring(join(xs))), sCharacters = $" (~$" eChr)*:xs $" -> ma("App", "token", programstring(join(xs))), string = (($# | $`) tsName | tsString):s -> ma("App", "exactly", programstring(s)), character = $$ eChr:x -> ma("App", "exactly", programstring(x)), number = <($- | empty) digit+>:x -> ma("App", "exactly", x), keyword :xs = token(xs) ~letterOrDigit -> xs, args = $( hostExprs:xs ")" -> xs | empty -> ma(), application = "^" name:rule args:aa -> cat(ma("SuperApp", rule), aa) | name:grm "." name:rule args:aa -> cat(ma("App", "foreign", programstring(grm), programstring(rule)), aa) | name:rule args:aa -> cat(ma("App", rule), aa), hostExpr = spaces oSAParser.semAction, hostExprs = spaces oSAParser.semActions, semAction = hostExpr:x -> ma("Act", x) | "!" hostExpr:x -> ma("Act", x), arrSemAction = "->" hostExpr:x -> ma("Ans", x), semPred = "?" hostExpr:x -> ma("Pred", x), expr = expr5(true):x ("|" expr5(true))+:xs -> cat(ma("oOr", x), xs) | expr5(true):x ("||" expr5(true))+:xs -> cat(ma("oXOr", x), xs) | expr5(false), expr5 :nempty = interleavePart:x ("&&" interleavePart)+:xs -> cat(ma("Interleave", x), xs) | expr4(nempty), interleavePart = "(" expr4(true):part ")" -> ma("1", part) | expr4(true):part modedIPart(part), modedIPart = [#And [#Many :part]] -> ma("**", part) | [#And [#Many1 :part]] -> ma("+", part) | [#And [#Opt :part]] -> ma("?", part) | :part -> ma("1", part), expr4 :nempty = expr3*:xs arrSemAction:act -> cat(ma("oAnd"), xs, ma(act)) | ?nempty expr3+:xs -> cat(ma("oAnd"), xs) | ?(not nempty) expr3*:xs -> cat(ma("oAnd"), xs), optIter :x = $* -> ma("Many", x) | $+ -> ma("Many1", x) | $? -> ma("Opt", x) | empty -> x, optBind :x = $: name:n { this.locals..push(n) } -> ma("Set", n, x) | empty -> x, expr3 = ":" name:n { this.locals..push(n) } -> ma("Set", n, ma("App", "anything")) | (expr2:x optIter(x) | semAction):e optBind(e) | semPred, expr2 = "~" expr2:x -> ma("oNot", x) | "&" expr1:x -> ma("Lookahead", x) | expr1, expr1 = application | ( keyword("undefined") | keyword("nil") | keyword("true") | keyword("false") ):x -> ma("App", "exactly", x) | spaces (characters | sCharacters | string | character | number) | "[" expr:x "]" -> ma("Form", x) | "<" expr:x ">" -> ma("ConsBy", x) | "@<" expr:x ">" -> ma("IdxConsBy", x) | "(" expr:x ")" -> x, ruleName = name | spaces tsString, rule = &(ruleName:n) { this.locals := vector..new() this.locals..push("_ans = true") } rulePart(n):x ("," rulePart(n))*:xs -> ma("Rule", n, this.locals..to_array(), cat(ma("oOr", x), xs)), rulePart :rn = ruleName:n ?($strcmp(n, rn) = 0) expr4(false):b1 ( "=" expr:b2 -> ma("oAnd", b1, b2) | empty -> b1 ), grammar = keyword("ometa") name:n ( "<:" name | empty -> "ometa" ):sn "{" listOf("rule", ","):rs "}" oOMetaOptimizer.optimizeGrammar( cat(ma("Grammar", n, sn), rs) ), start = grammar } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans") tree = BCPLOMetaParser.matchAll(""" // 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, so you can give your rules an "ML feel".) ometa oBCPLOMetaTranslator { App :rule anything*:args -> emitApp(rule, args), SuperApp :rule anything*:args -> emitSuperApp(rule, args), Ans :expr -> emitAns(expr), Act :expr -> emitAct(expr), Pred :expr -> emitPred(expr), oOr trans*:xs -> emitOr(xs), oXOr trans*:xs -> emitXOr(xs), oAnd trans*:xs -> emitAnd(xs), Opt trans:x -> emitOpt(x), Many trans:x -> emitMany(x), Many1 trans:x -> emitMany1(x), Set :n trans:v -> emitSet(n, v), oNot trans:x -> emitNot(x), Lookahead trans:x -> emitLookahead(x), Form trans:x -> emitForm(x), ConsBy trans:x -> emitConsBy(x), IdxConsBy trans:x -> emitIdxConsBy(x), JumpTable jtCase*:cases -> emitJumpTable(cases), Interleave intPart*:xs -> emitInterleave(xs), Rule :name {this.rName := name} locals:locs trans:body -> emitRule(this.rName, locs, body), Grammar :name :sName {this.name := name} {this.sName := sName} trans*:rules -> emitGrammar(this.name, this.sName, rules), intPart = [:mode trans:part] -> join(ma(programstring(mode), ",", part)), jtCase = [:x trans:e] -> ma(programstring(x), e), locals = [string+:vs] -> emitLocals(vs), trans = [:t apply(t):ans] -> ans, start = oBCPLOMetaParser.grammar:tree trans(tree) } """, "grammar") code = BCPLOMetaTranslator.match(tree, "trans")