ometa BSOMetaParser <: 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                                         -> ometaUnescape('\\' +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, rule].concat(as),
  hostExpr       = foreign(BSJSParser, #expr):r                           foreign(BSJSTranslator, #trans, r),
  atomicHostExpr = foreign(BSJSParser, #semAction):r                      foreign(BSJSTranslator, #trans, r),
  curlyHostExpr  = foreign(BSJSParser, #curlySemAction):r                 foreign(BSJSTranslator, #trans, r),
  semAction      = ("!" | "->") atomicHostExpr:x                       -> [#Act, x]
                 | curlyHostExpr: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 "}"                        foreign(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 an ML "feel".)
ometa BSOMetaTranslator {
  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, '=Object.delegated(',sName,',{', rules.join(','), '})']      .join(''),
  locals  = [string+:vs]              -> ['var ', vs.join(','), ';']                                    .join('')
          | []                        -> '',
  transFn = trans:x                   -> ['(function(){return ', x, '})']                               .join('')
}
