This commit is contained in:
Arthur Drobot 2017-07-26 15:26:35 -07:00
commit f35f83a92f
10 changed files with 243 additions and 17 deletions

View File

@ -8,3 +8,5 @@
* Rembulan(5.3) - https://github.com/mjanicek/rembulan * Rembulan(5.3) - https://github.com/mjanicek/rembulan
* LuaJava - https://github.com/jasonsantos/luajava * LuaJava - https://github.com/jasonsantos/luajava
* jnlua - https://code.google.com/archive/p/jnlua/  * jnlua - https://code.google.com/archive/p/jnlua/ 
## Gist
* https://gist.github.com/rileyJones/1c18338821b88e92a477bfa270344db3

View File

@ -23,7 +23,7 @@ public class Abacus {
} }
manager = new PluginManager(); manager = new PluginManager();
manager.addInstantiated(new StandardPlugin(manager)); manager.addInstantiated(new StandardPlugin(manager));
mainUi = new Window(); mainUi = new Window(manager);
mainUi.setVisible(true); mainUi.setVisible(true);
} }

View File

@ -87,11 +87,12 @@ public class Lexer<T> {
int index = startAt; int index = startAt;
ArrayList<Match<T>> matches = new ArrayList<>(); ArrayList<Match<T>> matches = new ArrayList<>();
Match<T> lastMatch = null; Match<T> 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; if(lastMatch.getTo() == lastMatch.getFrom()) return null;
matches.add(lastMatch); matches.add(lastMatch);
index += lastMatch.getTo() - lastMatch.getFrom(); index += lastMatch.getTo() - lastMatch.getFrom();
} }
if(lastMatch == null) return null;
return matches; return matches;
} }

View File

@ -44,4 +44,9 @@ public class NumberNode extends TreeNode {
public NumberInterface getNumber() { public NumberInterface getNumber() {
return number; return number;
} }
@Override
public <T> T reduce(Reducer<T> reducer) {
return reducer.reduceNode(this);
}
} }

View File

@ -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<NumberInterface> {
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;
}
}

View File

@ -18,6 +18,8 @@ public class OpNode extends TreeNode {
*/ */
private TreeNode right; private TreeNode right;
private OpNode() {}
/** /**
* Creates a new operation node with the given operation * Creates a new operation node with the given operation
* and no child nodes. * and no child nodes.
@ -79,4 +81,11 @@ public class OpNode extends TreeNode {
public void setRight(TreeNode right) { public void setRight(TreeNode right) {
this.right = right; this.right = right;
} }
@Override
public <T> T reduce(Reducer<T> reducer) {
T leftReduce = left.reduce(reducer);
T rightReduce = right.reduce(reducer);
return reducer.reduceNode(this, leftReduce, rightReduce);
}
} }

View File

@ -0,0 +1,17 @@
package org.nwapw.abacus.tree;
/**
* Interface used to reduce a tree into a single value.
* @param <T> the value to reduce into.
*/
public interface Reducer<T> {
/**
* 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);
}

View File

@ -14,7 +14,6 @@ public abstract class TreeNode {
* The lexer used to lex tokens. * The lexer used to lex tokens.
*/ */
private static Lexer<TokenType> lexer = new Lexer<TokenType>(){{ private static Lexer<TokenType> lexer = new Lexer<TokenType>(){{
register(".", TokenType.ANY);
register("\\+|-|\\*|/|^", TokenType.OP); register("\\+|-|\\*|/|^", TokenType.OP);
register("[0-9]+(\\.[0-9]+)?", TokenType.NUM); register("[0-9]+(\\.[0-9]+)?", TokenType.NUM);
register("[a-zA-Z]+", TokenType.WORD); register("[a-zA-Z]+", TokenType.WORD);
@ -130,11 +129,15 @@ public abstract class TreeNode {
* @return the resulting tree. * @return the resulting tree.
*/ */
public static TreeNode fromString(String string){ public static TreeNode fromString(String string){
ArrayList<Match<TokenType>> matches = intoPostfix(string, tokenize(string)); ArrayList<Match<TokenType>> matches = tokenize(string);
if(matches == null) return null;
matches = intoPostfix(string, matches);
if(matches == null) return null; if(matches == null) return null;
Collections.reverse(matches); Collections.reverse(matches);
return fromStringRecursive(string, matches); return fromStringRecursive(string, matches);
} }
public abstract <T> T reduce(Reducer<T> reducer);
} }

View File

@ -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<HistoryEntry> 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) {
}
}

View File

@ -1,13 +1,35 @@
package org.nwapw.abacus.window; 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 javax.swing.*;
import java.awt.*; 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. * The main UI window for the calculator.
*/ */
public class Window extends JFrame { 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. * A collection of outputs from the calculator.
*/ */
@ -26,13 +48,17 @@ public class Window extends JFrame {
*/ */
private JTextArea lastOutputArea; 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. * The scroll pane for the history area.
*/ */
private JScrollPane historyAreaScroll; private JScrollPane historyScroll;
/** /**
* The panel where the input occurs. * The panel where the input occurs.
@ -74,9 +100,21 @@ public class Window extends JFrame {
*/ */
private JButton functionSelectButton; private JButton functionSelectButton;
public Window() { /**
super(); * 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();
history = ""; history = "";
lastOutput = ""; lastOutput = "";
@ -84,37 +122,37 @@ public class Window extends JFrame {
setSize(640, 480); setSize(640, 480);
inputField = new JTextField(); inputField = new JTextField();
inputEnterButton = new JButton("Calculate"); inputEnterButton = new JButton(CALC_STRING);
inputPanel = new JPanel(); inputPanel = new JPanel();
inputPanel.setLayout(new BorderLayout()); inputPanel.setLayout(new BorderLayout());
inputPanel.add(inputField, BorderLayout.CENTER); inputPanel.add(inputField, BorderLayout.CENTER);
inputPanel.add(inputEnterButton, BorderLayout.EAST); inputPanel.add(inputEnterButton, BorderLayout.EAST);
historyArea = new JTextArea(history); historyModel = new HistoryTableModel();
historyAreaScroll = new JScrollPane(historyArea); historyTable = new JTable(historyModel);
historyScroll = new JScrollPane(historyTable);
lastOutputArea = new JTextArea(lastOutput); lastOutputArea = new JTextArea(lastOutput);
lastOutputArea.setEditable(false); lastOutputArea.setEditable(false);
lastOutputArea.setText(":)");
outputPanel = new JPanel(); outputPanel = new JPanel();
outputPanel.setLayout(new BorderLayout()); outputPanel.setLayout(new BorderLayout());
outputPanel.add(historyAreaScroll, BorderLayout.CENTER); outputPanel.add(historyScroll, BorderLayout.CENTER);
outputPanel.add(lastOutputArea, BorderLayout.SOUTH); outputPanel.add(lastOutputArea, BorderLayout.SOUTH);
numberSystemList = new JComboBox<>(); numberSystemList = new JComboBox<>();
numberSystemPanel = new JPanel(); numberSystemPanel = new JPanel();
numberSystemPanel.setLayout(new BorderLayout()); 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); numberSystemPanel.add(numberSystemList, BorderLayout.CENTER);
functionList = new JComboBox<>(); functionList = new JComboBox<>();
functionSelectButton = new JButton("Select"); functionSelectButton = new JButton(SELECT_STRING);
functionSelectPanel = new JPanel(); functionSelectPanel = new JPanel();
functionSelectPanel.setLayout(new BorderLayout()); 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(functionList, BorderLayout.CENTER);
functionSelectPanel.add(functionSelectButton, BorderLayout.SOUTH); functionSelectPanel.add(functionSelectButton, BorderLayout.SOUTH);
@ -126,5 +164,33 @@ public class Window extends JFrame {
add(outputPanel, BorderLayout.CENTER); add(outputPanel, BorderLayout.CENTER);
add(sidePanel, BorderLayout.EAST); add(sidePanel, BorderLayout.EAST);
add(inputPanel, BorderLayout.SOUTH); 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);
});
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);
}
}
});
} }
} }