org.mvel2.util.ASTBinaryTree Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of virtdata-lib-realer Show documentation
Show all versions of virtdata-lib-realer Show documentation
With inspiration from other libraries
package org.mvel2.util;
import static org.mvel2.Operator.*;
import org.mvel2.ast.ASTNode;
import org.mvel2.ast.EndOfStatement;
import org.mvel2.ast.OperatorNode;
public class ASTBinaryTree {
private ASTNode root;
private ASTBinaryTree left;
private ASTBinaryTree right;
public ASTBinaryTree(ASTNode node) {
this.root = node;
}
public ASTBinaryTree append(ASTNode node) {
if (comparePrecedence(root, node) >= 0) {
ASTBinaryTree tree = new ASTBinaryTree(node);
tree.left = this;
return tree;
} else {
if (left == null) throw new RuntimeException("Missing left node");
if (right == null) {
right = new ASTBinaryTree(node);
} else {
right = right.append(node);
}
return this;
}
}
public Class> getReturnType(boolean strongTyping) {
if (!(root instanceof OperatorNode)) return root.getEgressType();
if (left == null || right == null) throw new RuntimeException("Malformed expression");
Class> leftType = left.getReturnType(strongTyping);
Class> rightType = right.getReturnType(strongTyping);
switch (((OperatorNode)root).getOperator()) {
case CONTAINS:
case SOUNDEX:
case INSTANCEOF:
case SIMILARITY:
case REGEX:
return Boolean.class;
case ADD:
if (leftType.equals(String.class) || rightType.equals(String.class)) return String.class;
case SUB:
case MULT:
case DIV:
if (strongTyping && !CompatibilityStrategy.areEqualityCompatible(leftType, rightType))
throw new RuntimeException("Associative operation requires compatible types. Found " + leftType + " and " + rightType);
return Double.class;
case TERNARY_ELSE:
if (strongTyping && !CompatibilityStrategy.areEqualityCompatible(leftType, rightType))
throw new RuntimeException("Associative operation requires compatible types. Found " + leftType + " and " + rightType);
return leftType;
case EQUAL:
case NEQUAL:
if (strongTyping && !CompatibilityStrategy.areEqualityCompatible(leftType, rightType))
throw new RuntimeException("Comparison operation requires compatible types. Found " + leftType + " and " + rightType);
return Boolean.class;
case LTHAN:
case LETHAN:
case GTHAN:
case GETHAN:
if (strongTyping && !CompatibilityStrategy.areComparisonCompatible(leftType, rightType))
throw new RuntimeException("Comparison operation requires compatible types. Found " + leftType + " and " + rightType);
return Boolean.class;
case AND:
case OR:
if (strongTyping) {
if (leftType != Boolean.class && leftType != Boolean.TYPE)
throw new RuntimeException("Left side of logical operation is not of type boolean. Found " + leftType);
if (rightType != Boolean.class && rightType != Boolean.TYPE)
throw new RuntimeException("Right side of logical operation is not of type boolean. Found " + rightType);
}
return Boolean.class;
case TERNARY:
if (strongTyping && leftType != Boolean.class && leftType != Boolean.TYPE)
throw new RuntimeException("Condition of ternary operator is not of type boolean. Found " + leftType);
return rightType;
}
// TODO: should throw new RuntimeException("Unknown operator");
// it doesn't because I am afraid I am not covering all the OperatorNode types
return root.getEgressType();
}
private int comparePrecedence(ASTNode node1, ASTNode node2) {
if (!(node1 instanceof OperatorNode) && !(node2 instanceof OperatorNode)) return 0;
if (node1 instanceof OperatorNode && node2 instanceof OperatorNode) {
return PTABLE[((OperatorNode)node1).getOperator()] - PTABLE[((OperatorNode)node2).getOperator()];
}
return node1 instanceof OperatorNode ? -1 : 1;
}
public static ASTBinaryTree buildTree(ASTIterator input) {
ASTIterator iter = new ASTLinkedList(input.firstNode());
ASTBinaryTree tree = new ASTBinaryTree(iter.nextNode());
while (iter.hasMoreNodes()) {
ASTNode node = iter.nextNode();
if (node instanceof EndOfStatement) {
if (iter.hasMoreNodes()) tree = new ASTBinaryTree(iter.nextNode());
} else {
tree = tree.append(node);
}
}
return tree;
}
}