I have a set of incoming records, that needs to be evaluated under a set of logical clauses defined and stored. An example logical clause be like :
Acct1 != 'Y' AND Acct2 > 1004 AND Acct3 >= 96 AND Acct4 < 1004 AND Acct5 = 99 AND ((Acct6 <= 9090 OR Acct7 IN (A1,A2,A6) AND Acct1 NOT IN (A3,A4)) AND Formatted LIKE 'LINUX' AND Acct9 NOT LIKE 'WINDOWS' AND (Acct10 = 'N' AND NOT Acct11 = 'N') AND EditableField BETWEEN (10 AND 20) )
My data input to the clause be like :
map.put(Acct1,"Y")
map.put(Acct2,1010)
map.put(Acct3,99)
map.put(Acct4,1015)
map.put(Acct5,99)
map.put(Acct6,9090)
map.put(Acct7,"A3")
map.put(Formatted,"LINUX_INST")
map.put(Updated,"LINUX_TMP")
map.put(Acct10,"Y")
map.put(Acct11,"N")
map.put(EditableFIeld,25)
I have to evaluate the incoming records populated into the map onto the clause defined above and print true or false based on the evaluation result.
The clause conditions and map values will be changed and executed as well.
I have the following conditional clauses to be evaluated:
!=
>
>=
<
=
<=
IN(
NOT IN(
LIKE(
NOT LIKE(
BETWEEN(
AND
OR
AND NOT
OR NOT
I have tried using grammar generators but I am told that it is not a recommended solution for our application hence I am looking for java code and I have this detailed example for reference to AND,OR,=.
resolving logical operations - AND, OR, looping conditions dynamically and looking for snippets to build on top of that if possible.
If you want to avoid a parser generator, consider using a StreamTokenizer to implement a recursive descent parser, with one method for each grammar rule.
For a subset of your grammar, this should look roughly like this (and should be straightforward to extend to your full grammar):
public class Parser {
public static Node parse(String expr) {
StreamTokenizer tokenizer =
new StreamTokenizer(new StringReader(expr));
tokenizer.nextToken();
Parser parser = new Parser(tokenizer);
Node result = parser.parseExpression();
if (tokenizer.ttype != StreamTokenizer.TT_EOF) {
throw new RuntimeException("EOF expected, got "
+ tokenizer.ttype + "/" + tokenizer.sval);
}
private StreamTokenizer tokenizer;
private Parser(StreamTokenizer tokenizer) {
this.tokenizer = tokenizer;
}
private Node parseExpression() {
Node left = parseAnd();
if (tokenizer.ttype == StreamTokenizer.TT_WORD
&& tokenizer.sval.equals("OR")) {
tokenizer.nextToken();
return new OperationNode(OperationNode.Type.OR,
left, parseExpression());
}
return left;
}
private Node parseAnd() {
Node left = parseRelational();
if (tokenizer.ttype == StreamTokenizer.TT_WORD
&& tokenizer.sval.equals("AND")) {
tokenizer.nextToken();
return new OperationNode(OperationNode.Type.AND,
left, parseAnd());
}
return left;
}
private Node parseRelational() {
Node left = parsePrimary();
OperationNode.Type type;
switch (tokenizer.ttype) {
case '<': type = OperationNode.Type.LESS; break;
case '=': type = OperationNode.Type.EQUAL; break;
case '>': type = OperationNode.Type.GREATER; break;
default:
return left;
}
tokenizer.nextToken();
return new OperationNode(type, left, parseRelational());
}
private Node parsePrimary() {
Node result;
if (tokenizer.ttype == '(') {
tokenizer.nextToken();
result = parseExpression();
if (tokenizer.ttype != ')') {
throw new RuntimeException(") expected, got "
+ tokenizer.ttype + "/" + tokenizer.sval);
}
} else if (tokenizer.ttype == '"' || tokenizer.ttype == '\'') {
result = new LiteralNode(tokenizer.sval);
} else if (tokenizer.ttype == TT_NUMBER) {
result = new LiteralNode(tokenizer.nval);
} else if (tokenizer.ttype == StreamTokenizer.TT_WORD) {
result = new FieldNode(tokenizer.sval);
} else {
throw new RuntimeException("Unrecognized token: "
+ tokenizer.ttype + "/" + tokenizer.sval);
}
tokenizer.nextToken();
return result;
}
}
This assumes a Node object hierarchy like this:
interface Node {
Object eval(Map<String,Object> data);
}
class FieldNode implements Node {
private String name;
FieldNode(String name) {
this.name = name;
}
public Object eval(Map<String,Object> data) {
return data.get(name);
}
}
class LiteralNode implements Node {
private Object value;
FieldNode(Object value) {
this.value = value;
}
public Object eval(Map<String,Object> data) {
return value;
}
}
class OperationNode implements Node {
enum Type {
AND, OR, LESS, GREATER, EQUALS
}
private Type type;
private Node leftChild;
private Node rightChild;
OperationNode(Type type, Node leftChild, Node rightChild) {
this.type = type;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
public Object eval(Map<String,Object> data) {
Object left = leftChild.eval(data);
Object right = rightChild.eval(data);
switch (type) {
case AND: return ((Boolean) left) && ((Boolean) right);
case OR: return ((Boolean) left) || ((Boolean) right);
case LESS: return ((Comparable) left).compareTo(right) < 0;
case EQUALS: return left.equals(right);
case GREATE: return ((Comparable) left).compareTo(right) > 0;
default:
throw new RuntimeException("Invalid op: " + type);
}
}
To directly answer the question, a number of SO questions (e.g. 1, 2) describe the basics of writing a parser by hand, though in practice it is very unusual to write a parser manually outside of university compiler courses due to the boilerplate and exacting detail involved.
As discussed in the comments, it sounds like the main reason to avoid grammar generators is to avoid a dependency on outside libraries. However, when using a grammar generator (parser generator) like JavaCC (Java Compiler-Compiler), there are no JAR files or outside dependencies involved: The JavaCC binary converts a grammar specification into Java code, that can be run without involving any further libraries.
See this IBM tutorial, JoAnn Brereton's "Use JavaCC to build a user friendly boolean query language" (via archive.org) as an example, which incidentally involves a grammar for a search language not unlike yours.
Example inputs:
actor = "Christopher Reeve" and keyword=action and keyword=adventure
(actor = "Christopher Reeve" and keyword=action) or keyword=romance
actor = "Christopher Reeve" and (keyword=action or keyword=romance)
Grammar excerpts:
TOKEN :
{
<STRING : (["A"-"Z", "0"-"9"])+ >
<QUOTED_STRING: "\"" (~["\""])+ "\"" >
}
void queryTerm() :
{
}
{
(<TITLE> | <ACTOR> |
<DIRECTOR> | <KEYWORD>)
( <EQUALS> | <NOTEQUAL>)
( <STRING> | <QUOTED_STRING> )
|
<LPAREN> expression() <RPAREN>
}
Output files:
UQLParser.java
UQLParserConstants.java
UQLParserTokenManager.java
TokenMgrError.java
ParseException.java
Token.java
SimpleCharStream.java
This is one of several parser generators that you can consider; others, like yacc and bison, also generate standalone Java files without requiring outside libraries. If necessary, you can check the generated Java files directly into your repository, leaving the .jj compiler source file only if you need to adjust the syntax. (Though it would probably be better to compile freshly from source as part of your build process and avoid checking generated files into source control, this may better suit your constraints of a Java-only solution.)
Related
I wrote these method for my program and i felt that it is hard to read as there are too many loops, is there any other alternative to this code to make it look cleaner and easier to read
public static void printRoutingTable(Map <Node, List<Edge>> adj, Node Root)
{
for (Node thisNode : adj.keySet())
{
Node currentNode = thisNode;
String nextHop;
if(currentNode.getParent() != null){
do{
if(currentNode.getParent() != Root){
currentNode = currentNode.getParent();
nextHop = currentNode.getAddr();
}
else{
nextHop = currentNode.getAddr() ;
}
}
while(currentNode.getParent() != Root);
}
else
{
nextHop = ""+currentNode.getAddr();
}
nextHop = nextHop.trim();
}
}
I've not tried, but this should be a functional and recursive version of your code.
String getNextAddr(Node node, StringBuilder sb, Node root) {
sb.add(node.getAddr());
if (node.getParent() != null && node.getParent() != root) {
return getNextAddr(node.getParent(), sb);
}
return sb.toString();
}
String nextHopList =
adj.keySet()
.stream()
.map(k -> getNextAddr(k, new StringBuilder(), Root))
.collect(Collectors.toList())
It's difficult to tell what your code is trying to achieve. At the moment it's not actually doing anything because the nextHop variable is local and nothing seems to be accumulated in the loop. I'm assuming you intend to join the strings your are generating.
there's no point passing in a map if you aren't going to use it. Better to pass a collection (or, better, Stream) of nodes.
generally the root node is the only one with a null parent. So it's likely you also don't need to pass in a reference to the root node.
if parent is optional I suggest you return Optional<Node> from getParent rather than Node.
an easy way to make the code easier to read is to break the parts into separate methods that are named after exactly what they do.
So taking these suggestions into account, something like the following:
String getRoutingTable(Stream<Node> nodes) {
return nodes
.flatMap(this::getRoutingForNode)
.map(Node::getAddr)
.collect(joining(";"));
}
private Stream<Node> getRoutingForNode(Node node) {
Stream.Builder<Node> pathToRoot = Stream.builder();
for (Node c = node; c.getParent().isPresent(); c = node.getParent().get()) {
pathToRoot.accept(c);
}
return pathToRoot.build();
}
Note that in Java 9 the getRoutingForNode will become much more readable as you will be able to dispense with the Builder:
return Stream.iterate(node,
n -> node.getParent().isPresent(),
n -> n.getParent().get());
Background - Question below
I am at the start of implementing a metric suite in Java for Java however I am concerned that my approach is not appropriate.
Currently I am using the JDT's ASTParser for every file within a directory. This started off well and I was able to collect things around line count and average lines per method for each class. This was done via a MethodVisitor class which extends ASTVisitor and contains a method visit(MethodDeclaration node).
I am now trying to calculate Cyclomatic Complexity for every method. I have split out the method body and have a ComplexityVisitor which contains a visit(IfStatement node) and a visit(ReturnStatement node).
Using this structure I know that there is a if statement within the code but I am unsure on how to know how many levels of "if else"s there are. The only method I can find that is helpful is the node.getElseStatement() but this returns what is basically (or seems to me) a string and would therefore have to use regex to know the number of paths the statement could take.
So my question is:
Is there a way to deduce how many levels are in the "if - else if - else" statement when using eclipses ASTParser?
or
Should I be looking for a cleaner solution such as IJavaElement or parsing the code myself putting key words onto a list then looping back through them.
Some sample Code - very much in testing phase
public class Test {
private static List<ClassInfo> klasses = new ArrayList<ClassInfo>();
// Called for every file where str is what the file contains
public static void parse(String str) {
ASTParser parser = ASTParser.newParser(AST.JLS3);
parser.setSource(str.toCharArray());
parser.setKind(ASTParser.K_COMPILATION_UNIT);
final CompilationUnit cu = (CompilationUnit) parser.createAST(null);
ClassVisitor cv = new ClassVisitor();
cu.accept(cv);
MethodVisitor methodsVisitor = new MethodVisitor(cu);
cu.accept(methodsVisitor);
ClassInfo klass = new ClassInfo(cv.getClassName(),
cu.getLineNumber(cu.getLength() - 1),
methodsVisitor.getNumberOfMethods(),
methodsVisitor.getAverageLinesPerMethod(),
methodsVisitor.getMethods());
for(int i = 0; i < klass.methods.size(); i++){
parser.setSource(klass.methods.get(i).body.toCharArray());
CyclomaticComplexityVisitor ccv = new CyclomaticComplexityVisitor();
cu.accept(ccv);
}
klasses.add(klass);
}
-
public class MethodVisitor extends ASTVisitor {
private CompilationUnit cu;
private int numberOfMethods;
private int lineCount;
private List<MethodInfo> methods = new ArrayList<MethodInfo>();
public MethodVisitor(CompilationUnit cu){
this.cu = cu;
}
public boolean visit(MethodDeclaration node){
int startPos = cu.getLineNumber(node.getStartPosition());
int endPos = cu.getLineNumber(node.getStartPosition() + node.getLength());
lineCount += (endPos - startPos);
numberOfMethods++;
String methodBody = node.getBody().toString();
MethodInfo m = new MethodInfo(node.getName().getIdentifier(),
(endPos - startPos),
node.getReturnType2());
m.body = methodBody;
methods.add(m);
return true;
}
-
public class CyclomaticComplexityVisitor extends ASTVisitor {
private int complexityScore = 0;
private int edges = 0;
private int nodes = 0;
private int exitPoints = 1;
private boolean firstReturn = true;
public boolean visit(IfStatement node){
System.out.println("THERE WAS AN IF");
String statement = node.toString();
System.out.println(statement);
return true;
}
public boolean visit(ReturnStatement node){
if (firstReturn) {
firstReturn = false;
} else {
exitPoints++;
}
return true;
}
Cheers
I'm not sure if this will answer your question, but for calculating McCabe's Cyclomatic Complexity (McCC) metric, you don't need to care about if-else-if nesting levels. You simply need to count the number of "branching" instructions and add 1 in the end. See the definition in the User's Guide of our SourceMeter tool:
McCabe's Cyclomatic Complexity (McCC)
Method: complexity of the method expressed as the number of independent control flow paths in it. It represents a lower bound for the number of possible execution paths in the source code and at the same time it is an upper bound for the minimum number of test cases needed for achieving full branch test coverage. The value of the metric is calculated as the number of the following instructions plus 1: if, for, foreach, while, do-while, case label (which belongs to a switch instruction), catch, conditional statement (?:). Moreover,
logical “and” (&&) and logical “or” (||) expressions also add 1 to the value because their short-circuit evaluation can cause branching depending on the first operand. The following instructions are not included: else, switch, default label (which belongs to a switch instruction), try, finally.
I am new in Java and trying to add evaluate method to my class. The ExpTree class and its testing program is given to me. I wrote my code as I learned in the class, but do not know why it does not work.
An evaluate() method, which returns the arithmetic evaluation of the ExpTree. This should be done recursively, so you will need 2 methods to do it. In the case where it would result in division or mod by 0, it should throw a new ArithmeticException with a descriptive String. If the tree is empty, evaluate() should also throw a new ArithmeticException with a descriptive String.
Here is my code:
// This will implement an "Expression Tree" which stores an arithmetic expression
import java.util.*;
public class ExpTree
{
//-------data
private ExpNode root;
//-------constructor
public ExpTree()
{
root = null;
}
//constructor where a string is passed in. It is parsed and stored
public ExpTree(String expString)
{
//declare StringTokenizer, Stacks, and other variables used in parsing
StringTokenizer tokenizer = new StringTokenizer (expString, "()+-*/%", true);
String token;
ExpNode operator, leftOperand, rightOperand;
Stack<ExpNode> operators = new Stack<ExpNode>();
Stack<ExpNode> operands = new Stack<ExpNode>();
//break up expString into tokens
while (tokenizer.hasMoreTokens())
{
token = tokenizer.nextToken();
// if the current token is a left paren, ignore it
if (token.equals ("("))
;
// if the current token is an operator, put it on the
// operator stack
else if ((token.equals ("+")) || (token.equals ("-")) ||
(token.equals ("*")) || (token.equals ("/")) || (token.equals ("%")))
operators.push (new ExpNode(token));
//if the current token is a right paren, pop the operators stack
//to get the operator, pop the operands stack twice to get the two
//operands (stored as expression trees). Then make the two operands
//children of the operator and push back on the operands tree.
else if (token.equals (")"))
{
operator = operators.pop();
rightOperand = operands.pop();
leftOperand = operands.pop();
operator.setLeft(leftOperand);
operator.setRight(rightOperand);
operands.push(operator);
}
//otherwise, the token should be a number - put it in the operands stack
else
operands.push (new ExpNode(token));
} // while (tokenizer.hasMoreTokens())
//when finished parsing, the operands stack should contain the fully-built
//expression tree.
if (!operands.isEmpty())
root = operands.pop();
}
//-------methods
//isEmpty()
public boolean isEmpty()
{
return (root == null);
}
//printTree methods - prints the tree in RNL order, with indents. Called from "outside"
public void printTree()
{
if (root == null)
System.out.println("The tree is empty");
else
printTree(root, 0); //start with the root with 0 indentations
}
//recursive, private version of printTree
private void printTree(ExpNode subTree, int indents)
{
//if there is a right side, handle it first (with 1 more indent)
if (subTree.getRight() != null)
printTree(subTree.getRight(), indents+1);
//then print the node itself (first move over the right amount of indents)
System.out.println("\n\n\n");
for (int i=0; i<indents; i++)
System.out.print("\t");
System.out.println(subTree);
//if there is a left side, handle it first (with 1 more indent)
if (subTree.getLeft() != null)
printTree(subTree.getLeft(), indents+1);
}
//inorder traversal - starts the recursive calls to print inorder
public String inOrder()
{
return inOrder(root);
}
//inorder traversal - recursive left side of tree, print node, right side of tree
private String inOrder(ExpNode theTreeToTraverse)
{
if (theTreeToTraverse == null)
return ""; //don't try to do anything if tree is null
//else build up a String to return. It will involve recursive calls
String returnString = "";
if (theTreeToTraverse.getLeft() != null)
{
returnString += "(" + inOrder(theTreeToTraverse.getLeft());
}
returnString += theTreeToTraverse;
if (theTreeToTraverse.getRight() != null)
{
returnString += inOrder(theTreeToTraverse.getRight()) + ")";
}
return returnString;
}
//public version of evaluate
public double evaluate(){
if (root == null) //Am I null?
throw new ArithmeticException("The tree is empty, nothing to be evaluated!");
else //You handle it!
return recursiveEvaluate(root);
}
//Recursive version of evaluate
private double recursiveEvaluate(ExpNode subTree){
//If subTree is empty
if (subTree == null)
return 0;
//What are you subTree? A number? An operator?
else if(subTree.getData().equals("+"))
return recursiveEvaluate(subTree.getLeft()) +
recursiveEvaluate(subTree.getRight()) ;
else if(subTree.getData().equals("-"))
return recursiveEvaluate(subTree.getLeft()) -
recursiveEvaluate(subTree.getRight()) ;
else if(subTree.getData().equals("*"))
return recursiveEvaluate(subTree.getLeft()) *
recursiveEvaluate(subTree.getRight()) ;
else if(subTree.getData().equals("/")){
double right = recursiveEvaluate(subTree.getRight());
if(right == 0.0)
throw new ArithmeticException("Divide by zero is undefined!");
return recursiveEvaluate(subTree.getLeft()) / right;
}
else if(subTree.getData().equals("%")){
double right = recursiveEvaluate(subTree.getRight());
if(right == 0.0)
throw new ArithmeticException("Mod by zero exception");
return recursiveEvaluate(subTree.getLeft()) % right;
}
//Converting String type to double
else
return Double.parseDouble(subTree.getData());
}
//Public version of numPlus
public int numPlus(){
return recursiveNumPlus(root);
}
//Recursive version of numPlus
private int recursiveNumPlus(ExpNode subTree){
if (subTree == null)
return 0;
//If you are a '+' sign
if(subTree.getData().equals("+"))
return recursiveNumPlus(subTree.getLeft()) +
recursiveNumPlus(subTree.getRight()) + 1;
else
return recursiveNumPlus(subTree.getLeft()) +
recursiveNumPlus(subTree.getRight());
}
}
//***************************************************************************
// ExpNode holds a "node" for an ExpTree.
class ExpNode
{
//data
private String data;
private ExpNode left;
private ExpNode right;
//constructor
public ExpNode(String el)
{
data = el;
left = right = null;
}
//methods
//toString() - this is how an ExpNode represents itself as a String
public String toString()
{
return data;
}
//getLeft - returns the reference to the left subTree
public ExpNode getLeft()
{
return left;
}
//getRight - returns the reference to the right subTree
public ExpNode getRight()
{
return right;
}
//getData - returns the data (could be an operator or a number, so returns as a String)
public String getData()
{
return data;
}
//setLeft - sets the left subTree to whatever is passed in
public void setLeft(ExpNode newNode)
{
left = newNode;
}
//setRight - sets the right subTree to whatever is passed in
public void setRight(ExpNode newNode)
{
right = newNode;
}
}
The object oriented approach to your problem is to define a dedicated type for each kind of node. In order to keep the length of this answer reasonable and to avoid doing your homework, I'll only show a minimal example for integer expressions only involving addition and multiplication.
The first step is to define what an expression node must provide. For this, we define the interface ExprNode. If you did not learn about polymorphism in your class yet (which should surprise me) you'll probably want to stop reading now and come back after you have learned about it.
We want to evaluate nodes so we'll add an evaluate method that should return the value of the sub-expression rooted at that node. We'll defer its implementation to the specific node classes as these will know best how to evaluate themselves.
We also want to format expressions so we will add another method to format the sub-expression in infix notation.
public interface ExprNode {
int evaluate();
String asInfixString();
}
Now, let's see what nodes we need. Certainly, any expression will contain numbers at the leafs so we'll better start defining a class for them. The implementation of ValueNode is really simple, not to say trivial.
public final class ValueNode implements ExprNode {
private final int value;
public ValueNode(final int value) {
this.value = value;
}
#Override
public int evaluate() {
return this.value;
}
#Override
public String asInfixString() {
return String.valueOf(this.value);
}
}
Next, we have our two binary operations + and *. The implementation of the respective classes is again very simple.
public final class PlusNode implements ExprNode {
private final ExprNode lhs;
private final ExprNode rhs;
public PlusNode(final ExprNode lhs, final ExprNode rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
#Override
public int evaluate() {
return this.lhs.evaluate() + this.rhs.evaluate();
}
#Override
public String asInfixString() {
return String.format("(%s) + (%s)",
this.lhs.asInfixString(),
this.rhs.asInfixString());
}
}
public final class TimesNode implements ExprNode {
private final ExprNode lhs;
private final ExprNode rhs;
public TimesNode(final ExprNode lhs, final ExprNode rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
#Override
public int evaluate() {
return this.lhs.evaluate() * this.rhs.evaluate();
}
#Override
public String asInfixString() {
return String.format("(%s) * (%s)",
this.lhs.asInfixString(),
this.rhs.asInfixString());
}
}
Equipped with that, we can elegantly build expression trees, print and evaluate them. Here is an example for the expression 2 * (3 + 4).
ExprNode expr = new TimesNode(
new ValueNode(2),
new PlusNode(new ValueNode(3), new ValueNode(4)));
System.out.println(expr.asInfixString() + " = " + expr.evaluate());
It will print (2) * ((3) + (4)) = 14.
So, for your ExprTree, you would simply check if the root != null and if so, return root.evaluate().
What if we want more expressions?
Clearly, we will define another sub-type of ExprNode. For example, we can define more binary operators to handle subtraction and division, another unary node for the unary minus and so on. Each of these classes must implement the interface dictated by ExprNode so any sub-class can be used the same way, encapsulating the logic how it evaluates itself.
What if we want more operations?
For example, we might want to format expressions in postfix notation. To be able to do this, we could add another method asPostfixString to ExprNode. However, this is a little awkward since it means we'll have to go and edit all sub-classes we have implemented so far, adding the new operation.
This is a rather fundamental problem. If you lay out a composite structure to encapsulate operations in the nodes, then it is elegant to use and simple to add new node types but difficult to add mode operations. If you are using a case selection in every operation (somewhat like in your code) then it is simpler to add new operations but the code for the operations gets convoluted and it is difficult to add more node types (the code for all operations needs to be altered). This dilemma is known as the tyranny of the dominant model decomposition. The visitor pattern is an attempt to break out of it.
Anyway, if you are taking a basic Java class, I think what you are expected to learn is implementing a polymorphic tree with operations defined in the nodes as shown above.
This problem is sort of a continuation of How to write visitor classes for collections? - I tried that answer, but I find that the code works in eclipse, but has a null pointer problem in unix or windows. So now it looks like a different problem, so I created a new question.
I have uploaded the full code at https://sites.google.com/site/rogergdmn/ , Below is the summary.
Here are the details (the code is a variation of the LabeledExpr.g4 from the book) - I am trying to create an intermediate data structure by using the visitor classes. When I run in command line (in unix or windows), the line "why null e" is printed, but this line is not printed when I run in eclipse. How do I fix this bug?
This is the grammar:
prog: stat+ ;
stat: expr NEWLINE # printExpr
| NEWLINE # blank
;
expr: INT # int
;
These are the functions in EvalVisitor.java:
public Object visitInt(ExprParser.IntContext ctx) {
System.out.printf("visited----- 1\n");
int value = Integer.valueOf(ctx.INT().getText());
return new E(1, value);
}
public Object visitPrintExpr(ExprParser.PrintExprContext ctx) {
System.out.printf("visited----- 2\n");
E e = (E) visit(ctx.expr()); // evaluate the expr child
return new E(2, e);
}
public Object visitProg(ExprParser.ProgContext ctx) {
System.out.printf("visited----- 3\n");
List<ExprParser.StatContext> sL = ctx.stat();
List<E> eL = new ArrayList<E>();
for(ExprParser.StatContext s : sL) {
E e = (E) visit(s);
if(e==null) System.out.printf("why null e??\n");
eL.add(e);
}
return new E(7, eL);
}
This is the E class (only constructors present, to demonstrate the error):
public class E {
int typ;
int val;
E e1;
List<E> eL;
public E(int _typ, int _v) { typ = _typ; val = _v; }
public E(int _typ, E _e1) { typ = _typ; e1 = _e1; }
public E(int _typ, List<E> _eL) { typ = _typ; eL = _eL; }
}
The other codes are directly from the book example (http://pragprog.com/titles/tpantlr2/source_code , directory "starter").
BTW, a similar visitor code is shown at If/else statements in ANTLR using listeners and https://github.com/bkiers/Mu, and that code works fine for me. My code is pretty similar to that code, so I am not sure whats going wrong here.
You didn't override visitBlank, so any blank statement will return null from the visit method.
Edit: The difference between Eclipse and the other case is one of the following:
You could be using different input in the two cases. You'll need to examine the raw contents of the TokenStream to be sure.
You didn't include an explicit EOF at the end of your prog rule, so there is a possibility that your parser is ignoring some tokens of the input. To ensure all tokens are considered in all cases, add a reference to EOF at the end of the prog rule.
Firstly I ve searched about usage of Generic Types in java, however answers I ve found was way too simple or complicated. So here is my exact question.
I have three classes respectively PerfectTreeControl, Tree and Entry.
Tree has
public class Tree<K> {
public Entry <K> root;
Entry has
public class Entry<K> {
public K element;
public Entry<K> parent, left_child, right_child;
public Entry(K element) {
this.element = element;
}
public Entry(K element, Entry<K> left, Entry<K> right) {
left_child = left;
right_child = right;
this.element = element;
}
I am trying to understand what is the difference between Entry parent and Entry <K> parent? I know that K element can be used as integer, String or whatever I want, but does the same thing goes for the object? I tried to use Entry variables without parameter and it only said that Entry is a raw type and should be parameterized and it still working without error.
My second question is about checking out a tree whether its perfect or not. Here are the some codes I ve tried so far:
public class PerfectTreeControl {
public static boolean isPerfect(Tree<String> tree) {
Tree t1 = new Tree();
if( t1.isFull( tree.root ) ) {
int depth = t1.height(tree.root);
return t1.everyLeafHasSameDepth(tree.root, depth);
}
else
return false;
}
}
public class Tree<K> {
public Entry <K> root;
public boolean isLeaf(Entry e) {
return e.left_child == null &&
e.right_child == null;
}
public int height(Entry e) {
if( e == null ||
e.left_child == null &&
e.right_child == null )
return 0;
int left = height( e.left_child );
int right = height( e.right_child );
return 1 + Math.max(left, right);
}
public boolean isFull(Entry base) {
if( isLeaf(base) )
return true;
else
if( base.left_child != null && base.right_child != null ) {
return isFull(base.left_child) &&
isFull(base.right_child);
} else {
return false;
}
}
public int depth(Entry e) {
if( e == root ) {
return 0;
} else {
return 1 + depth(e.parent);
}
}
public boolean everyLeafHasSameDepth(Entry base, int depth) {
if( base == null )
return false;
else if(isLeaf(base) )
return depth( base ) == depth;
else {
return
everyLeafHasSameDepth(base.left_child, depth) &&
everyLeafHasSameDepth(base.right_child, depth);
}
}
entry class(I wrote it at the top of the page) As you can see, isPerfect method in the PerfectTreeControl class uses Tree -String- tree as a paramater and I have no idea what it is. In the Tree class, I tried Entry with and and again no difference. The code won't work properly, and I am totally confused.
Generics in Java are, fundamentally, a way to name a particular class within an object with knowing which class until that object is declared. This is useful because it allows the compiler to enforce consistency among references to that class.
More concretely, in your class Entry<K>, any time you reference K, the Java compiler will enforce that all references of type K are, in fact, treated as type K. For instance, if you create an object of type Entry<String>, the element member of that object must be of type String, the parent member must be of type Entry<String>, etc. If you had a method that returned a K, the compiler would recognize that the return value is String. If the compiler sees an inconsistency here - say, if you try to set member's value to an Integer - it will complain.
Keep in mind that qualities I describe in the example above is all in reference to the particular Entry<String> object that you've defined. If you instead define an Entry<Integer>, without updating your Entry class, the consistency is enforced within that new object - except this time with K meaning Integer.
If you create an object without specifying a type argument for K, you are using a "raw type". This prevents the compiler from enforcing consistency rules and it will assume that the type of K is Object. This means you'll have to start worrying about casting, which can be tedious to do properly.
To check if a tree is full (or "perfect"), the most intuitive approach is a recursive one. The recursive rule to use in this scenario is "if a tree's children are perfect and have the same depth, the tree is perfect."