// Sample programs to compare with my implementation. // My first hand translated project: ometa M { number = number:n digit:d -> { n * 10 + d.digitValue() } | digit:d -> { d.digitValue() } } M.matchAll("123456789", "number") // My second hand translated project: ometa Calc { digit = super(#digit):d -> d.digitValue(), number = number:n digit:d -> (n * 10 + d) | digit, addExpr = addExpr:x '+' mulExpr:y -> (x + y) | addExpr:x '-' mulExpr:y -> (x - y) | mulExpr, mulExpr = mulExpr:x '*' primExpr:y -> (x * y) | mulExpr:x '/' primExpr:y -> (x / y) | primExpr, primExpr = '(' expr:x ')' -> x | number, expr = addExpr } Calc.matchAll('1+2', 'expr') // Worked on 6/7/2008 around 1:30 EDT // My third hand translated project: ometa littleTypechecker <: Parser { typecheck = term:t spaces end -> t, term = "0" -> "Int" | "1" -> "Int" | "true" -> "Bool" | "false" -> "Bool" | "isZero" term:t ?(t == "Int") -> "Bool" | "if" term:t1 "then" term:t2 "else" term:t3 ?(t1 == "Bool") ?(t2 == t3) -> t2 } program = " if isZero 1 then if true then 0 else 1 else 1 " littleTypechecker.matchAll(program, "typecheck") // Worked around 11:20 PM EDT on 6/7/2008 ometa M { fact 0 -> 1, fact :n ?(n > 0) fact(n - 1):m -> (n * m) } M.match(5, "fact") 120 ometa CalcParser { digit = super(#digit):d -> d.digitValue(), number = number:n digit:d -> (n * 10 + d) | digit, addExpr = addExpr:x '+' mulExpr:y -> ['add', x, y] | addExpr:x '-' mulExpr:y -> ['sub', x, y] | mulExpr, mulExpr = mulExpr:x '*' primExpr:y -> ['mul', x, y] | mulExpr:x '/' primExpr:y -> ['div', x, y] | primExpr, primExpr = '(' expr:x ')' -> x | number:n -> ['num', n], expr = addExpr } tree = CalcParser.matchAll('6*(4+3)', 'expr') ometa CalcInterpreter { interp = ['num' anything:x] -> x | ['add' interp:x interp:y] -> (x + y) | ['sub' interp:x interp:y] -> (x - y) | ['mul' interp:x interp:y] -> (x * y) | ['div' interp:x interp:y] -> (x / y) } CalcInterpreter.match(tree, 'interp') // Above worked around 11:20 PM on 6/8/2008 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) } parse = function(g) { return OMetaParser.matchAll(g, "grammar") } parse("ometa M { Fact 0 -> 1, Fact :n ?(n > 0) Fact(n - 1):m -> (n * m) }") [Grammar, M, OMeta, [Rule, Fact, [$elf=this, n, m], [Or, [And, [App, exactly, 0], [Act, (1)]], [And, [Set, n, [App, anything]], [Pred, (n > (0))], [Set, m, [App, Fact, (n - (1))]], [Act, (n * m)]]]]] My result: [Grammar, M, OMeta, [Rule, Fact, [var m, n], [Or, [And, [App, Exactly, 0], [Act, [App, Seq, 1]]]], [And, [Set, n, [App, Anything]], [Pred, [App, Seq, (n > 0)]], [Set, m, [App, Fact, [App, Seq, n - 1]]], [Act, [App, Seq, (n * m)]]]]] // ometa ArithmeticCalc { digit = super(#digit):d -> d.digitValue(), number = number:n digit:d -> (n * 10 + d) | digit, addExpr = addExpr:x '+' mulExpr:y -> (x + y) | addExpr:x '-' mulExpr:y -> (x - y) | mulExpr, mulExpr = mulExpr:x '*' primExpr:y -> (x * y) | mulExpr:x '/' primExpr:y -> (x / y) | primExpr, primExpr = '(' expr:x ')' -> x | number, expr = addExpr } ometa ExponentCalc <: ArithmeticCalc { mulExpr = mulExpr:x '^' primExpr:e -> (Math.pow(x,e)) | super(#mulExpr) } ometa ScientificCalc <: Parser { func :n = token(n) spaces, advExp = func("sqrt") advExp:x -> Math.sqrt(x) | facExp, facExp = primExpr:x '!' -> {r = 1; while(x > 1) { r = r * x; x-- } r} | primExpr, primExpr = foreign(ExponentCalc, #expr):x -> x, expr = advExp } ScientificCalc.matchAll('sqrt (1+2^3)!', 'expr') 602.3952191045344 // FUTURE Bootstrap compiler Use bootstrapped compiler to create optimizer Port Prolog library