If then a conditional assessment

I have a language that is mainly designed to map columns to a new structure in an array. The language is intended for product managers to define mappings without having to know many programming details. I am sure that there is much more, but that is what I have.

Language works, basically. I have a problem with conditional statements.

My analyzer has the following rule:

conditionalexpr : IF^ LPAREN! (statement) RPAREN! THEN! LCURLY! statement RCURLY! (ELSE! LCURLY! statement RCURLY!)?; 

Works to generate a tree with three children.

My problem is to avoid evaluating operators if the condition does not allow this.

Very naively I did:

 conditionalexpr returns[Object o]: ^(IF a=statement b=statement (c=statement)?) { $o = (Boolean)$ao ? $bo : $co != null ? $co : ""; } ; 

Obviously this will not work.

I play with syntactic predicates, but I cannot get them to work correctly.

Operator

returns the object at the moment. Basically, the language deals with strings, but I need to support booleans and numbers (integer and decimal).

Should I add something like {$ ao}? =>, I get $ a in the generated code.

I looked at the list of business interest, but I didn’t answer this question very well, most likely because it seems obvious to them.

I am ready to publish the full grammar, but left it to save it.

+7
source share
1 answer

If you do not want certain subtrees to be evaluated, you need to allow trees to return nodes instead of the actual values. You can extend CommonTree and provide a custom TreeAdaptor to help create your own nodes, but for me it is easiest to create a custom node class (or classes) and use them instead. Demonstration for clarification:

Tg

 grammar T; options { output=AST; } tokens { ASSIGNMENT; } parse : statement+ EOF -> statement+ ; statement : ifStatement | assignment ; ifStatement : IF a=expression THEN b=expression (ELSE c=expression)? -> ^(IF $a $b $c?) ; assignment : ID '=' expression -> ^(ASSIGNMENT ID expression) ; expression : orExpression ; orExpression : andExpression (OR^ andExpression)* ; andExpression : equalityExpression (AND^ equalityExpression)* ; equalityExpression : relationalExpression (('==' | '!=')^ relationalExpression)* ; relationalExpression : atom (('<=' | '<' | '>=' | '>')^ atom)* ; atom : BOOLEAN | NUMBER | ID | '(' expression ')' -> expression ; IF : 'if'; THEN : 'then'; ELSE : 'else'; OR : 'or'; AND : 'and'; BOOLEAN : 'true' | 'false'; ID : ('a'..'z' | 'A'..'Z')+; NUMBER : '0'..'9'+ ('.' '0'..'9'+)?; SPACE : (' ' | '\t' | '\r' | '\n') {skip();}; 

Main.java

I created a Node interface that has an eval(): Object method, and also created an abstract BinaryNode class that implements Node and will always have 2 children. As you can see in the tree grammar that follows these Java classes, the whole rule now returns Node .

 import org.antlr.runtime.*; import org.antlr.runtime.tree.*; public class Main { public static void main(String[] args) throws Exception { String source = "a = 3 b = 4 if b > a then b==b else c==c"; TLexer lexer = new TLexer(new ANTLRStringStream(source)); TParser parser = new TParser(new CommonTokenStream(lexer)); TWalker walker = new TWalker(new CommonTreeNodeStream(parser.parse().getTree())); Node root = walker.walk(); System.out.println(root.eval()); } } interface Node { Object eval(); } abstract class BinaryNode implements Node { protected Node left; protected Node right; public BinaryNode(Node l, Node r) { left = l; right = r; } } class AtomNode implements Node { private Object value; public AtomNode(Object v) { value = v; } @Override public Object eval() { return value; } } class OrNode extends BinaryNode { public OrNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return (Boolean)super.left.eval() || (Boolean)super.right.eval(); } } class AndNode extends BinaryNode { public AndNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return (Boolean)super.left.eval() && (Boolean)super.right.eval(); } } class LTNode extends BinaryNode { public LTNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return (Double)super.left.eval() < (Double)super.right.eval(); } } class LTEqNode extends BinaryNode { public LTEqNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return (Double)super.left.eval() <= (Double)super.right.eval(); } } class GTNode extends BinaryNode { public GTNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return (Double)super.left.eval() > (Double)super.right.eval(); } } class GTEqNode extends BinaryNode { public GTEqNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return (Double)super.left.eval() >= (Double)super.right.eval(); } } class EqNode extends BinaryNode { public EqNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return super.left.eval().equals(super.right.eval()); } } class NEqNode extends BinaryNode { public NEqNode(Node left, Node right) { super(left, right); } @Override public Object eval() { return !super.left.eval().equals(super.right.eval()); } } class VarNode implements Node { private java.util.Map<String, Object> memory; private String var; VarNode(java.util.Map<String, Object> m, String v) { memory = m; var = v; } @Override public Object eval() { Object value = memory.get(var); if(value == null) { throw new RuntimeException("Unknown variable: " + var); } return value; } } class IfNode implements Node { private Node test; private Node ifTrue; private Node ifFalse; public IfNode(Node a, Node b, Node c) { test = a; ifTrue = b; ifFalse = c; } @Override public Object eval() { return (Boolean)test.eval() ? ifTrue.eval() : ifFalse.eval(); } } 

TWalker.g

 tree grammar TWalker; options { tokenVocab=T; ASTLabelType=CommonTree; } @members { private java.util.Map<String, Object> memory = new java.util.HashMap<String, Object>(); } walk returns [Node n] : (statement {$n = $statement.n;})+ ; statement returns [Node n] : ifStatement {$n = $ifStatement.n;} | assignment {$n = null;} ; assignment : ^(ASSIGNMENT ID expression) {memory.put($ID.text, $expression.n.eval());} ; ifStatement returns [Node n] : ^(IF a=expression b=expression c=expression?) {$n = new IfNode($an, $bn, $cn);} ; expression returns [Node n] : ^(OR a=expression b=expression) {$n = new OrNode($an, $bn);} | ^(AND a=expression b=expression) {$n = new AndNode($an, $bn);} | ^('==' a=expression b=expression) {$n = new EqNode($an, $bn);} | ^('!=' a=expression b=expression) {$n = new NEqNode($an, $bn);} | ^('<=' a=expression b=expression) {$n = new LTEqNode($an, $bn);} | ^('<' a=expression b=expression) {$n = new LTNode($an, $bn);} | ^('>=' a=expression b=expression) {$n = new GTEqNode($an, $bn);} | ^('>' a=expression b=expression) {$n = new GTNode($an, $bn);} | BOOLEAN {$n = new AtomNode(Boolean.valueOf($BOOLEAN.text));} | NUMBER {$n = new AtomNode(Double.valueOf($NUMBER.text));} | ID {$n = new VarNode(memory, $ID.text);} ; 

If you run the main class now and evaluate:

 a = 3 b = 4 if b > a then b==b else c==c 

true prints to the console:

 bart@hades :~/Programming/ANTLR/Demos/T$ java -cp antlr-3.3.jar org.antlr.Tool Tg bart@hades :~/Programming/ANTLR/Demos/T$ java -cp antlr-3.3.jar org.antlr.Tool TWalker.g bart@hades :~/Programming/ANTLR/Demos/T$ javac -cp antlr-3.3.jar *.java bart@hades :~/Programming/ANTLR/Demos/T$ java -cp .:antlr-3.3.jar Main true 

But if you check if b < a , forcing else to execute, you will see the following:

 Exception in thread "main" java.lang.RuntimeException: Unknown variable: c at VarNode.eval(Main.java:140) at EqNode.eval(Main.java:112) at IfNode.eval(Main.java:160) at Main.main(Main.java:11) 

For implementations of more complex language constructs (areas, functions, etc.), see my blog .

Good luck

+6
source

All Articles