简体   繁体   中英

Nested Boolean Expression Parser using ANTLR

["

I'm trying to parse a Nested Boolean Expression and get the individual conditions within the expression separately.<\/i>

grammar SimpleBoolean;

rule_set : nestedCondition* EOF;

AND : 'AND' ;
OR  : 'OR' ;
NOT : 'NOT';

TRUE  : 'TRUE' ;
FALSE : 'FALSE' ;

GT : '>' ;
GE : '>=' ;
LT : '<' ;
LE : '<=' ;
EQ : '=' ;

LPAREN : '(' ;
RPAREN : ')' ;

DECIMAL : '-'?[0-9]+('.'[0-9]+)? ;

IDENTIFIER : [a-zA-Z_][a-zA-Z_0-9]* ;

WS : [ \r\t\u000C\n]+ -> skip;

nestedCondition : LPAREN condition+ RPAREN (binary nestedCondition)*;
condition: predicate (binary predicate)*
            | predicate (binary component)*;
component: predicate | multiAttrComp;
multiAttrComp : LPAREN predicate (and predicate)+ RPAREN;
predicate : IDENTIFIER comparator IDENTIFIER;
comparator : GT | GE | LT | LE | EQ ;
binary: AND | OR ;
unary: NOT;
and: AND;
["

I'd just wrap all the expressions into a single expression<\/code> rule.<\/i>

grammar SimpleBoolean;

parse
 : expression EOF
 ;

expression
 : LPAREN expression RPAREN                       #parenExpression
 | NOT expression                                 #notExpression
 | left=expression op=comparator right=expression #comparatorExpression
 | left=expression op=binary right=expression     #binaryExpression
 | bool                                           #boolExpression
 | IDENTIFIER                                     #identifierExpression
 | DECIMAL                                        #decimalExpression
 ;

comparator
 : GT | GE | LT | LE | EQ
 ;

binary
 : AND | OR
 ;

bool
 : TRUE | FALSE
 ;

AND        : 'AND' ;
OR         : 'OR' ;
NOT        : 'NOT';
TRUE       : 'TRUE' ;
FALSE      : 'FALSE' ;
GT         : '>' ;
GE         : '>=' ;
LT         : '<' ;
LE         : '<=' ;
EQ         : '=' ;
LPAREN     : '(' ;
RPAREN     : ')' ;
DECIMAL    : '-'? [0-9]+ ( '.' [0-9]+ )? ;
IDENTIFIER : [a-zA-Z_] [a-zA-Z_0-9]* ;
WS         : [ \r\t\u000C\n]+ -> skip;

The C# Equivalent of @Bart Kiers:

EvalVistor.cs :

public class EvalVisitor : NOABaseVisitor<object> {
    private readonly Dictionary<string, object> variables = new();


    public EvalVisitor(Dictionary<string, object> variables)
    {
        this.variables = variables;
    }

    public override object VisitParse([NotNull] NOAParser.ParseContext context)
    {
        return base.Visit(context.expression());
    }

    public override object VisitDecimalExpression([NotNull] NOAParser.DecimalExpressionContext context)
    {
        return Double.Parse(context.DECIMAL().GetText());
    }

    public override object VisitIdentifierExpression([NotNull] NOAParser.IdentifierExpressionContext context)
    {
        return variables[context.IDENTIFIER().GetText()];
    }

    public override object VisitNotExpression([NotNull] NOAParser.NotExpressionContext context)
    {
        return !((bool)this.Visit(context.expression()));
    }

    public override object VisitParenExpression([NotNull] NOAParser.ParenExpressionContext context)
    {
        return base.Visit(context.expression());
    }

    public override object VisitComparatorExpression([NotNull] NOAParser.ComparatorExpressionContext context)
    {
        if (context.op.EQ() != null)
        {
            return this.Visit(context.left).Equals(this.Visit(context.right));
        }
        else if (context.op.LE() != null)
        {
            return AsDouble(context.left) <= AsDouble(context.right);
        }
        else if (context.op.GE() != null)
        {
            return AsDouble(context.left) >= AsDouble(context.right);
        }
        else if (context.op.LT() != null)
        {
            return AsDouble(context.left) < AsDouble(context.right);
        }
        else if (context.op.GT() != null)
        {
            return AsDouble(context.left) > AsDouble(context.right);
        }
        throw new ApplicationException("not implemented: comparator operator " + context.op.GetText());
    }

    public override object VisitBinaryExpression([NotNull] NOAParser.BinaryExpressionContext context)
    {
        if (context.op.AND() != null)
        {
            return AsBool(context.left) && AsBool(context.right);
        }
        else if (context.op.OR() != null)
        {
            return AsBool(context.left) || AsBool(context.right);
        }
        throw new ApplicationException("not implemented: binary operator " + context.op.GetText());
    }

    public override object VisitBoolExpression([NotNull] NOAParser.BoolExpressionContext context)
    {
        return bool.Parse(context.GetText());
    }

    private bool AsBool(NOAParser.ExpressionContext context)
    {
        return (bool)Visit(context);
    }

    private double AsDouble(NOAParser.ExpressionContext context)
    {
        return (double)Visit(context);
    }
}

Program.cs :

Dictionary<string, object> variables = new()
{
    {"a", true },
    {"A", true },
    {"B", false },
    {"b", false },
    {"C", 42.0 },
    {"c", 42.0 },
    {"D", -999.0 },
    {"d", -1999.0 },
    {"E", 42.001 },
    {"e", 142.001 },
    {"F", 42.001 },
    {"f", 42.001 },
    {"G", -1.0 },
    { "g", -1.0 },
};

string[] expressions =
{
    "1 > 2",
    "1 >= 1.0",
    "TRUE = FALSE",
    "FALSE = FALSE",
    "A OR B",
    "B",
    "A = B",
    "c = C",
    "E > D",
    "B OR (c = B OR (A = A AND c = C AND E > D))",
    "(A = a OR B = b OR C = c AND ((D = d AND E = e) OR (F = f AND G = g)))"
};

foreach (var expression in expressions)
{
    NOALexer lexer = new (new AntlrInputStream(expression));
    NOAParser parser = new (new CommonTokenStream(lexer));
    Object result = new EvalVisitor(variables).Visit(parser.parse());
    Console.WriteLine($"{expression} - {result}\n");
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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