Antlr
ANTLR is a parser generator tool.
BITPlan has been using ANTLR in projects for a few years now and Wolfgang Fahl has been active in improving ANTLR see e.g. #Motivation
Library with helpers for ANTLR Language development com.bitplan.antlr
To simplify Parser development with ANTLR BITPlan has created a library with some helper code for ANTLR Language Development and published it as Open Source at:
Base Class Language Parser
The abstract base class LanguageParser has some help code that makes language development and debugging easier.
Example
Exp grammar
/**
* Copyright 2016-2017 BITPlan GmbH
* Author: Wolfgang Fahl
*
* this is an Example Antlr Grammar
*
*
* it is specified using antlr syntax and uses the ANTLR V4 parser generator
* see http://www.antlr.org
*
* for Eclipse you might want to install the IDE support:
* https://github.com/jknack/antlr4ide
*
*/
grammar Exp;
/* This will be the entry point of our parser. */
eval returns [double value]
: exp=additionExp {$value = $exp.value;}
;
/* Addition and subtraction have the lowest precedence. */
additionExp returns [double value]
: m1=multiplyExp {$value = $m1.value;}
( '+' m2=multiplyExp {$value += $m2.value;}
| '-' m2=multiplyExp {$value -= $m2.value;}
)*
;
/* Multiplication and division have a higher precedence. */
multiplyExp returns [double value]
: a1=atomExp {$value = $a1.value;}
( '*' a2=atomExp {$value *= $a2.value;}
// ...
)*
;
/* An expression atom is the smallest part of an expression: a number. Or
when we encounter parenthesis, we are making a recursive call back to the
rule 'additionExp'. As you can see, an 'atomExp' has the highest precedence. */
atomExp returns [double value]
: n=Number {$value = Double.parseDouble($n.text);}
| '(' exp=additionExp ')' {$value = $exp.value;}
;
/* A number: can be an integer value, or a decimal value */
Number
: ('0'..'9')+ ('.' ('0'..'9')+)?
;
/* We're going to ignore all white space characters */
WS
: (' ' | '\t' | '\r'| '\n') {
}
;
Java Source for ExpLanguage Parser
This code wraps the generated ExpParser into a class derived from LanguageParser
/**
* example parser
* @author wf
*
*/
public class ExpLanguageParser extends LanguageParser {
private ExpParser parser;
ExpLexer lexer;
public ExpParser getParser() {
return parser;
}
@Override
protected ParseTree getRootContext(Parser parser) {
if (!(parser instanceof ExpParser)) {
throw new RuntimeException("wrong parser type for getRootContext, expected Rule but got "+parser.getClass().getName());
} else {
ExpParser expParser=(ExpParser) parser;
return expParser.eval();
}
}
@Override
protected ParseTree parse(ANTLRInputStream in, String inputText)
throws Exception {
lexer = new ExpLexer(in);
parser=new ExpParser(getTokens(lexer));
ParseTree result=super.parse(lexer,getParser());
return result;
}
@Override
public void showParseTree() {
super.showParseTree(getParser());
}
}
JUnit Test
With the BaseTest abstract base Junit Test class testing and debugging gets easier. Take the following JUnit Test:
JUnit Test Source Code
@Test
public void testExpressionParser() throws Exception {
String expressions[] = { "2*3", "4+5", "(2+3)*(4+5)" };
ExpLanguageParser exprParser = new ExpLanguageParser();
for (String expression : expressions) {
super.runParser(exprParser, expression, 0);
}
}
if you add some invalid Expression to the expressions:
JUnit Test Source Code
String expressions[] = { "2*3", "4+5", "(2+3)*(4+5)","(4+5)--(6-7)" };
a parse Tree will show showing you what's wrong: