mirror of
https://github.com/DanilaFe/abacus
synced 2026-01-25 08:05:19 +00:00
Compare commits
15 Commits
promotion-
...
configurat
| Author | SHA1 | Date | |
|---|---|---|---|
| 5aba5c350b | |||
| 21b7bd5e2b | |||
| fd246f935c | |||
| 6604af5b0f | |||
| d49a763e8f | |||
| 48a4d8adc2 | |||
| 5417b45106 | |||
| 585cabc478 | |||
| 28802cfed3 | |||
| 428df8bfd3 | |||
| 146f3994ef | |||
| daffdb6b42 | |||
| 178f59ef7b | |||
| 61616a428a | |||
| 9c77fa8aeb |
@@ -1,4 +1,3 @@
|
||||
dependencies {
|
||||
compile 'com.moandjiezana.toml:toml4j:0.7.1'
|
||||
testCompile 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
package org.nwapw.abacus;
|
||||
|
||||
import org.nwapw.abacus.config.Configuration;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.number.PromotionManager;
|
||||
import org.nwapw.abacus.parsing.LexerTokenizer;
|
||||
import org.nwapw.abacus.parsing.ShuntingYardParser;
|
||||
import org.nwapw.abacus.parsing.TreeBuilder;
|
||||
import org.nwapw.abacus.plugin.NumberImplementation;
|
||||
import org.nwapw.abacus.plugin.PluginManager;
|
||||
import org.nwapw.abacus.plugin.StandardPlugin;
|
||||
import org.nwapw.abacus.tree.NumberReducer;
|
||||
import org.nwapw.abacus.tree.TreeNode;
|
||||
|
||||
/**
|
||||
* The main calculator class. This is responsible
|
||||
* for piecing together all of the components, allowing
|
||||
* their interaction with each other.
|
||||
*/
|
||||
public class Abacus {
|
||||
|
||||
/**
|
||||
* The default number implementation to be used if no other one is available / selected.
|
||||
*/
|
||||
public static final NumberImplementation DEFAULT_IMPLEMENTATION = StandardPlugin.IMPLEMENTATION_NAIVE;
|
||||
|
||||
/**
|
||||
* The plugin manager responsible for
|
||||
* loading and unloading plugins,
|
||||
* and getting functions from them.
|
||||
*/
|
||||
private PluginManager pluginManager;
|
||||
/**
|
||||
* The reducer used to evaluate the tree.
|
||||
*/
|
||||
private NumberReducer numberReducer;
|
||||
/**
|
||||
* The configuration loaded from a file.
|
||||
*/
|
||||
private Configuration configuration;
|
||||
/**
|
||||
* The tree builder used to construct a tree
|
||||
* from a string.
|
||||
*/
|
||||
private TreeBuilder treeBuilder;
|
||||
/**
|
||||
* Promotion manager responsible for the promotion system.
|
||||
*/
|
||||
private PromotionManager promotionManager;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the Abacus calculator.
|
||||
*
|
||||
* @param configuration the configuration object for this Abacus instance.
|
||||
*/
|
||||
public Abacus(Configuration configuration) {
|
||||
pluginManager = new PluginManager(this);
|
||||
numberReducer = new NumberReducer(this);
|
||||
this.configuration = new Configuration(configuration);
|
||||
LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
||||
ShuntingYardParser shuntingYardParser = new ShuntingYardParser();
|
||||
treeBuilder = new TreeBuilder<>(lexerTokenizer, shuntingYardParser);
|
||||
promotionManager = new PromotionManager(this);
|
||||
|
||||
pluginManager.addListener(shuntingYardParser);
|
||||
pluginManager.addListener(lexerTokenizer);
|
||||
pluginManager.addListener(promotionManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the promotion manager.
|
||||
*
|
||||
* @return the promotion manager.
|
||||
*/
|
||||
public PromotionManager getPromotionManager() {
|
||||
return promotionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current tree builder.
|
||||
*
|
||||
* @return the main tree builder in this abacus instance.
|
||||
*/
|
||||
public TreeBuilder getTreeBuilder() {
|
||||
return treeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current plugin manager,
|
||||
*
|
||||
* @return the plugin manager in this abacus instance.
|
||||
*/
|
||||
public PluginManager getPluginManager() {
|
||||
return pluginManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reducer that is responsible for transforming
|
||||
* an expression into a number.
|
||||
*
|
||||
* @return the number reducer in this abacus instance.
|
||||
*/
|
||||
public NumberReducer getNumberReducer() {
|
||||
return numberReducer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration object associated with this instance.
|
||||
*
|
||||
* @return the configuration object.
|
||||
*/
|
||||
public Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string into a tree structure using the main
|
||||
* tree builder.
|
||||
*
|
||||
* @param input the input string to parse
|
||||
* @return the resulting tree, null if the tree builder or the produced tree are null.
|
||||
*/
|
||||
public TreeNode parseString(String input) {
|
||||
return treeBuilder.fromString(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given tree using the main
|
||||
* number reducer.
|
||||
*
|
||||
* @param tree the tree to reduce, must not be null.
|
||||
* @return the resulting number, or null of the reduction failed.
|
||||
*/
|
||||
public NumberInterface evaluateTree(TreeNode tree) {
|
||||
return tree.reduce(numberReducer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number implementation.
|
||||
*
|
||||
* @return the number implementation to use for creating numbers.
|
||||
*/
|
||||
public NumberImplementation getNumberImplementation() {
|
||||
NumberImplementation selectedImplementation =
|
||||
pluginManager.numberImplementationFor(configuration.getNumberImplementation());
|
||||
if (selectedImplementation != null) return selectedImplementation;
|
||||
return DEFAULT_IMPLEMENTATION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
package org.nwapw.abacus.config;
|
||||
|
||||
import com.moandjiezana.toml.Toml;
|
||||
import com.moandjiezana.toml.TomlWriter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The configuration object that stores
|
||||
* options that the user can change.
|
||||
*/
|
||||
public class Configuration {
|
||||
|
||||
/**
|
||||
* The defaults TOML string.
|
||||
*/
|
||||
private static final String DEFAULT_CONFIG =
|
||||
"numberImplementation = \"naive\"\n" +
|
||||
"disabledPlugins = []";
|
||||
/**
|
||||
* The defaults TOML object, parsed from the string.
|
||||
*/
|
||||
private static final Toml DEFAULT_TOML = new Toml().read(DEFAULT_CONFIG);
|
||||
/**
|
||||
* The TOML writer used to write this configuration to a file.
|
||||
*/
|
||||
private static final TomlWriter TOML_WRITER = new TomlWriter();
|
||||
|
||||
/**
|
||||
* The computation delay for which the thread can run without interruption.
|
||||
*/
|
||||
private double computationDelay = 0;
|
||||
/**
|
||||
* The implementation of the number that should be used.
|
||||
*/
|
||||
private String numberImplementation = "<default>";
|
||||
/**
|
||||
* The list of disabled plugins in this Configuration.
|
||||
*/
|
||||
private Set<String> disabledPlugins = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Creates a new configuration form the given configuration.
|
||||
*
|
||||
* @param copyFrom the configuration to copy.
|
||||
*/
|
||||
public Configuration(Configuration copyFrom) {
|
||||
copyFrom(copyFrom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new configuration with the given values.
|
||||
*
|
||||
* @param computationDelay the delay before the computation gets killed.
|
||||
* @param numberImplementation the number implementation, like "naive" or "precise"
|
||||
* @param disabledPlugins the list of disabled plugins.
|
||||
*/
|
||||
public Configuration(double computationDelay, String numberImplementation, String[] disabledPlugins) {
|
||||
this.computationDelay = computationDelay;
|
||||
this.numberImplementation = numberImplementation;
|
||||
this.disabledPlugins.addAll(Arrays.asList(disabledPlugins));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a configuration from a given file, keeping non-specified fields default.
|
||||
*
|
||||
* @param fromFile the file to load from.
|
||||
*/
|
||||
public Configuration(File fromFile) {
|
||||
if (!fromFile.exists()) return;
|
||||
copyFrom(new Toml(DEFAULT_TOML).read(fromFile).to(Configuration.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the values from the given configuration into this one.
|
||||
*
|
||||
* @param otherConfiguration the configuration to copy from.
|
||||
*/
|
||||
public void copyFrom(Configuration otherConfiguration) {
|
||||
this.computationDelay = otherConfiguration.computationDelay;
|
||||
this.numberImplementation = otherConfiguration.numberImplementation;
|
||||
this.disabledPlugins.addAll(otherConfiguration.disabledPlugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this configuration to the given file, creating
|
||||
* any directories that do not exist.
|
||||
*
|
||||
* @param file the file to save to.
|
||||
*/
|
||||
public void saveTo(File file) {
|
||||
if (file.getParentFile() != null) file.getParentFile().mkdirs();
|
||||
try {
|
||||
TOML_WRITER.write(this, file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of this configuration as a string.
|
||||
*
|
||||
* @return the string that represents this configuration.
|
||||
*/
|
||||
public String asTomlString() {
|
||||
return TOML_WRITER.write(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number implementation from this configuration.
|
||||
*
|
||||
* @return the number implementation.
|
||||
*/
|
||||
public String getNumberImplementation() {
|
||||
return numberImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number implementation for the configuration
|
||||
*
|
||||
* @param numberImplementation the number implementation.
|
||||
*/
|
||||
public void setNumberImplementation(String numberImplementation) {
|
||||
this.numberImplementation = numberImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of disabled plugins.
|
||||
*
|
||||
* @return the list of disabled plugins.
|
||||
*/
|
||||
public Set<String> getDisabledPlugins() {
|
||||
return disabledPlugins;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the computation delay specified in the configuration.
|
||||
*
|
||||
* @return the computaton delay.
|
||||
*/
|
||||
public double getComputationDelay() {
|
||||
return computationDelay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the computation delay.
|
||||
*
|
||||
* @param computationDelay the new computation delay.
|
||||
*/
|
||||
public void setComputationDelay(double computationDelay) {
|
||||
this.computationDelay = computationDelay;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.nwapw.abacus.function;
|
||||
|
||||
/**
|
||||
* Exception thrown if the function parameters do not match
|
||||
* requirements.
|
||||
*/
|
||||
public class DomainException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Creates a new DomainException.
|
||||
* @param reason the reason for which the exception is thrown.
|
||||
*/
|
||||
public DomainException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DomainException with a default message.
|
||||
*/
|
||||
public DomainException(){
|
||||
this("Domain Error");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.nwapw.abacus.plugin;
|
||||
|
||||
import org.nwapw.abacus.function.*;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.variables.VariableDatabase;
|
||||
|
||||
/**
|
||||
* A plugin class that can be externally implemented and loaded via the
|
||||
@@ -219,4 +220,12 @@ public abstract class Plugin {
|
||||
*/
|
||||
public abstract void onDisable();
|
||||
|
||||
/**
|
||||
* Get the variable database.
|
||||
* @return the variable database.
|
||||
*/
|
||||
public final VariableDatabase getVariableDatabase(){
|
||||
return manager.getVariableDatabase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.nwapw.abacus.plugin;
|
||||
import org.nwapw.abacus.Abacus;
|
||||
import org.nwapw.abacus.function.*;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.variables.VariableDatabase;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
@@ -421,4 +422,12 @@ public class PluginManager {
|
||||
public Set<Class<?>> getLoadedPluginClasses() {
|
||||
return loadedPluginClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the variable database.
|
||||
* @return the database.
|
||||
*/
|
||||
public VariableDatabase getVariableDatabase(){
|
||||
return abacus.getVariableDatabase();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package org.nwapw.abacus.plugin;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.nwapw.abacus.function.*;
|
||||
import org.nwapw.abacus.number.NaiveNumber;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.number.PreciseNumber;
|
||||
import org.nwapw.abacus.tree.Reducer;
|
||||
import org.nwapw.abacus.tree.TreeNode;
|
||||
import org.nwapw.abacus.tree.VariableNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -14,6 +19,40 @@ import java.util.HashMap;
|
||||
*/
|
||||
public class StandardPlugin extends Plugin {
|
||||
|
||||
/**
|
||||
* The set operator.
|
||||
*/
|
||||
public final TreeValueOperator opSet = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
|
||||
@Override
|
||||
public boolean matchesParams(NumberImplementation implementation, TreeNode[] params) {
|
||||
return params.length == 2 && params[0] instanceof VariableNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumberInterface applyWithReducerInternal(NumberImplementation implementation, Reducer<? extends NumberInterface> reducer, TreeNode[] params) {
|
||||
String assignTo = ((VariableNode) params[0]).getVariable();
|
||||
NumberInterface value = params[1].reduce(reducer);
|
||||
getVariableDatabase().getVariables().put(assignTo, value);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The define operator.
|
||||
*/
|
||||
public final TreeValueOperator opDefine = new TreeValueOperator(OperatorAssociativity.LEFT, OperatorType.BINARY_INFIX, 0) {
|
||||
@Override
|
||||
public boolean matchesParams(NumberImplementation implementation, TreeNode[] params) {
|
||||
return params.length == 2 && params[0] instanceof VariableNode;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public NumberInterface applyWithReducerInternal(NumberImplementation implementation, Reducer<? extends NumberInterface> reducer, TreeNode[] params) {
|
||||
String assignTo = ((VariableNode) params[0]).getVariable();
|
||||
getVariableDatabase().getDefinitions().put(assignTo, params[1]);
|
||||
return params[1].reduce(reducer);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The addition operator, +
|
||||
*/
|
||||
@@ -712,6 +751,9 @@ public class StandardPlugin extends Plugin {
|
||||
registerOperator("^", OP_CARET);
|
||||
registerOperator("!", OP_FACTORIAL);
|
||||
|
||||
registerTreeValueOperator("=", opSet);
|
||||
registerTreeValueOperator(":=", opDefine);
|
||||
|
||||
registerOperator("nPr", OP_NPR);
|
||||
registerOperator("nCr", OP_NCR);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.nwapw.abacus.function.TreeValueOperator;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.number.PromotionManager;
|
||||
import org.nwapw.abacus.number.PromotionResult;
|
||||
import org.nwapw.abacus.variables.VariableDatabase;
|
||||
|
||||
/**
|
||||
* A reducer implementation that turns a tree into a single number.
|
||||
@@ -35,6 +36,12 @@ public class NumberReducer implements Reducer<NumberInterface> {
|
||||
if (node instanceof NumberNode) {
|
||||
return abacus.getNumberImplementation().instanceForString(((NumberNode) node).getNumber());
|
||||
} else if (node instanceof VariableNode) {
|
||||
VariableDatabase database = abacus.getVariableDatabase();
|
||||
String name = ((VariableNode) node).getVariable();
|
||||
NumberInterface variable = database.getVariables().get(name);
|
||||
if(variable != null) return variable;
|
||||
TreeNode definition = database.getDefinitions().get(name);
|
||||
if(definition != null) return definition.reduce(this);
|
||||
return abacus.getNumberImplementation().instanceForString("0");
|
||||
} else if (node instanceof NumberBinaryNode) {
|
||||
NumberInterface left = (NumberInterface) children[0];
|
||||
|
||||
90
core/src/main/kotlin/org/nwapw/abacus/Abacus.kt
Normal file
90
core/src/main/kotlin/org/nwapw/abacus/Abacus.kt
Normal file
@@ -0,0 +1,90 @@
|
||||
package org.nwapw.abacus
|
||||
|
||||
import org.nwapw.abacus.config.Configuration
|
||||
import org.nwapw.abacus.number.NumberInterface
|
||||
import org.nwapw.abacus.number.PromotionManager
|
||||
import org.nwapw.abacus.parsing.LexerTokenizer
|
||||
import org.nwapw.abacus.parsing.ShuntingYardParser
|
||||
import org.nwapw.abacus.parsing.TreeBuilder
|
||||
import org.nwapw.abacus.plugin.NumberImplementation
|
||||
import org.nwapw.abacus.plugin.PluginManager
|
||||
import org.nwapw.abacus.plugin.StandardPlugin
|
||||
import org.nwapw.abacus.tree.NumberReducer
|
||||
import org.nwapw.abacus.tree.TreeNode
|
||||
import org.nwapw.abacus.variables.VariableDatabase
|
||||
|
||||
/**
|
||||
* Core class to handle all mathematics.
|
||||
*
|
||||
* The main calculator class. This is responsible
|
||||
* for piecing together all of the components, allowing
|
||||
* their interaction with each other.
|
||||
*
|
||||
* @property configuration the configuration to use.
|
||||
*/
|
||||
class Abacus(val configuration: Configuration) {
|
||||
|
||||
/**
|
||||
* The tokenizer used to convert strings into tokens.
|
||||
*/
|
||||
private val tokenizer = LexerTokenizer()
|
||||
/**
|
||||
* Parser the parser used to convert tokens into trees.
|
||||
*/
|
||||
private val parser = ShuntingYardParser()
|
||||
/**
|
||||
* The plugin manager used to handle loading and unloading plugins.
|
||||
*/
|
||||
val pluginManager = PluginManager(this)
|
||||
/**
|
||||
* The reducer used to turn trees into a single number.
|
||||
*/
|
||||
val numberReducer = NumberReducer(this)
|
||||
/**
|
||||
* The tree builder that handles the conversion of strings into trees.
|
||||
*/
|
||||
val treeBuilder = TreeBuilder(tokenizer, parser)
|
||||
/**
|
||||
* The promotion manager used to convert between number implementations.
|
||||
*/
|
||||
val promotionManager = PromotionManager(this)
|
||||
/**
|
||||
* The database of variable definitions.
|
||||
*/
|
||||
val variableDatabase = VariableDatabase(this)
|
||||
/**
|
||||
* The number implementation used by default.
|
||||
*/
|
||||
val numberImplementation: NumberImplementation
|
||||
get() {
|
||||
val selectedImplementation =
|
||||
pluginManager.numberImplementationFor(configuration.numberImplementation)
|
||||
if (selectedImplementation != null) return selectedImplementation
|
||||
return StandardPlugin.IMPLEMENTATION_NAIVE
|
||||
}
|
||||
|
||||
init {
|
||||
pluginManager.addListener(tokenizer)
|
||||
pluginManager.addListener(parser)
|
||||
pluginManager.addListener(promotionManager)
|
||||
pluginManager.addListener(variableDatabase)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string into a tree structure using the main
|
||||
* tree builder.
|
||||
*
|
||||
* @param input the input string to parse
|
||||
* @return the resulting tree, null if the tree builder or the produced tree are null.
|
||||
*/
|
||||
fun parseString(input: String): TreeNode? = treeBuilder.fromString(input)
|
||||
/**
|
||||
* Evaluates the given tree using the main
|
||||
* number reducer.
|
||||
*
|
||||
* @param tree the tree to reduce, must not be null.
|
||||
* @return the resulting number, or null of the reduction failed.
|
||||
*/
|
||||
fun evaluateTree(tree: TreeNode): NumberInterface? = tree.reduce(numberReducer)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.nwapw.abacus.config
|
||||
|
||||
/**
|
||||
* A class that holds information that tells Abacus how to behave.
|
||||
*
|
||||
* Configuration stores information about how Abacus should behave, for
|
||||
* instance, what number implementation it should use and what
|
||||
* plugins should be ignored during loading.
|
||||
*
|
||||
* @property numberImplementation the number implementation Abacus should use for loading.
|
||||
* @param disabledPlugins the plugins that should be disabled and not loaded by the plugin manager.
|
||||
*/
|
||||
open class Configuration(var numberImplementation: String = "<default>", disabledPlugins: Array<String> = emptyArray()) {
|
||||
|
||||
/**
|
||||
* The set of disabled plugins that should be ignored by the plugin manager.
|
||||
*/
|
||||
val disabledPlugins = disabledPlugins.toMutableSet()
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.nwapw.abacus.function.applicable
|
||||
|
||||
import org.nwapw.abacus.function.DomainException
|
||||
import org.nwapw.abacus.plugin.NumberImplementation
|
||||
|
||||
/**
|
||||
@@ -25,7 +26,7 @@ interface Applicable<in T : Any, out O : Any> {
|
||||
* @param params the parameters to apply to.
|
||||
* @return the result of the application.
|
||||
*/
|
||||
fun applyInternal(implementation: NumberImplementation, params: Array<out T>): O?
|
||||
fun applyInternal(implementation: NumberImplementation, params: Array<out T>): O
|
||||
|
||||
/**
|
||||
* If the parameters can be used with this applicable, returns
|
||||
@@ -34,8 +35,8 @@ interface Applicable<in T : Any, out O : Any> {
|
||||
* @param params the parameters to apply to.
|
||||
* @return the result of the operation, or null if parameters do not match.
|
||||
*/
|
||||
fun apply(implementation: NumberImplementation, vararg params: T): O? {
|
||||
if (!matchesParams(implementation, params)) return null
|
||||
fun apply(implementation: NumberImplementation, vararg params: T): O {
|
||||
if (!matchesParams(implementation, params)) throw DomainException()
|
||||
return applyInternal(implementation, params)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.nwapw.abacus.function.applicable
|
||||
|
||||
import org.nwapw.abacus.function.DomainException
|
||||
import org.nwapw.abacus.plugin.NumberImplementation
|
||||
import org.nwapw.abacus.tree.Reducer
|
||||
|
||||
@@ -27,7 +28,7 @@ interface ReducerApplicable<in T : Any, out O : Any, in R : Any> {
|
||||
* @param params the arguments to apply to.
|
||||
* @return the result of the application.
|
||||
*/
|
||||
fun applyWithReducerInternal(implementation: NumberImplementation, reducer: Reducer<R>, params: Array<out T>): O?
|
||||
fun applyWithReducerInternal(implementation: NumberImplementation, reducer: Reducer<R>, params: Array<out T>): O
|
||||
|
||||
/**
|
||||
* Applies this applicable to the given arguments, and reducer,
|
||||
@@ -36,8 +37,8 @@ interface ReducerApplicable<in T : Any, out O : Any, in R : Any> {
|
||||
* @param params the arguments to apply to.
|
||||
* @return the result of the application, or null if the arguments are incompatible.
|
||||
*/
|
||||
fun applyWithReducer(implementation: NumberImplementation, reducer: Reducer<R>, vararg params: T): O? {
|
||||
if (!matchesParams(implementation, params)) return null
|
||||
fun applyWithReducer(implementation: NumberImplementation, reducer: Reducer<R>, vararg params: T): O {
|
||||
if (!matchesParams(implementation, params)) throw DomainException()
|
||||
return applyWithReducerInternal(implementation, reducer, params)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.nwapw.abacus.variables
|
||||
|
||||
import org.nwapw.abacus.Abacus
|
||||
import org.nwapw.abacus.number.NumberInterface
|
||||
import org.nwapw.abacus.plugin.PluginListener
|
||||
import org.nwapw.abacus.plugin.PluginManager
|
||||
import org.nwapw.abacus.tree.TreeNode
|
||||
|
||||
/**
|
||||
* A database for variables and definition.
|
||||
*
|
||||
* The variable database is used to keep track of
|
||||
* variables and definitions throughout the calculator.
|
||||
*
|
||||
* @property abacus the Abacus instance.
|
||||
*/
|
||||
class VariableDatabase(val abacus: Abacus): PluginListener {
|
||||
|
||||
/**
|
||||
* The variables that are stored in the database.
|
||||
*/
|
||||
val variables = mutableMapOf<String, NumberInterface>()
|
||||
/**
|
||||
* The definitions that are stored in the database.
|
||||
*/
|
||||
val definitions = mutableMapOf<String, TreeNode>()
|
||||
|
||||
override fun onLoad(manager: PluginManager?) {
|
||||
|
||||
}
|
||||
|
||||
override fun onUnload(manager: PluginManager?) {
|
||||
variables.clear()
|
||||
definitions.clear()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,13 +5,14 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.nwapw.abacus.Abacus;
|
||||
import org.nwapw.abacus.config.Configuration;
|
||||
import org.nwapw.abacus.function.DomainException;
|
||||
import org.nwapw.abacus.number.NumberInterface;
|
||||
import org.nwapw.abacus.plugin.StandardPlugin;
|
||||
import org.nwapw.abacus.tree.TreeNode;
|
||||
|
||||
public class CalculationTests {
|
||||
|
||||
private static Abacus abacus = new Abacus(new Configuration(0, "precise", new String[]{}));
|
||||
private static Abacus abacus = new Abacus(new Configuration( "precise", new String[]{}));
|
||||
|
||||
@BeforeClass
|
||||
public static void prepareTests() {
|
||||
@@ -28,11 +29,14 @@ public class CalculationTests {
|
||||
Assert.assertTrue(result.toString().startsWith(output));
|
||||
}
|
||||
|
||||
private void testEvalError(String input, String parseOutput) {
|
||||
private void testDomainException(String input, String parseOutput) {
|
||||
TreeNode parsedTree = abacus.parseString(input);
|
||||
Assert.assertNotNull(parsedTree);
|
||||
Assert.assertEquals(parsedTree.toString(), parseOutput);
|
||||
Assert.assertNull(abacus.evaluateTree(parsedTree));
|
||||
try {
|
||||
abacus.evaluateTree(parsedTree);
|
||||
Assert.fail("Function did not throw DomainException.");
|
||||
} catch (DomainException e){ }
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,7 +77,7 @@ public class CalculationTests {
|
||||
|
||||
@Test
|
||||
public void testLn() {
|
||||
testEvalError("ln(-1)", "ln((1)`)");
|
||||
testDomainException("ln(-1)", "ln((1)`)");
|
||||
testOutput("ln2", "ln(2)", "0.6931471805599453094172321214581765680755");
|
||||
}
|
||||
|
||||
@@ -100,8 +104,8 @@ public class CalculationTests {
|
||||
testOutput("2^-1", "(2^(1)`)", "0.5");
|
||||
testOutput("2^50", "(2^50)", "112589990684262");
|
||||
testOutput("7^(-sqrt2*17)", "(7^((sqrt(2)*17))`)", "4.81354609155297814551845300063563");
|
||||
testEvalError("0^0", "(0^0)");
|
||||
testEvalError("(-13)^.9999", "((13)`^.9999)");
|
||||
testDomainException("0^0", "(0^0)");
|
||||
testDomainException("(-13)^.9999", "((13)`^.9999)");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.List;
|
||||
|
||||
public class TokenizerTests {
|
||||
|
||||
private static Abacus abacus = new Abacus(new Configuration(0, "precise", new String[]{}));
|
||||
private static Abacus abacus = new Abacus(new Configuration("precise", new String[]{}));
|
||||
private static LexerTokenizer lexerTokenizer = new LexerTokenizer();
|
||||
private static NumberFunction subtractFunction = new NumberFunction() {
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
apply plugin: 'application'
|
||||
|
||||
dependencies {
|
||||
compile 'com.moandjiezana.toml:toml4j:0.7.1'
|
||||
compile project(':core')
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.nwapw.abacus.Abacus;
|
||||
import org.nwapw.abacus.config.Configuration;
|
||||
import org.nwapw.abacus.function.Documentation;
|
||||
import org.nwapw.abacus.function.DocumentationType;
|
||||
import org.nwapw.abacus.function.DomainException;
|
||||
import org.nwapw.abacus.number.*;
|
||||
import org.nwapw.abacus.plugin.ClassFinder;
|
||||
import org.nwapw.abacus.plugin.PluginListener;
|
||||
@@ -152,6 +153,8 @@ public class AbacusController implements PluginListener {
|
||||
return resultingString;
|
||||
} catch (ComputationInterruptedException exception) {
|
||||
return ERR_STOP;
|
||||
} catch (DomainException exception) {
|
||||
return exception.getMessage();
|
||||
} catch (RuntimeException exception) {
|
||||
exception.printStackTrace();
|
||||
return ERR_EXCEPTION;
|
||||
@@ -194,7 +197,7 @@ public class AbacusController implements PluginListener {
|
||||
*/
|
||||
private final Runnable TIMER_RUNNABLE = () -> {
|
||||
try {
|
||||
Configuration abacusConfig = abacus.getConfiguration();
|
||||
ExtendedConfiguration abacusConfig = (ExtendedConfiguration) abacus.getConfiguration();
|
||||
if (abacusConfig.getComputationDelay() == 0) return;
|
||||
Thread.sleep((long) (abacusConfig.getComputationDelay() * 1000));
|
||||
performStop();
|
||||
@@ -254,12 +257,12 @@ public class AbacusController implements PluginListener {
|
||||
if (oldValue.equals(settingsTab)) alertIfApplyNeeded(true);
|
||||
});
|
||||
|
||||
abacus = new Abacus(new Configuration(CONFIG_FILE));
|
||||
abacus = new Abacus(new ExtendedConfiguration(CONFIG_FILE));
|
||||
PluginManager abacusPluginManager = abacus.getPluginManager();
|
||||
abacusPluginManager.addListener(this);
|
||||
performScan();
|
||||
|
||||
computationLimitField.setText(Double.toString(abacus.getConfiguration().getComputationDelay()));
|
||||
computationLimitField.setText(Double.toString(((ExtendedConfiguration) abacus.getConfiguration()).getComputationDelay()));
|
||||
computationLimitField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (!newValue.matches("(\\d+(\\.\\d*)?)?")) {
|
||||
computationLimitField.setText(oldValue);
|
||||
@@ -336,8 +339,8 @@ public class AbacusController implements PluginListener {
|
||||
if (!pluginEntry.isEnabled()) disabledPlugins.add(pluginEntry.getClassName());
|
||||
}
|
||||
if (computationLimitField.getText().matches("\\d*(\\.\\d+)?") && computationLimitField.getText().length() != 0)
|
||||
configuration.setComputationDelay(Double.parseDouble(computationLimitField.getText()));
|
||||
configuration.saveTo(CONFIG_FILE);
|
||||
((ExtendedConfiguration) configuration).setComputationDelay(Double.parseDouble(computationLimitField.getText()));
|
||||
((ExtendedConfiguration) configuration).saveTo(CONFIG_FILE);
|
||||
changesMade = false;
|
||||
reloadAlertShown = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.nwapw.abacus.fx
|
||||
|
||||
import com.moandjiezana.toml.Toml
|
||||
import com.moandjiezana.toml.TomlWriter
|
||||
import org.nwapw.abacus.config.Configuration
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Additional settings for user interface.
|
||||
*
|
||||
* ExtendedConfiguration is used to add other settings
|
||||
* that aren't built into Abacus core, but are necessary
|
||||
* for the fx module.
|
||||
*
|
||||
* @property computationDelay the delay before which the computation stops.
|
||||
* @param implementation the number implementation, same as [Configuration.numberImplementation]
|
||||
* @param disabledPlugins the list of plugins that should be disabled, same as [Configuration.disabledPlugins]
|
||||
*/
|
||||
class ExtendedConfiguration(var computationDelay: Double = 0.0,
|
||||
implementation: String = "<default>",
|
||||
disabledPlugins: Array<String> = emptyArray())
|
||||
: Configuration(implementation, disabledPlugins) {
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The default TOML.
|
||||
*/
|
||||
val DEFAULT_TOML_STRING = """
|
||||
computationDelay=0.0
|
||||
implementation="naive"
|
||||
disabledPlugins=[]
|
||||
"""
|
||||
/**
|
||||
* A reader with the default TOML data.
|
||||
*/
|
||||
val DEFAULT_TOML_READER = Toml().read(DEFAULT_TOML_STRING)
|
||||
/**
|
||||
* A writer used to writing the configuration to disk.
|
||||
*/
|
||||
val DEFAULT_TOML_WRITER = TomlWriter()
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new configuration from a file on disk.
|
||||
* @param tomlFile the file from disk to load.
|
||||
*/
|
||||
constructor(tomlFile: File) : this() {
|
||||
copyFrom(Toml(DEFAULT_TOML_READER).read(tomlFile).to(ExtendedConfiguration::class.java))
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies data from another configuration into this one.
|
||||
* @param config the configuration to copy from.
|
||||
*/
|
||||
fun copyFrom(config: ExtendedConfiguration) {
|
||||
computationDelay = config.computationDelay
|
||||
numberImplementation = config.numberImplementation
|
||||
disabledPlugins.clear()
|
||||
disabledPlugins.addAll(config.disabledPlugins)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this configuration to a file.
|
||||
* @param file the file to save to.
|
||||
*/
|
||||
fun saveTo(file: File) {
|
||||
DEFAULT_TOML_WRITER.write(this, file)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user