From 12e34230ec77724d1a25d47dd10d5d4c6571230f Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 18:41:21 -0700 Subject: [PATCH 1/7] Add correct handling of failed reduces to both OpNode and Window. --- src/org/nwapw/abacus/tree/OpNode.java | 1 + src/org/nwapw/abacus/window/Window.java | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/org/nwapw/abacus/tree/OpNode.java b/src/org/nwapw/abacus/tree/OpNode.java index 8a399f7..e6565f7 100644 --- a/src/org/nwapw/abacus/tree/OpNode.java +++ b/src/org/nwapw/abacus/tree/OpNode.java @@ -86,6 +86,7 @@ public class OpNode extends TreeNode { public T reduce(Reducer reducer) { T leftReduce = left.reduce(reducer); T rightReduce = right.reduce(reducer); + if(leftReduce == null || rightReduce == null) return null; return reducer.reduceNode(this, leftReduce, rightReduce); } diff --git a/src/org/nwapw/abacus/window/Window.java b/src/org/nwapw/abacus/window/Window.java index 319ddbd..8d35f20 100644 --- a/src/org/nwapw/abacus/window/Window.java +++ b/src/org/nwapw/abacus/window/Window.java @@ -1,5 +1,6 @@ package org.nwapw.abacus.window; +import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.plugin.PluginManager; import org.nwapw.abacus.tree.NumberReducer; import org.nwapw.abacus.tree.TreeNode; @@ -19,6 +20,7 @@ public class Window extends JFrame { private static final String CALC_STRING = "Calculate"; private static final String SELECT_STRING = "Select"; private static final String SYNTAX_ERR_STRING = "Syntax Error"; + private static final String EVAL_ERR_STRING = "Evaluation Error"; private static final String NUMBER_SYSTEM_LABEL = "Number Type:"; private static final String FUNCTION_LABEL = "Functions:"; @@ -117,7 +119,12 @@ public class Window extends JFrame { lastOutputArea.setText(SYNTAX_ERR_STRING); return; } - lastOutput = parsedExpression.reduce(reducer).toString(); + NumberInterface numberInterface = parsedExpression.reduce(reducer); + if(numberInterface == null){ + lastOutputArea.setText(EVAL_ERR_STRING);; + return; + } + lastOutput = numberInterface.toString(); historyModel.addEntry(new HistoryTableModel.HistoryEntry(inputField.getText(), parsedExpression, lastOutput)); historyTable.invalidate(); lastOutputArea.setText(lastOutput); From e8d983643116da55a030bfb39ee76927c3de1dbb Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 18:44:17 -0700 Subject: [PATCH 2/7] Implement parsing functions. --- src/org/nwapw/abacus/tree/FunctionNode.java | 47 +++++++++++++++++++++ src/org/nwapw/abacus/tree/TokenType.java | 2 +- src/org/nwapw/abacus/tree/TreeNode.java | 35 ++++++++++++--- 3 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 src/org/nwapw/abacus/tree/FunctionNode.java diff --git a/src/org/nwapw/abacus/tree/FunctionNode.java b/src/org/nwapw/abacus/tree/FunctionNode.java new file mode 100644 index 0000000..bd82d38 --- /dev/null +++ b/src/org/nwapw/abacus/tree/FunctionNode.java @@ -0,0 +1,47 @@ +package org.nwapw.abacus.tree; + +import java.util.ArrayList; + +public class FunctionNode extends TreeNode { + + private String function; + private ArrayList children; + + private FunctionNode() { } + + public FunctionNode(String function){ + this.function = function; + children = new ArrayList<>(); + } + + public String getFunction() { + return function; + } + + public void addChild(TreeNode node){ + children.add(node); + } + + @Override + public T reduce(Reducer reducer) { + Object[] reducedChildren = new Object[children.size()]; + for(int i = 0; i < reducedChildren.length; i++){ + reducedChildren[i] = children.get(i).reduce(reducer); + if(reducedChildren[i] == null) return null; + } + return reducer.reduceNode(this, reducedChildren); + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append(function); + buffer.append("("); + for(int i = 0; i < children.size(); i++){ + buffer.append(children.get(i)); + buffer.append(i == children.size() - 1 ? "" : ", "); + } + buffer.append(")"); + return buffer.toString(); + } +} diff --git a/src/org/nwapw/abacus/tree/TokenType.java b/src/org/nwapw/abacus/tree/TokenType.java index ce92eb3..06301ae 100644 --- a/src/org/nwapw/abacus/tree/TokenType.java +++ b/src/org/nwapw/abacus/tree/TokenType.java @@ -6,7 +6,7 @@ package org.nwapw.abacus.tree; */ public enum TokenType { - ANY(0), OP(1), NUM(2), WORD(3), OPEN_PARENTH(4), CLOSE_PARENTH(5); + INTERNAL_FUNCTION_END(-1), INTERNAL_FUNCTION_START(-1), ANY(0), COMMA(1), OP(2), NUM(3), WORD(4), OPEN_PARENTH(5), CLOSE_PARENTH(6); /** * The priority by which this token gets sorted. diff --git a/src/org/nwapw/abacus/tree/TreeNode.java b/src/org/nwapw/abacus/tree/TreeNode.java index accdefa..f028274 100644 --- a/src/org/nwapw/abacus/tree/TreeNode.java +++ b/src/org/nwapw/abacus/tree/TreeNode.java @@ -14,6 +14,7 @@ public abstract class TreeNode { * The lexer used to lex tokens. */ protected static Lexer lexer = new Lexer(){{ + register(",", TokenType.COMMA); register("\\+|-|\\*|/|^", TokenType.OP); register("[0-9]+(\\.[0-9]+)?", TokenType.NUM); register("[a-zA-Z]+", TokenType.WORD); @@ -67,9 +68,10 @@ public abstract class TreeNode { Stack> tokenStack = new Stack<>(); while(!from.isEmpty()){ Match match = from.remove(0); - if(match.getType() == TokenType.NUM) { + TokenType matchType = match.getType(); + if(matchType == TokenType.NUM || matchType == TokenType.WORD) { output.add(match); - } else if(match.getType() == TokenType.OP){ + } else if(matchType == TokenType.OP){ String tokenString = source.substring(match.getFrom(), match.getTo()); int precedence = precedenceMap.get(tokenString); OperatorAssociativity associativity = associativityMap.get(tokenString); @@ -86,14 +88,24 @@ public abstract class TreeNode { output.add(tokenStack.pop()); } tokenStack.push(match); - } else if(match.getType() == TokenType.OPEN_PARENTH){ + } else if(matchType == TokenType.OPEN_PARENTH){ + if(!output.isEmpty() && output.get(output.size() - 1).getType() == TokenType.WORD){ + tokenStack.push(output.remove(output.size() - 1)); + output.add(new Match<>(0, 0, TokenType.INTERNAL_FUNCTION_END)); + } tokenStack.push(match); - } else if(match.getType() == TokenType.CLOSE_PARENTH){ + } else if(matchType == TokenType.CLOSE_PARENTH || matchType == TokenType.COMMA){ while(!tokenStack.empty() && tokenStack.peek().getType() != TokenType.OPEN_PARENTH){ output.add(tokenStack.pop()); } if(tokenStack.empty()) return null; - tokenStack.pop(); + if(matchType == TokenType.CLOSE_PARENTH){ + tokenStack.pop(); + if(!tokenStack.empty() && tokenStack.peek().getType() == TokenType.WORD) { + output.add(tokenStack.pop()); + output.add(new Match<>(0, 0, TokenType.INTERNAL_FUNCTION_START)); + } + } } } while(!tokenStack.empty()){ @@ -119,6 +131,19 @@ public abstract class TreeNode { else return new OpNode(source.substring(match.getFrom(), match.getTo()), left, right); } else if(match.getType() == TokenType.NUM){ return new NumberNode(Double.parseDouble(source.substring(match.getFrom(), match.getTo()))); + } else if(match.getType() == TokenType.INTERNAL_FUNCTION_START){ + if(matches.isEmpty() || matches.get(0).getType() != TokenType.WORD) return null; + Match stringName = matches.remove(0); + String functionName = source.substring(stringName.getFrom(), stringName.getTo()); + FunctionNode node = new FunctionNode(functionName); + while(!matches.isEmpty() && matches.get(0).getType() != TokenType.INTERNAL_FUNCTION_END){ + TreeNode argument = fromStringRecursive(source, matches); + if(argument == null) return null; + node.addChild(argument); + } + if(matches.isEmpty()) return null; + matches.remove(0); + return node; } return null; } From d54ec9e8fab99b2d0b6536e273f6f3554d6499dc Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 18:44:30 -0700 Subject: [PATCH 3/7] Implement reducing functions. --- src/org/nwapw/abacus/tree/NumberReducer.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/org/nwapw/abacus/tree/NumberReducer.java b/src/org/nwapw/abacus/tree/NumberReducer.java index fed6b59..2f71347 100644 --- a/src/org/nwapw/abacus/tree/NumberReducer.java +++ b/src/org/nwapw/abacus/tree/NumberReducer.java @@ -1,5 +1,6 @@ package org.nwapw.abacus.tree; +import org.nwapw.abacus.function.Function; import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.plugin.PluginManager; @@ -18,7 +19,17 @@ public class NumberReducer implements Reducer { } else if(node instanceof OpNode){ NumberInterface left = (NumberInterface) children[0]; NumberInterface right = (NumberInterface) children[1]; - return manager.functionFor(((OpNode) node).getOperation()).apply(left, right); + Function function = manager.functionFor(((OpNode) node).getOperation()); + if(function == null) return null; + return function.apply(left, right); + } else if(node instanceof FunctionNode){ + NumberInterface[] convertedChildren = new NumberInterface[children.length]; + for(int i = 0; i < convertedChildren.length; i++){ + convertedChildren[i] = (NumberInterface) children[i]; + } + Function function = manager.functionFor(((FunctionNode) node).getFunction()); + if(function == null) return null; + return function.apply(convertedChildren); } return null; } From 1b788c0a46d3e6695fb60fa939f93f563192879e Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 19:04:39 -0700 Subject: [PATCH 4/7] Comment and clean up HistoryTableModel code. --- .../abacus/window/HistoryTableModel.java | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/org/nwapw/abacus/window/HistoryTableModel.java b/src/org/nwapw/abacus/window/HistoryTableModel.java index 934e699..f488faf 100644 --- a/src/org/nwapw/abacus/window/HistoryTableModel.java +++ b/src/org/nwapw/abacus/window/HistoryTableModel.java @@ -7,20 +7,34 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import java.util.ArrayList; +/** + * A table model to store data about the history of inputs + * in the calculator. + */ public class HistoryTableModel extends AbstractTableModel { + /** + * Static array used to get the column names. + */ public static final String[] COLUMN_NAMES = { "Input", "Parsed Input", "Output" }; + /** + * Static array used to get the class of each column. + */ public static final Class[] CLASS_TYPES = { String.class, TreeNode.class, String.class }; + /** + * Class used specifically to hold data about + * the previous entries into the calculator. + */ public static class HistoryEntry { public String input; public TreeNode parsedInput; @@ -40,17 +54,27 @@ public class HistoryTableModel extends AbstractTableModel { } } + /** + * The list of entries. + */ ArrayList entries; + /** + * Creates a new empty history table model + */ public HistoryTableModel() { entries = new ArrayList<>(); } + /** + * Adds an entry to the model. + * @param entry the entry to add. + */ public void addEntry(HistoryEntry entry){ entries.add(entry); } - @Override + @Override public int getRowCount() { return entries.size(); } @@ -80,18 +104,4 @@ public class HistoryTableModel extends AbstractTableModel { return entries.get(rowIndex).nthValue(columnIndex); } - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - return; - } - - @Override - public void addTableModelListener(TableModelListener l) { - - } - - @Override - public void removeTableModelListener(TableModelListener l) { - - } } From 49f6f2d21ea617236cc3a4b4dce0ba8b65ea3d60 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 19:10:55 -0700 Subject: [PATCH 5/7] Comment and clean up the Window class. --- src/org/nwapw/abacus/window/Window.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/org/nwapw/abacus/window/Window.java b/src/org/nwapw/abacus/window/Window.java index 8d35f20..d2d4891 100644 --- a/src/org/nwapw/abacus/window/Window.java +++ b/src/org/nwapw/abacus/window/Window.java @@ -18,18 +18,27 @@ import java.awt.event.MouseEvent; public class Window extends JFrame { private static final String CALC_STRING = "Calculate"; - private static final String SELECT_STRING = "Select"; private static final String SYNTAX_ERR_STRING = "Syntax Error"; private static final String EVAL_ERR_STRING = "Evaluation Error"; private static final String NUMBER_SYSTEM_LABEL = "Number Type:"; private static final String FUNCTION_LABEL = "Functions:"; + /** + * Array of Strings to which the "calculate" button's text + * changes. For instance, in the graph tab, the name will + * be "Graph" and not "Calculate". + */ private static final String[] BUTTON_NAMES = { CALC_STRING, CALC_STRING }; - private static boolean[] BUTTON_ENABLED = { + /** + * Array of booleans that determine whether the input + * field and the input button are enabled at a particular + * index. + */ + private static boolean[] INPUT_ENABLED = { true, false }; @@ -131,6 +140,10 @@ public class Window extends JFrame { inputField.setText(""); }; + /** + * Array of listeners that tell the input button how to behave + * at a given input tab. + */ private ActionListener[] listeners = { evaluateListener, null @@ -205,7 +218,7 @@ public class Window extends JFrame { pane.add("Settings", settingsPanel); pane.addChangeListener(e -> { int selectionIndex = pane.getSelectedIndex(); - boolean enabled = BUTTON_ENABLED[selectionIndex]; + boolean enabled = INPUT_ENABLED[selectionIndex]; ActionListener listener = listeners[selectionIndex]; inputEnterButton.setText(BUTTON_NAMES[selectionIndex]); inputField.setEnabled(enabled); From ba3a7339282fdbd844f711db04794e427d9500c1 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 19:16:10 -0700 Subject: [PATCH 6/7] Add comments to NumberReducer and FunctionNode. --- src/org/nwapw/abacus/tree/FunctionNode.java | 24 ++++++++++++++++++++ src/org/nwapw/abacus/tree/NumberReducer.java | 11 +++++++++ 2 files changed, 35 insertions(+) diff --git a/src/org/nwapw/abacus/tree/FunctionNode.java b/src/org/nwapw/abacus/tree/FunctionNode.java index bd82d38..f8dc8c8 100644 --- a/src/org/nwapw/abacus/tree/FunctionNode.java +++ b/src/org/nwapw/abacus/tree/FunctionNode.java @@ -2,22 +2,46 @@ package org.nwapw.abacus.tree; import java.util.ArrayList; +/** + * A node that represents a function call. + */ public class FunctionNode extends TreeNode { + /** + * The name of the function being called + */ private String function; + /** + * The list of arguments to the function. + */ private ArrayList children; + /** + * Creates a function node with no function. + */ private FunctionNode() { } + /** + * Creates a new function node with the given function name. + * @param function the function name. + */ public FunctionNode(String function){ this.function = function; children = new ArrayList<>(); } + /** + * Gets the function name for this node. + * @return the function name. + */ public String getFunction() { return function; } + /** + * Adds a child to this node. + * @param node the child to add. + */ public void addChild(TreeNode node){ children.add(node); } diff --git a/src/org/nwapw/abacus/tree/NumberReducer.java b/src/org/nwapw/abacus/tree/NumberReducer.java index 2f71347..ab80374 100644 --- a/src/org/nwapw/abacus/tree/NumberReducer.java +++ b/src/org/nwapw/abacus/tree/NumberReducer.java @@ -4,10 +4,21 @@ import org.nwapw.abacus.function.Function; import org.nwapw.abacus.number.NumberInterface; import org.nwapw.abacus.plugin.PluginManager; +/** + * A reducer implementation that turns a tree into a single number. + * This is not always guaranteed to work. + */ public class NumberReducer implements Reducer { + /** + * The plugin manager from which to draw the functions. + */ private PluginManager manager; + /** + * Creates a new number reducer with the given plugin manager. + * @param manager the plugin manager. + */ public NumberReducer(PluginManager manager){ this.manager = manager; } From cc1d67a078282a7916df0313cde0a3ea67da7e55 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 19:28:57 -0700 Subject: [PATCH 7/7] Fix strangely incomplete comment. --- src/org/nwapw/abacus/number/NumberInterface.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/nwapw/abacus/number/NumberInterface.java b/src/org/nwapw/abacus/number/NumberInterface.java index 85f2214..f425a30 100755 --- a/src/org/nwapw/abacus/number/NumberInterface.java +++ b/src/org/nwapw/abacus/number/NumberInterface.java @@ -49,8 +49,8 @@ public interface NumberInterface { /** * Raises this number to an integer power. - * @param exponent - * @return + * @param exponent the exponent to which to take the number. + * @return the resulting value. */ NumberInterface intPow(int exponent);