我有一个关于antlr4及其令牌优先级的问题。我有以下语法:
grammar TestGrammar;
@header {
package some.package;
}
fragment A : ('A'|'a') ;
fragment E : ('E'|'e') ;
fragment F : ('F'|'f') ;
fragment L : ('L'|'l') ;
fragment R : ('R'|'r') ;
fragment S : ('S'|'s') ;
fragment T : ('T'|'t') ;
fragment U : ('U'|'u') ;
BOOL : (T R U E | F A L S E) ;
AND : '&' ;
OR : '|' ;
IMPLIES : '=>' ;
AS : 'als' ;
ID : [a-zA-Z_][a-zA-Z0-9_]+ ;
value_assignment : AS name=ID ;
formula :
BOOL /*(variable=value_assignment)?*/ #ExpressionBoolean
| identifier=ID /*(variable=value_assignment)?*/ #ExpressionIdentifier
| leftFormula=formula operator=(AND | OR) rightFormula=formula /*(variable=value_assignment)?*/ #ExpressionBinaryAndOr
| leftFormula=formula operator=IMPLIES rightFormula=formula /*(variable=value_assignment)?*/ #ExpressionBinaryImplies
;
它被用来做一些命题逻辑。我希望它首先评估和或或,以及之后的含义。如果我使用的是提议的语法,它会像预期的那样起作用。请注意,value_assignment-规则已被注释掉。我有一些测试用例可以玩这个功能:
public class TestGrammarTest {
private static ParserRuleContext parse(final String input) {
final TestGrammarLexer lexer = new TestGrammarLexer(CharStreams.fromString(input));
final CommonTokenStream tokens = new CommonTokenStream(lexer);
return new TestGrammarParser(tokens).formula();
}
private static Set<Object> states() {
final Set<Object> states = new HashSet<Object>();
states.add(0);
states.add(1);
states.add(2);
return states;
}
@DataProvider (name = "testEvaluationData")
public Object[][] testEvaluationData() {
return new Object [][] {
{"true & false => true", states(), states()},
{"false & true => true", states(), states()},
};
}
@Test (dataProvider = "testEvaluationData")
public void testEvaluation(final String input, final Set<Object> states, final Set<Object> expectedResult) {
System.out.println("test evaluation of <" + input + ">");
Assert.assertEquals(
new TestGrammarVisitor(states).visit(parse(input)),
expectedResult
);
}
}
我认为我还需要向我的访客展示(一个较小版本的(,以使其行为清楚。正如你所期望的那样,实现是直接的:
public class TestGrammarVisitor extends TestGrammarBaseVisitor<Set<Object>> {
final Set<Object> states;
public TestGrammarVisitor(final Set<Object> theStates) {
states = Collections.unmodifiableSet(theStates);
}
@Override
public Set<Object> visitExpressionBoolean(final ExpressionBooleanContext ctx) {
System.out.println("nvisitExpressionBoolean called ...n");
final TerminalNode node = ctx.BOOL();
final Set<Object> result;
if (node.getText().equalsIgnoreCase("true")) {
result = new HashSet<>(states);
return result;
}
result = Collections.emptySet();
return result;
}
@Override
public Set<Object> visitExpressionBinaryAndOr(final ExpressionBinaryAndOrContext ctx) {
System.out.println("nvisitExpressionBinaryAndOr called ...n");
final Set<Object> result = new HashSet<>(super.visit(ctx.leftFormula));
switch (ctx.operator.getText()) {
case "&":
result.retainAll(super.visit(ctx.rightFormula));
return result;
case "|":
result.addAll(super.visit(ctx.rightFormula));
return result;
default:
throw new UnsupportedOperationException();
}
}
@Override
public Set<Object> visitExpressionBinaryImplies(final ExpressionBinaryImpliesContext ctx) {
System.out.println("nvisitExpressionBinaryImplies called ...n");
final Set<Object> result = new HashSet<>(states);
result.removeAll(super.visit(ctx.leftFormula));
result.addAll(super.visit(ctx.rightFormula));
return result;
}
@Override
protected Set<Object> aggregateResult(Set<Object> aggregate, Set<Object> nextResult) {
if (aggregate == null) {
return nextResult;
}
if (nextResult == null) {
return aggregate;
}
Set<Object> clone = new HashSet<>(aggregate);
clone.addAll(nextResult);
return clone;
}
}
我使用println语句来查看何时调用不同的规则。如果我测试所在的显示语法
(variable=value_assignment)?
被注释掉,输出如预期:
test evaluation of <true & false => true>
visitExpressionBinaryImplies called ...
visitExpressionBinaryAndOr called ...
visitExpressionBoolean called ...
visitExpressionBoolean called ...
visitExpressionBoolean called ...
test evaluation of <false & true => true>
visitExpressionBinaryImplies called ...
visitExpressionBinaryAndOr called ...
visitExpressionBoolean called ...
visitExpressionBoolean called ...
visitExpressionBoolean called ...
PASSED: testEvaluation("true & false => true", [0, 1, 2], [0, 1, 2])
PASSED: testEvaluation("false & true => true", [0, 1, 2], [0, 1, 2])
但是,当我包含这些语句时,优先级发生了变化:
test evaluation of <true & false => true>
visitExpressionBinaryAndOr called ...
visitExpressionBoolean called ...
visitExpressionBinaryImplies called ...
visitExpressionBoolean called ...
visitExpressionBoolean called ...
test evaluation of <false & true => true>
visitExpressionBinaryAndOr called ...
visitExpressionBoolean called ...
visitExpressionBinaryImplies called ...
visitExpressionBoolean called ...
visitExpressionBoolean called ...
PASSED: testEvaluation("true & false => true", [0, 1, 2], [0, 1, 2])
FAILED: testEvaluation("false & true => true", [0, 1, 2], [0, 1, 2])
java.lang.AssertionError: Sets differ: expected [0, 1, 2] but got []
正如你所看到的,言外之意将在连词之后被称为,这不是我想要的。此外,第一个测试用例意外通过了测试,因为不会满足预期的操作员优先级。有人能向我解释为什么运算符优先级会因为使用value_assignment-规则而更改吗(我刚刚删除了它周围的注释符号(?
非常感谢你的帮助!
经过一些尝试,我设法解决了以下问题:
grammar TestGrammar;
@header {
package some.package;
}
fragment A : ('A'|'a') ;
fragment E : ('E'|'e') ;
fragment F : ('F'|'f') ;
fragment L : ('L'|'l') ;
fragment R : ('R'|'r') ;
fragment S : ('S'|'s') ;
fragment T : ('T'|'t') ;
fragment U : ('U'|'u') ;
BOOL : (T R U E | F A L S E) ;
AND : '&' ;
OR : '|' ;
IMPLIES : '=>' ;
AS : 'als' ;
ID : [a-zA-Z_][a-zA-Z0-9_]+ ;
formula :
BOOL #ExpressionBoolean
| leftFormula=formula operator=(AND | OR) rightFormula=formula #ExpressionBinaryAndOr
| leftFormula=formula operator=IMPLIES rightFormula=formula #ExpressionBinaryImplies
| innerFormula=formula AS storageName=ID #ExpressionAssignment
| identifier=ID #ExpressionIdentifier
;
因此,我将把存储能力作为一个单独的公式来处理。这并不是我想要做的(它迫使我为每个子公式提供存储选项,如果特定子公式需要或不需要存储行为,我必须在访问者中管理它(。但是,我可以忍受周围的工作。