0

问题是因为 -- addExpression ((EQ | NE | GT | LT | LE | GE) addExpression)+ --- 。通过这样做,我想过滤掉单独的数学表达式,除非后面跟着一个关系以使其整体合乎逻辑......我不希望我的解析器单独解析数学表达式......

expression
    :
        logicalExpression
    ;

primitive
    :
        DECIMAL
    |   UNSIGNED_INTEGER
    |   SIGNED_INTEGER
    |   VARIABLE
    |   '(' logicalExpression ')'
    |   '{' logicalExpression '}'
    |   '[' logicalExpression ']'
    ;

notExpression
    :
        (NOT)* primitive
    ;

signExpression
    :
        ( '-' | '+' )* notExpression
    ;

mulExpression
    :
        signExpression ( ( '*' | '/' ) signExpression)*
    ;

addExpression
    :
        mulExpression ( ( '+' | '-' ) mulExpression)*
    ;

relationalExpression
    :
        addExpression ((EQ | NE | GT | LT | LE | GE) addExpression)+
    |   '(' VARIABLE ')'
    |   '{' VARIABLE '}'
    |   '[' VARIABLE ']'
    |       VARIABLE
    ;

测试:

Input string: (5) --> Expected: to fail -- Current:failed
    Input string: 5*6 --> Expected: to fail -- Current:failed
    Input string: A-B --> Expected: to fail -- Current:failed
    Input string: A/B --> Expected: to fail -- Current:failed
    Input string: 5/A --> Expected: to fail -- Current:failed
    Input string: --2 --> Expected: to fail -- Current:failed
    Input string: 5++ --> Expected: to fail -- Current:failed
    Input string: A-B and B-A --> Expected: to fail -- Current:failed
    Input string: (A and B)--> Expected: to pass {AND(A,B)} -- Current:failed
    Input string: A and B-->   Expected: to pass AND(A,B) -- Current:failed
    Input string: (5)>6 --> Expected: to pass {GT(5,6)} -- Current:failed
    Input string:  5>6 --> Expected: to pass  {GT(5,6)} -- Current:pass
    Input string: 5*6 < 6-5 --> Expected: to pass  GT(MUL(5,6),SUB(5,6)) --Current:pass
    Input string: A/B == B/A --> Expected: to pass  -- Current:pass
    Input string: (A/B)=B/A --> Expected: to pass  -- Current:failed
    Input string: 5 /= 5 --> Expected: to pass  -- Current:pass
4

1 回答 1

1

下面是两个简单的 ANTLR 语法,演示了两种评估逻辑和算术表达式混合的方法,然后是测试类及其输出。第一个语法采用您要求的形式,使用 ANTLR 确定表达式类型并确保表达式值兼容。另一种形式使用语法中的动作来确定表达式类型。我在这里提供它以进行比较。

我在两种语法中都对表达式求值进行了硬编码,以使示例更有用并显示它们按预期工作。请注意,除了您在下面的测试代码中看到的内容之外,我还没有测试过这些语法。此外,我只包括了演示感兴趣的概念所必需的运算符。添加其他人应该很容易。


要求 ANTLR 保持表达式类型不同需要告诉 ANTLR 当运算符对多个表达式类型有效时该怎么做。例如在下面的语法LogicRules中,词法分析器标记LPAR标记以 rule 开头的逻辑表达式compExpr或以 开头的算术表达式的开头primaryExpr。默认情况下,ANTLR 无法知道 input"(3..."是逻辑表达式分组 ( "(3 + 3 == 6)") 的开头还是算术/数字表达式分组 ( "(3 + 3) == 6") 的开头。rule 中的句法谓词compExpr用于帮助 ANTLR 区分这两种选择,实际上是告诉它等待和观察。

LogicRules.g — 让 ANTLR 规则区分布尔表达式和数字表达式

grammar LogicRules;

statement returns [boolean result]
    : logicalExpr {$result = $logicalExpr.result;} EOF
    ;

logicalExpr returns [boolean result]
    @init { $result = true;}
    : lhs=compExpr {$result = $lhs.result;} 
        (AND rhs=compExpr 
            {$result = $result && $rhs.result;}
        )*
    ;

compExpr returns [boolean result]
    @init { $result = true;}
    : (eqExpr) => eqExpr {$result = $eqExpr.result;}
        //^^ Needs a syntactic predicate to differentiate between logical grouping and arithmetic grouping
    | LPAR logicalExpr {$result = $logicalExpr.result;} RPAR
        //^^ Only group logical expressions at this level. 
    ;

eqExpr returns [boolean result]
@init {$result = true;}
    : lhs=arithmeticExpr 
        (EQ rhs=arithmeticExpr 
            {$result = $result && $lhs.result == $rhs.result;}
        )+ 
        //^^ use +: a logical expression of arithmetic expressions
        //          requires a logical operation to produce a boolean value
    ;

arithmeticExpr returns [int result]
    @init {$result = 0;}
    : lhs=primaryExpr {$result += $lhs.result;} 
        (PLUS rhs=primaryExpr 
            {$result += $rhs.result;}
        )* 
    ;

primaryExpr returns [int result]
    @init {$result = 0;}
    :   INT {$result = $INT.int;}
    |   LPAR arithmeticExpr RPAR {$result = $arithmeticExpr.result;}
        //^^ Only group other numeric/arithmetic expressions at this level.
    ;

INT     : ('0'..'9')+;
AND     : '&&';
EQ      : '==';
LPAR    : '(';
RPAR    : ')';
PLUS    : '+'; 
WS      : (' '|'\t'|'\r'|'\n')+ {skip();};

使用代码而不是 ANTLR 规则评估表达式的结果类型会导致如下文法。请注意,分组仅在一个级别完成,不需要句法谓词。整体语法保持相对简单。和前面的语法一样,这个语法在遇到无效的类型转换时会产生错误。

LogicEval.g — 让代码区分布尔表达式和数字表达式

grammar LogicEval;

@parser::members {
    private static boolean toBoolean(Object obj){
        if (obj instanceof Boolean){
            return (Boolean)obj;
        } else { 
            throw new RuntimeException("Cannot convert " + obj + " to boolean");
        }
    } 

    private static int toInt(Object obj){
        if (obj instanceof Integer){
            return (Integer)obj;
        } else { 
            throw new RuntimeException("Cannot convert " + obj + " to integer");
        }
    } 
}

statement returns [Object result]
    : expr {$result = $expr.result;} EOF
    ;

expr returns [Object result]
    : lhs=compExpr {$result = $lhs.result;} 
        (AND rhs=compExpr 
            {$result = toBoolean($result) && toBoolean($rhs.result);} 
        )*
    ;

compExpr returns [Object result]
    @init {Object lhsResult = null;}
    : lhs=arithmeticExpr {$result = lhsResult = $lhs.result;}
        (EQ rhs=arithmeticExpr
            {$result = toInt(lhsResult) == toInt($rhs.result);} 
        )* 
    ;

arithmeticExpr returns [Object result]
    : lhs=primaryExpr {$result = $lhs.result;}
        (PLUS rhs=primaryExpr
            {$result = toInt($result) + toInt($rhs.result);}
        )*
    ;

primaryExpr returns [Object result]
    :   INT {$result = $INT.int;}
    |   LPAR expr RPAR {$result = $expr.result;}
    ;

INT     : ('0'..'9')+;
AND     : '&&';
EQ      : '==';
LPAR    : '(';
RPAR    : ')';
PLUS    : '+'; 
WS      : (' '|'\t'|'\r'|'\n')+ {skip();};

LogicTest.java — 两种语法的测试代码

public class LogicTest {

    public static void main(String[] args) {
        test("1 + 2 == 3", Result.True);
        test("1 + 2 == 4", Result.False);
        test("1 + 2 == 3 && 1 + 2 == 4", Result.False);
        test("1 + 2 == 3 && 4 + 5 == 9", Result.True);
        test("(1 + 2) == 3 && (4 + 5 == 9)", Result.True);
        test("1 + 2 == (3 && 4 + 5 == 9)", Result.Failure);
        test("1 && 2", Result.Failure);
        test("1 + 2", Result.Failure);
    }

    private static void test(String rawInput, Result expectedResult){
        Result rulesResult = runRules(rawInput);
        Result evalResult = runEval(rawInput);
        System.out.println("---\n");
        System.out.printf("**Input:** %s%n%n", rawInput);
        System.out.printf("**Expected Result:** %s%n%n", expectedResult);
        System.out.printf("**LogicRules Result:** %s%n%n", rulesResult);
        System.out.printf("**LogicRules Passed?** %s%n%n", (rulesResult == expectedResult));
        System.out.printf("**LogicEval Result:** %s%n%n", evalResult);
        System.out.printf("**LogicEval Passed?** %s%n%n", (evalResult == expectedResult));
    }

    private static Result runRules(String rawInput){
        CharStream input = new ANTLRStringStream(rawInput);
        LogicRulesLexer lexer = new LogicRulesLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        LogicRulesParser parser = new LogicRulesParser(tokens);

        boolean result;
        try {
            result = parser.statement();
        } catch (Exception e) {
            return Result.Failure;
        }

        if (lexer.getNumberOfSyntaxErrors() > 0 || parser.getNumberOfSyntaxErrors() > 0){
            return Result.Failure;
        }

        return result ? Result.True : Result.False;
    }

    private static Result runEval(String rawInput){
        CharStream input = new ANTLRStringStream(rawInput);
        LogicEvalLexer lexer = new LogicEvalLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);

        LogicEvalParser parser = new LogicEvalParser(tokens);

        Object result;
        try {
            result = parser.statement();
        } catch (Exception e) {
            return Result.Failure;
        }

        if (lexer.getNumberOfSyntaxErrors() > 0 || parser.getNumberOfSyntaxErrors() > 0){
            return Result.Failure;
        }

        if (result instanceof Boolean){
            return ((Boolean)result) ? Result.True : Result.False;
        } else { 
            return Result.Failure; //Produced a valid result, but it wasn't a boolean.
        }
    }

    private static enum Result { 
        True, False, Failure;
    }
}

输出——所有测试通过


输入: 1 + 2 == 3

预期结果:

逻辑规则结果:

逻辑规则通过了吗?真的

LogicEval 结果:

逻辑评估通过了吗?真的


输入: 1 + 2 == 4

预期结果:错误

逻辑规则结果:

逻辑规则通过了吗?真的

LogicEval 结果:

逻辑评估通过了吗?真的


输入: 1 + 2 == 3 && 1 + 2 == 4

预期结果:错误

逻辑规则结果:

逻辑规则通过了吗?真的

LogicEval 结果:

逻辑评估通过了吗?真的


输入: 1 + 2 == 3 && 4 + 5 == 9

预期结果:

逻辑规则结果:

逻辑规则通过了吗?真的

LogicEval 结果:

逻辑评估通过了吗?真的


输入: (1 + 2) == 3 && (4 + 5 == 9)

预期结果:

逻辑规则结果:

逻辑规则通过了吗?真的

LogicEval 结果:

逻辑评估通过了吗?真的


输入: 1 + 2 == (3 && 4 + 5 == 9)

预期结果:失败

LogicRules 结果:失败

逻辑规则通过了吗?真的

LogicEval 结果:失败

逻辑评估通过了吗?真的


输入: 1 && 2

预期结果:失败

LogicRules 结果:失败

逻辑规则通过了吗?真的

LogicEval 结果:失败

逻辑评估通过了吗?真的


输入: 1 + 2

预期结果:失败

LogicRules 结果:失败

逻辑规则通过了吗?真的

LogicEval 结果:失败

逻辑评估通过了吗?真的

于 2013-03-04T21:49:07.637 回答