Question

Issue is because of -- addExpression ((EQ | NE | GT | LT | LE | GE) addExpression)+ --- . By doing this I want to filter the math expressions from being alone unless followed by a relational to make it logical as a whole... I don't want my parser to parse math expressions if alone...

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
    ;

Tests:

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
Was it helpful?

Solution

Below are two simple ANTLR grammars that demonstrate two ways to evaluate a mix of logical and arithmetic expressions, followed by a test class and its output. The first grammar is in the form you requested, using ANTLR to determine the expression type and to ensure that expression values are compatible. The other form determines the expression type using actions within the grammar. I'm providing it here for comparison.

I hard-coded expression evaluation in both grammars to make the examples more useful and to show that they work as expected. Please note that I haven't tested these grammars beyond what you see in the test code below. Also, I only included the operators necessary to demonstrate the concepts of interest. It should be easy enough to add others.


Asking ANTLR to keep the expression types distinct requires telling ANTLR what to do when an operator is valid for more than one expression type. For example in grammar LogicRules below, lexer token LPAR marks the beginning of either a logical expression beginning in rule compExpr or an arithmetic expression beginning in primaryExpr. ANTLR can't know by default whether input "(3..." marks the beginning of a logical expression grouping ("(3 + 3 == 6)") or the beginning of an arithmetic/numeric expression grouping ("(3 + 3) == 6"). The syntactic predicate in rule compExpr is used to help ANTLR distinguish between these two choices by telling it, in effect, to wait and see.

LogicRules.g — Let ANTLR rules distinguish between boolean and numeric expressions

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();};

Evaluating an expression's result type using code rather than ANTLR rules leads to a grammar like the following. Note that grouping is done only at one level and with no need for a syntactic predicate. The grammar overall is kept relatively simple. Like the previous grammar, this grammar produces an error when it encounters an invalid type conversion.

LogicEval.g — Let code distinguish between boolean and numeric expressions

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 — Test code for the two grammars

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;
    }
}

Output — All tests pass


Input: 1 + 2 == 3

Expected Result: True

LogicRules Result: True

LogicRules Passed? true

LogicEval Result: True

LogicEval Passed? true


Input: 1 + 2 == 4

Expected Result: False

LogicRules Result: False

LogicRules Passed? true

LogicEval Result: False

LogicEval Passed? true


Input: 1 + 2 == 3 && 1 + 2 == 4

Expected Result: False

LogicRules Result: False

LogicRules Passed? true

LogicEval Result: False

LogicEval Passed? true


Input: 1 + 2 == 3 && 4 + 5 == 9

Expected Result: True

LogicRules Result: True

LogicRules Passed? true

LogicEval Result: True

LogicEval Passed? true


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

Expected Result: True

LogicRules Result: True

LogicRules Passed? true

LogicEval Result: True

LogicEval Passed? true


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

Expected Result: Failure

LogicRules Result: Failure

LogicRules Passed? true

LogicEval Result: Failure

LogicEval Passed? true


Input: 1 && 2

Expected Result: Failure

LogicRules Result: Failure

LogicRules Passed? true

LogicEval Result: Failure

LogicEval Passed? true


Input: 1 + 2

Expected Result: Failure

LogicRules Result: Failure

LogicRules Passed? true

LogicEval Result: Failure

LogicEval Passed? true

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top