如何通过此语法解析(A * B)> C字符串?

时间:2013-03-01 10:02:56

标签: antlr context-free-grammar recursive-descent

问题是因为 - 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

1 个答案:

答案 0 :(得分:1)

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

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


要求ANTLR保持表达式类型不同需要告诉ANTLR当运算符对多种表达式类型有效时该怎么做。例如,在下面的语法LogicRules中,词法分析器标记LPAR标记从规则compExpr开始的逻辑表达式或从primaryExpr开始的算术表达式的开头。默认情况下,ANTLR无法知道输入"(3..."是标记逻辑表达式分组("(3 + 3 == 6)")的开头还是算术/数值表达式分组("(3 + 3) == 6")的开头。规则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

预期结果: True

LogicRules结果: True

通过LogicRules? true

LogicEval结果: True

LogicEval通过了吗?是真的


输入: 1 + 2 == 4

预期结果:错误

LogicRules结果:错误

通过LogicRules? true

LogicEval结果:错误

LogicEval通过了吗?是真的


输入 1 + 2 == 3&amp;&amp; 1 + 2 == 4

预期结果:错误

LogicRules结果:错误

通过LogicRules? true

LogicEval结果:错误

LogicEval通过了吗?是真的


输入 1 + 2 == 3&amp;&amp; 4 + 5 == 9

预期结果: True

LogicRules结果: True

通过LogicRules? true

LogicEval结果: True

LogicEval通过了吗?是真的


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

预期结果: True

LogicRules结果: True

通过LogicRules? true

LogicEval结果: True

LogicEval通过了吗?是真的


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

预期结果:失败

LogicRules结果:失败

通过LogicRules? true

LogicEval结果:失败

LogicEval通过了吗?是真的


输入: 1&amp;&amp; 2

预期结果:失败

LogicRules结果:失败

通过LogicRules? true

LogicEval结果:失败

LogicEval通过了吗?是真的


输入: 1 + 2

预期结果:失败

LogicRules结果:失败

通过LogicRules? true

LogicEval结果:失败

LogicEval通过了吗?是真的