我有这样的语法:
/* entry point */
parse: expr EOF;
expr
: value # argumentArithmeticExpr
| l=expr operator=(MULT|DIV) r=expr # multdivArithmeticExpr
| l=expr operator=(PLUS|MINUS) r=expr # addsubtArithmeticExpr
| operator=('-'|'+') r=expr # minusPlusArithmeticExpr
| IDENTIFIER '(' (expr ( COMMA expr )* ) ? ')'# functionExpr
| LPAREN expr RPAREN # parensArithmeticExpr
;
value
: number
| variable
| string // contains date
| bool
| null_value
;
/* Atomes */
bool
: BOOL
;
variable
: VARIABLE
;
string
: STRING_LITERAL
;
number
: ('+'|'-')? NUMERIC_LITERAL
;
null_value
: NULL // TODO: test this
;
IDENTIFIER
: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
;
NUMERIC_LITERAL
: DIGIT+ ( '.' DIGIT* )? ( E [-+]? DIGIT+ )? // ex: 0.05e3
| '.' DIGIT+ ( E [-+]? DIGIT+ )? // ex: .05e3
;
INT: DIGIT+;
STRING_LITERAL
: '\'' ( ~'\'' | '\'\'' )* '\''
| '"' ( ~'"' | '""' )* '"'
;
VARIABLE
: LBRACKET ( ~']' | ' ')* RBRACKET
;
现在,我想解析一下:
-1.3 * 5 + -2 * 7
有了Grun,我明白了:
antlr4 formula.g4 && javac *.java && time grun formula parse -gui
-1.3*5 + -2*7
^D
看起来不错,我会很高兴。
但是在我的Java代码中,我使用访问者模式进行调用:
visitMinusPlusArithmeticExpr -1.3*5+-2*7 // ugh ?? sees "- (1.3 * 5 + - 2 * 7 )" instead of "(-1.3*5) + (-2*7)"
visitAddsubtArithmeticExpr 1.3*5+-2*7
visitMultdivArithmeticExpr 1.3*5
visitArgumentArithmeticExpr 1.3
visitNumber 1.3
visitArgumentArithmeticExpr 5
visitValue 5
visitNumber 5
visitMinusPlusArithmeticExpr -2*7 // UHG? should see a MultDiv with -2 and 7
visitMultdivArithmeticExpr 2*7
visitArgumentArithmeticExpr 2
visitValue 2
visitNumber 2
visitArgumentArithmeticExpr 7
visitValue 7
visitNumber 7
这意味着我没有得到我的负数(-1.3),而是我不应该得到的减去表达式。
为什么我的Java结果与Grun不同?我已经验证语法是重新编译的,我使用我的解析器:
formulaLexer lexer = new formulaLexer(new ANTLRInputStream(s));
formulaParser parser = new formulaParser(new CommonTokenStream(lexer));
parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
parser.setErrorHandler(new BailErrorStrategy()); // will throw exceptions on failure
formula = tryParse(parser);
if( formula == null && errors.isEmpty() ){
// the parsing failed, retry in LL mode
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
parser.reset();
tryParse(parser);
}
我已禁用SLL模式以验证这不是问题,结果是一样的。
我认为这可能是一个优先问题,但在expr
我已经指定首先匹配value
,然后只匹配minusPlusArithmeticExpr
。
我无法理解我将如何发现这个'减去'表达而不是我的负值'。你能检查一下吗?
另外,为什么Grun会显示正确的行为而不是我的Java代码?
根据评论建议,我将语法修改为如下所示:
expr
: value # argumentArithmeticExpr
| (PLUS|MINUS) expr # plusMinusExpr
| l=expr operator=(MULT|DIV) r=expr # multdivArithmeticExpr
| l=expr operator=(PLUS|MINUS) r=expr # addsubtArithmeticExpr
| function=IDENTIFIER '(' (expr ( COMMA expr )* ) ? ')'# functionExpr
| '(' expr ')' # parensArithmeticExpr
;
但是现在,我想优化我有一个" -1.3"某处。
我不知道如何正确地做到这一点,因为当我登陆visitMinusPlusAritmeticExpr
时,我必须检查终端节点是否是数字。
以下是我在调试时得到的内容:
ctx = {formulaParser$PlusMinusExprContext@929} "[16]"
children = {ArrayList@955} size = 2
0 = {TerminalNodeImpl@962} "-"
1 = {formulaParser$ArgumentArithmeticExprContext@963} "[21 16]"
children = {ArrayList@967} size = 1
0 = {formulaParser$ValueContext@990} "[22 21 16]"
children = {ArrayList@992} size = 1
0 = {formulaParser$NumberContext@997} "[53 22 21 16]"
children = {ArrayList@999} size = 1
0 = {TerminalNodeImpl@1004} "1.3"
我怀疑我应该走在树下,告诉终端节点是否是一个数字,但这看起来很麻烦。您是否知道如何在不影响我的代码易读性的情况下做到这一点?
答案 0 :(得分:0)
好的,对于那些感兴趣的人,Lucas和Bart得到了答案,我的实现是这样的:
expr
: value # argumentArithmeticExpr
| (PLUS|MINUS) expr # plusMinusExpr
| l=expr operator=(MULT|DIV) r=expr # multdivArithmeticExpr
| l=expr operator=(PLUS|MINUS) r=expr # addsubtArithmeticExpr
| function=IDENTIFIER '(' (expr ( COMMA expr )* ) ? ')'# functionExpr
| '(' expr ')' # parensArithmeticExpr
;
在plusMinusExpr的访客中:
@Override
public Formula visitPlusMinusExpr(formulaParser.PlusMinusExprContext ctx) {
if( debug ) LOG.log(Level.INFO, "visitPlusMinusExpr " + ctx.getText());
Formula formulaExpr = visit(ctx.expr());
if( ctx.MINUS() == null ) return formulaExpr;
else {
if(formulaExpr instanceof DoubleFormula){
// optimization for numeric values: we don't return "(0.0 MINUS THEVALUE)" but directly "-THEVALUE"
Double v = - ((DoubleFormula) formulaExpr).getValue();
return new DoubleFormula( v );
} else {
return ArithmeticOperator.MINUS( 0, formulaExpr);
}
}
}