From 76705ed92b8d06f8f43e22ee912a295339a2108d Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 10:58:27 -0700 Subject: [PATCH 1/8] Implement the ability to reduce a tree to a single variable of a type. --- src/org/nwapw/abacus/tree/NumberNode.java | 5 +++++ src/org/nwapw/abacus/tree/OpNode.java | 9 +++++++++ src/org/nwapw/abacus/tree/Reducer.java | 17 +++++++++++++++++ src/org/nwapw/abacus/tree/TreeNode.java | 2 ++ 4 files changed, 33 insertions(+) create mode 100644 src/org/nwapw/abacus/tree/Reducer.java diff --git a/src/org/nwapw/abacus/tree/NumberNode.java b/src/org/nwapw/abacus/tree/NumberNode.java index 1ac4ea9..f88ef0a 100644 --- a/src/org/nwapw/abacus/tree/NumberNode.java +++ b/src/org/nwapw/abacus/tree/NumberNode.java @@ -44,4 +44,9 @@ public class NumberNode extends TreeNode { public NumberInterface getNumber() { return number; } + + @Override + public T reduce(Reducer reducer) { + return reducer.reduceNode(this); + } } diff --git a/src/org/nwapw/abacus/tree/OpNode.java b/src/org/nwapw/abacus/tree/OpNode.java index b338064..33ba805 100644 --- a/src/org/nwapw/abacus/tree/OpNode.java +++ b/src/org/nwapw/abacus/tree/OpNode.java @@ -18,6 +18,8 @@ public class OpNode extends TreeNode { */ private TreeNode right; + private OpNode() {} + /** * Creates a new operation node with the given operation * and no child nodes. @@ -79,4 +81,11 @@ public class OpNode extends TreeNode { public void setRight(TreeNode right) { this.right = right; } + + @Override + public T reduce(Reducer reducer) { + T leftReduce = left.reduce(reducer); + T rightReduce = right.reduce(reducer); + return reducer.reduceNode(this, leftReduce, rightReduce); + } } diff --git a/src/org/nwapw/abacus/tree/Reducer.java b/src/org/nwapw/abacus/tree/Reducer.java new file mode 100644 index 0000000..966478d --- /dev/null +++ b/src/org/nwapw/abacus/tree/Reducer.java @@ -0,0 +1,17 @@ +package org.nwapw.abacus.tree; + +/** + * Interface used to reduce a tree into a single value. + * @param the value to reduce into. + */ +public interface Reducer { + + /** + * Reduces the given tree into a single value of type T. + * @param node the node being passed in to be reduced. + * @param children the already-reduced children of this node. + * @return the resulting value from the reduce. + */ + public T reduceNode(TreeNode node, Object...children); + +} diff --git a/src/org/nwapw/abacus/tree/TreeNode.java b/src/org/nwapw/abacus/tree/TreeNode.java index d549621..58e98b3 100644 --- a/src/org/nwapw/abacus/tree/TreeNode.java +++ b/src/org/nwapw/abacus/tree/TreeNode.java @@ -137,4 +137,6 @@ public abstract class TreeNode { return fromStringRecursive(string, matches); } + public abstract T reduce(Reducer reducer); + } From 93e892f92e49f2cd671e7d4453d1acb0df8c310b Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 13:24:46 -0700 Subject: [PATCH 2/8] Correctly handle un-matched tokens and end-of-string situations. --- src/org/nwapw/abacus/lexing/Lexer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/org/nwapw/abacus/lexing/Lexer.java b/src/org/nwapw/abacus/lexing/Lexer.java index 2813061..1a3cab8 100644 --- a/src/org/nwapw/abacus/lexing/Lexer.java +++ b/src/org/nwapw/abacus/lexing/Lexer.java @@ -87,11 +87,12 @@ public class Lexer { int index = startAt; ArrayList> matches = new ArrayList<>(); Match lastMatch = null; - while((lastMatch = lexOne(from, index, compare)) != null && index < from.length()){ + while(index < from.length() && (lastMatch = lexOne(from, index, compare)) != null){ if(lastMatch.getTo() == lastMatch.getFrom()) return null; matches.add(lastMatch); index += lastMatch.getTo() - lastMatch.getFrom(); } + if(lastMatch == null) return null; return matches; } From 7c4ecdc1dbf75df1a17aa63025068f6f2b88ae99 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 13:25:01 -0700 Subject: [PATCH 3/8] Remove unnecessary pattern. --- src/org/nwapw/abacus/tree/TreeNode.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/nwapw/abacus/tree/TreeNode.java b/src/org/nwapw/abacus/tree/TreeNode.java index 58e98b3..45f7741 100644 --- a/src/org/nwapw/abacus/tree/TreeNode.java +++ b/src/org/nwapw/abacus/tree/TreeNode.java @@ -14,7 +14,6 @@ public abstract class TreeNode { * The lexer used to lex tokens. */ private static Lexer lexer = new Lexer(){{ - register(".", TokenType.ANY); register("\\+|-|\\*|/|^", TokenType.OP); register("[0-9]+(\\.[0-9]+)?", TokenType.NUM); register("[a-zA-Z]+", TokenType.WORD); From 9ce35aad5410b0b9cf740079b0a8cd8d2bdbb112 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 13:25:12 -0700 Subject: [PATCH 4/8] Correctly handle invalid strings. --- src/org/nwapw/abacus/tree/TreeNode.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/nwapw/abacus/tree/TreeNode.java b/src/org/nwapw/abacus/tree/TreeNode.java index 45f7741..8540677 100644 --- a/src/org/nwapw/abacus/tree/TreeNode.java +++ b/src/org/nwapw/abacus/tree/TreeNode.java @@ -129,7 +129,9 @@ public abstract class TreeNode { * @return the resulting tree. */ public static TreeNode fromString(String string){ - ArrayList> matches = intoPostfix(string, tokenize(string)); + ArrayList> matches = tokenize(string); + if(matches == null) return null; + matches = intoPostfix(string, matches); if(matches == null) return null; Collections.reverse(matches); From 53b9b56039050fab1b9d69b7456e7e71dd872449 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 13:31:05 -0700 Subject: [PATCH 5/8] Add a new constructor to the UI, and move strings into constants. --- src/org/nwapw/abacus/Abacus.java | 2 +- src/org/nwapw/abacus/tree/NumberReducer.java | 26 ++++++++++++ src/org/nwapw/abacus/window/Window.java | 42 +++++++++++++++++--- 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 src/org/nwapw/abacus/tree/NumberReducer.java diff --git a/src/org/nwapw/abacus/Abacus.java b/src/org/nwapw/abacus/Abacus.java index 7150c4e..6397157 100644 --- a/src/org/nwapw/abacus/Abacus.java +++ b/src/org/nwapw/abacus/Abacus.java @@ -23,7 +23,7 @@ public class Abacus { } manager = new PluginManager(); manager.addInstantiated(new StandardPlugin(manager)); - mainUi = new Window(); + mainUi = new Window(manager); mainUi.setVisible(true); } diff --git a/src/org/nwapw/abacus/tree/NumberReducer.java b/src/org/nwapw/abacus/tree/NumberReducer.java new file mode 100644 index 0000000..fed6b59 --- /dev/null +++ b/src/org/nwapw/abacus/tree/NumberReducer.java @@ -0,0 +1,26 @@ +package org.nwapw.abacus.tree; + +import org.nwapw.abacus.number.NumberInterface; +import org.nwapw.abacus.plugin.PluginManager; + +public class NumberReducer implements Reducer { + + private PluginManager manager; + + public NumberReducer(PluginManager manager){ + this.manager = manager; + } + + @Override + public NumberInterface reduceNode(TreeNode node, Object... children) { + if(node instanceof NumberNode) { + return ((NumberNode) node).getNumber(); + } else if(node instanceof OpNode){ + NumberInterface left = (NumberInterface) children[0]; + NumberInterface right = (NumberInterface) children[1]; + return manager.functionFor(((OpNode) node).getOperation()).apply(left, right); + } + return null; + } + +} diff --git a/src/org/nwapw/abacus/window/Window.java b/src/org/nwapw/abacus/window/Window.java index e8e32ff..f1f6710 100644 --- a/src/org/nwapw/abacus/window/Window.java +++ b/src/org/nwapw/abacus/window/Window.java @@ -1,5 +1,9 @@ package org.nwapw.abacus.window; +import org.nwapw.abacus.plugin.PluginManager; +import org.nwapw.abacus.tree.NumberReducer; +import org.nwapw.abacus.tree.TreeNode; + import javax.swing.*; import java.awt.*; @@ -8,6 +12,21 @@ import java.awt.*; */ 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 NUMBER_SYSTEM_LABEL = "Number Type:"; + private static final String FUNCTION_LABEL = "Functions:"; + + /** + * The plugin manager used to retrieve functions. + */ + private PluginManager manager; + /** + * The reducer used to evaluate the tree. + */ + private NumberReducer reducer; + /** * A collection of outputs from the calculator. */ @@ -74,7 +93,20 @@ public class Window extends JFrame { */ private JButton functionSelectButton; - public Window() { + /** + * Creates a new window with the given manager. + * @param manager the manager to use. + */ + public Window(PluginManager manager){ + this(); + this.manager = manager; + reducer = new NumberReducer(manager); + } + + /** + * Creates a new window. + */ + private Window() { super(); @@ -84,7 +116,7 @@ public class Window extends JFrame { setSize(640, 480); inputField = new JTextField(); - inputEnterButton = new JButton("Calculate"); + inputEnterButton = new JButton(CALC_STRING); inputPanel = new JPanel(); inputPanel.setLayout(new BorderLayout()); @@ -106,15 +138,15 @@ public class Window extends JFrame { numberSystemPanel = new JPanel(); numberSystemPanel.setLayout(new BorderLayout()); - numberSystemPanel.add(new JLabel("Number Type:"), BorderLayout.NORTH); + numberSystemPanel.add(new JLabel(NUMBER_SYSTEM_LABEL), BorderLayout.NORTH); numberSystemPanel.add(numberSystemList, BorderLayout.CENTER); functionList = new JComboBox<>(); - functionSelectButton = new JButton("Select"); + functionSelectButton = new JButton(SELECT_STRING); functionSelectPanel = new JPanel(); functionSelectPanel.setLayout(new BorderLayout()); - functionSelectPanel.add(new JLabel("Functions:"), BorderLayout.NORTH); + functionSelectPanel.add(new JLabel(FUNCTION_LABEL), BorderLayout.NORTH); functionSelectPanel.add(functionList, BorderLayout.CENTER); functionSelectPanel.add(functionSelectButton, BorderLayout.SOUTH); From d746db8c9f794d8ac185cb72605bed0cdfe5161f Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 14:34:19 -0700 Subject: [PATCH 6/8] Implement a table as a history tracker. --- .../abacus/window/HistoryTableModel.java | 97 +++++++++++++++++++ src/org/nwapw/abacus/window/Window.java | 35 +++++-- 2 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 src/org/nwapw/abacus/window/HistoryTableModel.java diff --git a/src/org/nwapw/abacus/window/HistoryTableModel.java b/src/org/nwapw/abacus/window/HistoryTableModel.java new file mode 100644 index 0000000..934e699 --- /dev/null +++ b/src/org/nwapw/abacus/window/HistoryTableModel.java @@ -0,0 +1,97 @@ +package org.nwapw.abacus.window; + +import org.nwapw.abacus.tree.TreeNode; + +import javax.swing.event.TableModelListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableModel; +import java.util.ArrayList; + +public class HistoryTableModel extends AbstractTableModel { + + public static final String[] COLUMN_NAMES = { + "Input", + "Parsed Input", + "Output" + }; + + public static final Class[] CLASS_TYPES = { + String.class, + TreeNode.class, + String.class + }; + + public static class HistoryEntry { + public String input; + public TreeNode parsedInput; + public String output; + + public HistoryEntry(String input, TreeNode parsedInput, String output){ + this.input = input; + this.parsedInput = parsedInput; + this.output = output; + } + + Object nthValue(int n){ + if(n == 0) return input; + if(n == 1) return parsedInput; + if(n == 2) return output; + return null; + } + } + + ArrayList entries; + + public HistoryTableModel() { + entries = new ArrayList<>(); + } + + public void addEntry(HistoryEntry entry){ + entries.add(entry); + } + + @Override + public int getRowCount() { + return entries.size(); + } + + @Override + public int getColumnCount() { + return 3; + } + + @Override + public String getColumnName(int columnIndex) { + return COLUMN_NAMES[columnIndex]; + } + + @Override + public Class getColumnClass(int columnIndex) { + return CLASS_TYPES[columnIndex]; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + 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) { + + } +} diff --git a/src/org/nwapw/abacus/window/Window.java b/src/org/nwapw/abacus/window/Window.java index f1f6710..62ab4ad 100644 --- a/src/org/nwapw/abacus/window/Window.java +++ b/src/org/nwapw/abacus/window/Window.java @@ -45,13 +45,17 @@ public class Window extends JFrame { */ private JTextArea lastOutputArea; /** - * The text area used for all history output. + * The table used for storing history results. */ - private JTextArea historyArea; + private JTable historyTable; + /** + * The table model used for managing history. + */ + private HistoryTableModel historyModel; /** * The scroll pane for the history area. */ - private JScrollPane historyAreaScroll; + private JScrollPane historyScroll; /** * The panel where the input occurs. @@ -109,7 +113,6 @@ public class Window extends JFrame { private Window() { super(); - history = ""; lastOutput = ""; @@ -123,15 +126,15 @@ public class Window extends JFrame { inputPanel.add(inputField, BorderLayout.CENTER); inputPanel.add(inputEnterButton, BorderLayout.EAST); - historyArea = new JTextArea(history); - historyAreaScroll = new JScrollPane(historyArea); + historyModel = new HistoryTableModel(); + historyTable = new JTable(historyModel); + historyScroll = new JScrollPane(historyTable); lastOutputArea = new JTextArea(lastOutput); lastOutputArea.setEditable(false); - lastOutputArea.setText(":)"); outputPanel = new JPanel(); outputPanel.setLayout(new BorderLayout()); - outputPanel.add(historyAreaScroll, BorderLayout.CENTER); + outputPanel.add(historyScroll, BorderLayout.CENTER); outputPanel.add(lastOutputArea, BorderLayout.SOUTH); numberSystemList = new JComboBox<>(); @@ -158,5 +161,21 @@ public class Window extends JFrame { add(outputPanel, BorderLayout.CENTER); add(sidePanel, BorderLayout.EAST); add(inputPanel, BorderLayout.SOUTH); + + inputEnterButton.addActionListener((event) -> { + TreeNode parsedExpression = TreeNode.fromString(inputField.getText()); + if(parsedExpression == null){ + lastOutputArea.setText(SYNTAX_ERR_STRING); + return; + } + lastOutput = parsedExpression.reduce(reducer).toString(); + history += (history.length() == 0) ? "" : "\n\n"; + history += lastOutput; + + historyModel.addEntry(new HistoryTableModel.HistoryEntry(inputField.getText(), parsedExpression, lastOutput)); + historyTable.invalidate(); + lastOutputArea.setText(lastOutput); + inputField.setText(lastOutput); + }); } } From 81c774fcf40341bd2b8cce93b5d408801fc3e90a Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Wed, 26 Jul 2017 14:48:43 -0700 Subject: [PATCH 7/8] Add copy pasting to history. --- src/org/nwapw/abacus/window/Window.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/org/nwapw/abacus/window/Window.java b/src/org/nwapw/abacus/window/Window.java index 62ab4ad..cfbcd8f 100644 --- a/src/org/nwapw/abacus/window/Window.java +++ b/src/org/nwapw/abacus/window/Window.java @@ -6,6 +6,9 @@ import org.nwapw.abacus.tree.TreeNode; import javax.swing.*; import java.awt.*; +import java.awt.datatransfer.StringSelection; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; /** * The main UI window for the calculator. @@ -177,5 +180,17 @@ public class Window extends JFrame { lastOutputArea.setText(lastOutput); inputField.setText(lastOutput); }); + historyTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + Point clickPoint = e.getPoint(); + if(e.getClickCount() == 2){ + int row = historyTable.rowAtPoint(clickPoint); + int column = historyTable.columnAtPoint(clickPoint); + String toCopy = historyTable.getValueAt(row, column).toString(); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(toCopy), null); + } + } + }); } } From 70616db85a217def0196b7b632dc6f2942a09006 Mon Sep 17 00:00:00 2001 From: rileyJones Date: Wed, 26 Jul 2017 14:53:52 -0700 Subject: [PATCH 8/8] Update RESOURCES.md --- RESOURCES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RESOURCES.md b/RESOURCES.md index 826c8ad..b0071a7 100644 --- a/RESOURCES.md +++ b/RESOURCES.md @@ -8,3 +8,5 @@ * Rembulan(5.3) - https://github.com/mjanicek/rembulan * LuaJava - https://github.com/jasonsantos/luajava * jnlua - https://code.google.com/archive/p/jnlua/  +## Gist +* https://gist.github.com/rileyJones/1c18338821b88e92a477bfa270344db3