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
* LuaJava - https://github.com/jasonsantos/luajava
* 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.addInstantiated(new StandardPlugin(manager));
mainUi = new Window();
mainUi = new Window(manager);
mainUi.setVisible(true);
}

View File

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

View File

@ -44,4 +44,9 @@ public class NumberNode extends TreeNode {
public NumberInterface getNumber() {
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 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> 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.
*/
private static Lexer<TokenType> lexer = new Lexer<TokenType>(){{
register(".", TokenType.ANY);
register("\\+|-|\\*|/|^", TokenType.OP);
register("[0-9]+(\\.[0-9]+)?", TokenType.NUM);
register("[a-zA-Z]+", TokenType.WORD);
@ -130,11 +129,15 @@ public abstract class TreeNode {
* @return the resulting tree.
*/
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;
Collections.reverse(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;
import org.nwapw.abacus.plugin.PluginManager;
import org.nwapw.abacus.tree.NumberReducer;
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.
*/
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.
*/
@ -26,13 +48,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.
@ -74,9 +100,21 @@ public class Window extends JFrame {
*/
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 = "";
lastOutput = "";
@ -84,37 +122,37 @@ 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());
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<>();
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);
@ -126,5 +164,33 @@ 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);
});
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);
}
}
});
}
}