//////////////////////////////////////////////////////// // a simple recognizer, produces no useful value Parse error Parse error -> ->e //////////////////////////////////////////////////////// ometa L { number = digit+, addExpr = addExpr '+' mulExpr | addExpr '-' mulExpr | mulExpr, mulExpr = mulExpr '*' primExpr | mulExpr '/' primExpr | primExpr, primExpr = '(' expr ')' | number, expr = addExpr } L.matchAll('6*(4+3)', 'expr') //////////////////////////////////////////////////////// // a recognizer that also interprets //////////////////////////////////////////////////////// ometa Calc { digit = ^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('6*(4+3)', 'expr') //////////////////////////////////////////////////////// // parser and simple interpreter combo //////////////////////////////////////////////////////// ometa CalcParser { digit = ^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') //////////////////////////////////////////////////////// // we can write a "compiler" instead //////////////////////////////////////////////////////// ometa CalcParser { digit = ^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 CalcCompiler { comp = ['num' anything:x] -> x.toString() | ['add' comp:x comp:y] -> ('(' + x + '+' + y + ')') | ['sub' comp:x comp:y] -> ('(' + x + '-' + y + ')') | ['mul' comp:x comp:y] -> ('(' + x + '*' + y + ')') | ['div' comp:x comp:y] -> ('(' + x + '/' + y + ')') } code = CalcCompiler.match(tree, 'comp') eval(code) // spice things up with ML-like syntax ometa CalcCompiler { comp ['num' anything:x] -> x.toString(), comp ['add' comp:x comp:y] -> ('(' + x + '+' + y + ')'), comp ['sub' comp:x comp:y] -> ('(' + x + '-' + y + ')'), comp ['mul' comp:x comp:y] -> ('(' + x + '*' + y + ')'), comp ['div' comp:x comp:y] -> ('(' + x + '/' + y + ')') } code = CalcCompiler.match(tree, 'comp') eval(code) // a neat trick: dispatch on node tags using higher-order rule "apply" ometa CalcCompiler { comp [anything:t apply(t):ans] -> ans, num anything:x -> x.toString(), add comp:x comp:y -> ('(' + x + '+' + y + ')'), sub comp:x comp:y -> ('(' + x + '-' + y + ')'), mul comp:x comp:y -> ('(' + x + '*' + y + ')'), div comp:x comp:y -> ('(' + x + '/' + y + ')') } code = CalcCompiler.match(tree, 'comp') eval(code) // I have a question. Why does this print '6' rather than '42'? // (I took the definition of "Calc" and split "addExpr" into two pieces.) ometa CalcQuestion1 { digit = ^digit:d -> d.digitValue(), number = number:n digit:d -> (n * 10 + d) | digit, addExpr = addExpr2:x -> x, addExpr2 = 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 } CalcQuestion1.matchAll('6*(4+3)', 'expr') // Here is a similar case. I would expect to see '42' and not '6'. // Am I misunderstanding something? // (In this case, I modified "addExpr" to immediately try itself first.) ometa CalcQuestion2 { digit = ^digit:d -> d.digitValue(), number = number:n digit:d -> (n * 10 + d) | digit, addExpr = addExpr:x -> x | 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 } CalcQuestion2.matchAll('6*(4+3)', 'expr') 6 // StatefulCalculator, Rüdiger Plantiko, NOV-10-2010 // See a working example on separate project #StatefulCalculator ometa StatefulCalculator <: Parser { var = letter:x -> x, num = num:n digit:d -> (n*10 + d*1) | digit:d -> (d*1), primaryExpr = spaces var:x -> self.vars[x] | spaces num:n -> n | "(" expr:r ")" -> r, mulExpr = mulExpr:x "*" primaryExpr:y -> ( x * y ) | mulExpr:x "/" primaryExpr:y -> ( x / y ) | primaryExpr, addExpr = addExpr:x "+" mulExpr:y -> ( x + y ) | addExpr:x "-" mulExpr:y -> ( x - y ) | mulExpr, expr = var:x "=" expr:r -> (self.vars[x] = r) | addExpr, doit = (expr:r)* spaces end -> r } StatefulCalculator.initialize = function() { this.vars = {}; } // Set a variable x to 1, so self.vars.x will be 1 StatefulCalculator.matchAll( 'x=1', 'doit' ); StatefulCalculator.matchAll( 'x+x', 'doit' ); // ^ will print NaN, not 2, as I would expect // When you call matchAll(), you're creating a new instance of the calculator grammar, // in which the calculator's state -- e.g., the dictionary that maps variable names // to values -- is stored. (Take a ook at the initialize method, which creates this // dictionary.) So it makes perfect sense that the value of x doesn't persist through // different runs of the calculator. This explains why you're getting a NaN above, and // also why it works below. // -- Alex StatefulCalculator.matchAll( 'x=3 x+x', 'doit' ); // ^ will print 6, as I would expect. As an answer to previous question.