1+ package discreteMath ;
2+
3+ import java .util .ArrayList ;
4+ import java .util .List ;
5+ import java .util .Queue ;
6+
7+ public class ExpressionTree
8+ {
9+ public Node root ;
10+ public List <Node > leafs ;
11+
12+ private List <Character > evaluationResults ;// The results of the last call to evaluate() on root.
13+ private List <String > evaluationOrder ;// The order that the nodes were evaluated.
14+
15+ /**
16+ * Builds a binary expression tree representing the expression to be evaluated.
17+ * @param initialExpression The expression to build the tree from.
18+ */
19+ public ExpressionTree (String initialExpression )
20+ {
21+ leafs = new ArrayList <Node >();
22+ evaluationOrder = new ArrayList <String >();
23+ root = new Node (initialExpression );
24+ }
25+
26+ /**
27+ * A node on the binary expression tree.
28+ */
29+ class Node
30+ {
31+ char operand ;
32+ Node left ;
33+ Node right ;
34+
35+ /**
36+ * Creates a node on the binary expression tree, from the input expression.
37+ * If the input expression is not in base form,
38+ * it recursively creates child nodes for the left and right branches.
39+ *
40+ * Also keeps track of the order that nodes are created in, since this is useful information
41+ * if using this tree to generate a truth table.
42+ */
43+ Node (String expression )
44+ {
45+ String leftExpression ;
46+ String rightExpression ;
47+ String [] elements ;
48+
49+ // Divide the original expression into individual elements
50+ elements = parseExpression (expression );
51+ // Retrieve the individual parsed elements as the elements of an array (e.g. left, middle, right)
52+ leftExpression = elements [0 ];
53+ this .operand = elements [1 ].charAt (0 );
54+ rightExpression = elements [2 ];
55+ // If not the base case, recurse again for left and right branches
56+ if (leftExpression != null )
57+ {
58+ this .left = new Node (leftExpression );
59+ }
60+ if (rightExpression != null )
61+ {
62+ this .right = new Node (rightExpression );
63+ }
64+ // Determine if this Node is terminal or has branches
65+ if (leftExpression == null && rightExpression == null )
66+ {
67+ // Keep track of the leafs so that class users can modify the leaf nodes or set their values.
68+ leafs .add (this );
69+ }
70+ evaluationOrder .add (this .toString ());
71+ }
72+
73+ public String toString ()
74+ {
75+ String result = "" ;
76+ result += (left == null ) ? ("" ) : ("(" + left .toString () + ")" );
77+ result += operand ;
78+ result += (right == null ) ? ("" ) : ("(" + right .toString () + ")" );
79+ return result ;
80+ }
81+
82+ void setOperand (char newOperand )
83+ {
84+ this .operand = newOperand ;
85+ }
86+ }
87+
88+ /**
89+ * Returns an expression parsed into three parts:
90+ * The left branch, the operand, and the right branch, respectively,
91+ * in the form of a 3 element array of Strings.
92+ *
93+ * @param expression The expression to parse.
94+ * @return A 3 element array of Strings with the parsed elements.
95+ */
96+ private String [] parseExpression (String expression )
97+ {
98+ String [] result = new String [3 ];
99+ String alphabet = "abcdefghijklmnopqrstuvwxyz" ;
100+
101+ // Begin by checking the case of an isolated variable.
102+ if (alphabet .contains (String .valueOf (expression .charAt (0 ))))
103+ {
104+ if (expression .length () == 1 )
105+ {
106+ result [0 ] = null ;
107+ result [1 ] = expression ;
108+ result [2 ] = null ;
109+ return result ;
110+ }
111+ else
112+ {
113+ // I should really throw an exception here, but this is just a toy program.
114+ System .err .println ("Syntax Error!" + "\n " + "Exiting Program." );
115+ System .exit (-1 );
116+ }
117+ }
118+ if (expression .startsWith ("(" ))
119+ {
120+ // Determine if the expression has unneeded parentheses...
121+ int count = 1 ; // The number of unmatched open parentheses.
122+ int position ;
123+ for (position = 1 ; position < expression .length (); position ++)
124+ {
125+ if (expression .charAt (position ) == '(' )
126+ {
127+ count ++;
128+ }
129+ else if (expression .charAt (position ) == ')' )
130+ {
131+ count --;
132+ }
133+ // If we reach the end of the original open parentheses
134+ if (count == 0 )
135+ {
136+ // check if there is another set after...
137+ if (expression .substring (position +1 ).contains ("(" ))
138+ {
139+ result [0 ] = expression .substring (0 , position + 1 );
140+ result [1 ] = String .valueOf (expression .charAt (position +1 ));
141+ result [2 ] = expression .substring (position +2 );
142+ return result ;
143+ }
144+ else
145+ {
146+ // Remove the unneeded parentheses and recurse...
147+ expression = expression .substring (1 , expression .length () - 1 );
148+ return parseExpression (expression );
149+ }
150+ }
151+ }
152+ }
153+ else if (expression .startsWith ("!" ))
154+ {
155+ // If in the form of !var
156+ if (alphabet .contains (String .valueOf (expression .charAt (1 ))))
157+ {
158+ result [0 ] = null ;
159+ result [1 ] = "!" ;
160+ result [2 ] = expression .substring (1 );
161+ return result ;
162+ }
163+ // If in the form of !(Expression)
164+ else if ((expression .charAt (1 ) == '(' ) && (expression .charAt (expression .length () - 1 ) == ')' ))
165+ {
166+ result [0 ] = null ;
167+ result [1 ] = "!" ;
168+ result [2 ] = expression .substring (1 );
169+ }
170+ else
171+ {
172+ // I should really throw an exception here, but this is just a toy program.
173+ System .err .println ("Syntax Error!" + "\n " + "Exiting Program." );
174+ System .exit (-1 );
175+ }
176+ }
177+ return result ;
178+ }
179+
180+ /**
181+ * Returns the boolean result of the expression starting from the passed node.
182+ *
183+ * Typical use would to pass the root node of the tree in the first call; the method will then
184+ * recursively iterate through the tree until reaching the base case, and then return it's results up
185+ * the tree, evaluating as it goes.
186+ *
187+ * This method also stores it's results in evaluationResults, because we need to know the results
188+ * of each sub-expression within the tree if we want to build a truth table.
189+ */
190+ public boolean evaluate (Node node )
191+ {
192+ // Reset the evaluation results list every time root is passed.
193+ if (node == this .root )
194+ {
195+ evaluationResults = new ArrayList <Character >();
196+ }
197+ // First, check if we already know the answer...
198+ if (node .operand == 'T' )
199+ {
200+ evaluationResults .add (Boolean .toString (true ).toUpperCase ().toCharArray ()[0 ]);
201+ return true ;
202+ }
203+ else if (node .operand == 'F' )
204+ {
205+ evaluationResults .add (Boolean .toString (false ).toUpperCase ().toCharArray ()[0 ]);
206+ return false ;
207+ }
208+ // Otherwise, continue recursion.
209+ boolean result ;
210+ switch (node .operand )
211+ {
212+ // Note that we have to AVOID short-circuit evaluation here by using | and & instead of || and &&
213+ case '&' : // AND
214+ result = (evaluate (node .left ) & evaluate (node .right ));
215+ break ;
216+ case '|' : // OR
217+ result = (evaluate (node .left )) | (evaluate (node .right ));
218+ break ;
219+ case '!' : // NOT
220+ result = !(evaluate (node .right ));
221+ break ;
222+ default :
223+ // Should never reach here.
224+ result = false ; // Default value
225+ break ;
226+ }
227+ evaluationResults .add (Boolean .toString (result ).toUpperCase ().toCharArray ()[0 ]);
228+ return result ;
229+ }
230+
231+ /**
232+ * When passed a Queue of Operands, this iterates throught the list of leaf-level nodes in the tree
233+ * and sets the values of their operands from the ones in the queue.
234+ */
235+ public void setLeafOperands (Queue <Character > operands )
236+ {
237+ if (operands .size () == leafs .size ())
238+ {
239+ for (Node leaf : leafs )
240+ {
241+ leaf .setOperand (operands .poll ());
242+ }
243+ }
244+ else
245+ {
246+ // I should really throw an exception here, but this is just a toy program.
247+ System .err .println ("Internal error!" + "\n " + "Exiting Program." );
248+ System .exit (-1 );
249+ }
250+ }
251+
252+ public List <Character > getLastEvaluationResults ()
253+ {
254+ return evaluationResults ;
255+ }
256+
257+
258+ public List <String > getEvaluationOrder ()
259+ {
260+ return evaluationOrder ;
261+ }
262+ }
0 commit comments