With Groovy's nice closure support, we can avoid most anonymous class and hence get a much less cluttered parser code. In the [Tutorial] section, we described how a calculator can be built using Java API. We will now present a groovy version that is much shorter.
import jfun.parsec.pattern.Patterns; import jfun.parsec.*; import jfun.parsec.tokens.Tokenizers; def getCalculatorParser(){ def s_line_comment = Scanners.javaLineComment(); def s_block_comment = Scanners.isBlockComment("/*","*/"); def s_whitespace = Scanners.isWhitespaces(); def l_number = Lexers.decimal(); def ops = Terms.getOperatorsInstance("+","-","*","/", "(", ")"); def s_delim = [s_line_comment, s_block_comment, s_whitespace].first().many(); def l_tok = ops.getLexer() | l_number; def lexer = Lexers.lexeme(s_delim, l_tok).followedBy(Parsers.eof()); def p_binary_ops = ops.getParser(["+","-","*","/"] as String[]); def p_whitespace_mul = p_binary_ops.not(); def getOperator2 = {k,v->ops.getParser(k).seq(Map2.impl(v))}; def getOperator = {k,v->ops.getParser(k).seq(Map.impl(v))}; def p_plus = getOperator2("+"){a,b->a+b}; def p_minus = getOperator2("-"){a,b->a-b}; def p_mul = (ops.getParser("*") | p_whitespace_mul).seq(Map2.impl{a,b->a*b}); def p_div = getOperator2("/"){a,b->a/b}; def p_neg = getOperator("-"){a->-a}; def p_lparen = ops.getParser("("); def p_rparen = ops.getParser(")"); def p_number = Terms.decimalParser(FromString.impl{ int from, int len, String s -> Double.valueOf(s) }); def p_expr; def p_lazy_expr = {p_expr}.asParser(); def p_term = Parsers.between(p_lparen, p_rparen, p_lazy_expr)|p_number; def optable = new OperatorTable() .infixl(p_plus, 10) .infixl(p_minus, 10) .infixl(p_mul, 20) .infixl(p_div, 20) .prefix(p_neg, 30); p_expr = Expressions.buildExpressionParser(p_term, optable); def parser = Parsers.parseTokens(lexer, p_expr.followedBy(Parsers.eof()), "calculator"); } use(org.codehaus.parsec.groovy.ParserCategory){ def parser = getCalculatorParser(); println(parser.parse("1+(1-0) 3.0")); }
- org.codehaus.parsec.groovy.ParserCategory class provides many useful convenience methods that make parser construction handy.
- operator "|" is overloaded for the parser "plus" operation, where the second alternative parser is tried when the first fails.
- [a parser list].first() is equivalent to Parsers.sum(new Parser[]{a parser list}).
- SomeInterface.impl{some closure here} is a convenience function that adapts a closure to any target interface.
- groovy allows the use of local mutable variable in closures, that's why we don't need an array here to get around the "final" requirement in Java.
- everything else is similar to the Java version, just simpler.
The source code of the ParserCategory class can be downloaded here, binary here
Labels
(None)
