繁体   English   中英

antlr4:运算符优先级更改

[英]antlr4: operator precedence changes

我有一个关于 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
;

它用于做一些命题逻辑。 我希望它先评估andor ,然后再评估。 如果我使用建议的语法,它会按预期工作。 请注意, 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 -rule 而发生变化(我刚刚删除了它周围的注释符号)?

非常感谢您的帮助!

经过一些尝试,我设法解决了这个问题,如下所示:

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
;

所以,我将把存储能力作为一个单独的公式来处理。 这并不是我想要做的(它迫使我为每个子公式提供存储选项,如果特定子公式需要或不需要存储行为,我必须在访问者中管理它)。 但是,我可以忍受这种变通办法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM