Nested Boolean Expression Parser mit ANTLR

Ich versuche, einen geschachtelten booleschen Ausdruck zu analysieren und die einzelnen Bedingungen innerhalb des Ausdrucks getrennt zu erhalten. Zum Beispiel, wenn die Eingabezeichenfolge ist:

(A = a ODER B = b ODER C = c UND ((D = d UND E = e) OR (F = f UND G = g)))

Ich möchte die Bedingungen mit der richtigen Reihenfolge erhalten. dh,

D = d UND E = e ODER F = f UND G = g UND A = a ODER B = b OR C = c

Ich benutze ANTLR 4 um den Eingabetext zu parsen und hier ist meine Grammatik:

grammar SimpleBoolean; rule_set : nestedCondition* EOF; AND : 'AND' ; OR : 'OR' ; NOT : 'NOT'; TRUE : 'TRUE' ; FALSE : 'FALSE' ; GT : '>' ; GE : '>=' ; LT : '<' ; LE : ' 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; 

Und hier ist der Java-Code, mit dem ich ihn analysiere:

 ANTLRInputStream inputStr = new ANTLRInputStream(input); SimpleBooleanLexer lexer = new SimpleBooleanLexer(inputStr); TokenStream tokens = new CommonTokenStream(lexer); SimpleBooleanParser parser = new SimpleBooleanParser(tokens); parser.getBuildParseTree(); ParseTree tree = parser.rule_set(); System.out.println(tree.toStringTree(parser)); 

Die Ausgabe ist:

 (rule_set (nestedCondition ( (condition (predicate A (comparator =) a) (binary OR) (component (predicate B (comparator =) b)) (binary OR) (component (predicate C (comparator =) c)) (binary AND) (component (multiAttrComp ( (predicate ( D (comparator =) d) (and AND) (predicate E (comparator =) e) ))) (binary OR) (component (multiAttrComp ( (predicate F (comparator =) f) (and AND) (predicate G (comparator =) g) )))) ) )) ) 

Ich suche nach Hilfe, wie dieser Baum analysiert werden kann, um die Bedingungen in der richtigen Reihenfolge zu erhalten? In ANTLR 3 könnten wir ^ und! zu entscheiden, wie der Baum gebaut wird (siehe diesen Thread ), aber ich habe gelernt, dass dies in ANTLR 4 nicht unterstützt wird.

Kann jemand vorschlagen, wie ich den String in der richtigen Reihenfolge in Java mit dem von ANTLR erstellten ParseTree analysieren kann?

Ich würde alle Ausdrücke in eine einzige expression umwandeln. Stellen Sie sicher, dass Sie vor der binary Ausdruckalternative die alternativen comparator definieren, um sicherzustellen, dass die comparator enger binden als OR und AND :

 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; 

Verwenden Sie die folgenden schnell und dreckig Testklassen, um die obige Grammatik zu testen:

 public class Main { public static void main(String[] args) throws Exception { Map variables = new HashMap() {{ put("A", true); put("a", true); put("B", false); put("b", false); put("C", 42.0); put("c", 42.0); put("D", -999.0); put("d", -1999.0); put("E", 42.001); put("e", 142.001); put("F", 42.001); put("f", 42.001); put("G", -1.0); put("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)))" }; for (String expression : expressions) { SimpleBooleanLexer lexer = new SimpleBooleanLexer(new ANTLRInputStream(expression)); SimpleBooleanParser parser = new SimpleBooleanParser(new CommonTokenStream(lexer)); Object result = new EvalVisitor(variables).visit(parser.parse()); System.out.printf("%-70s -> %s\n", expression, result); } } } class EvalVisitor extends SimpleBooleanBaseVisitor { private final Map variables; public EvalVisitor(Map variables) { this.variables = variables; } @Override public Object visitParse(SimpleBooleanParser.ParseContext ctx) { return super.visit(ctx.expression()); } @Override public Object visitDecimalExpression(SimpleBooleanParser.DecimalExpressionContext ctx) { return Double.valueOf(ctx.DECIMAL().getText()); } @Override public Object visitIdentifierExpression(SimpleBooleanParser.IdentifierExpressionContext ctx) { return variables.get(ctx.IDENTIFIER().getText()); } @Override public Object visitNotExpression(SimpleBooleanParser.NotExpressionContext ctx) { return !((Boolean)this.visit(ctx.expression())); } @Override public Object visitParenExpression(SimpleBooleanParser.ParenExpressionContext ctx) { return super.visit(ctx.expression()); } @Override public Object visitComparatorExpression(SimpleBooleanParser.ComparatorExpressionContext ctx) { if (ctx.op.EQ() != null) { return this.visit(ctx.left).equals(this.visit(ctx.right)); } else if (ctx.op.LE() != null) { return asDouble(ctx.left) < = asDouble(ctx.right); } else if (ctx.op.GE() != null) { return asDouble(ctx.left) >= asDouble(ctx.right); } else if (ctx.op.LT() != null) { return asDouble(ctx.left) < asDouble(ctx.right); } else if (ctx.op.GT() != null) { return asDouble(ctx.left) > asDouble(ctx.right); } throw new RuntimeException("not implemented: comparator operator " + ctx.op.getText()); } @Override public Object visitBinaryExpression(SimpleBooleanParser.BinaryExpressionContext ctx) { if (ctx.op.AND() != null) { return asBoolean(ctx.left) && asBoolean(ctx.right); } else if (ctx.op.OR() != null) { return asBoolean(ctx.left) || asBoolean(ctx.right); } throw new RuntimeException("not implemented: binary operator " + ctx.op.getText()); } @Override public Object visitBoolExpression(SimpleBooleanParser.BoolExpressionContext ctx) { return Boolean.valueOf(ctx.getText()); } private boolean asBoolean(SimpleBooleanParser.ExpressionContext ctx) { return (boolean)visit(ctx); } private double asDouble(SimpleBooleanParser.ExpressionContext ctx) { return (double)visit(ctx); } } 

Wenn Sie die Main class ausführen, erhalten Sie folgende Ausgabe:

 1 > 2 -> false 1 >= 1.0 -> true TRUE = FALSE -> false FALSE = FALSE -> true A OR B -> true B -> false A = B -> false c = C -> true E > D -> true B OR (c = B OR (A = A AND c = C AND E > D)) -> true (A = a OR B = b OR C = c AND ((D = d AND E = e) OR (F = f AND G = g))) -> true