diff --git a/core/src/main/java/org/nwapw/abacus/tree/NumberReducer.java b/core/src/main/java/org/nwapw/abacus/tree/NumberReducer.java deleted file mode 100644 index fcea885..0000000 --- a/core/src/main/java/org/nwapw/abacus/tree/NumberReducer.java +++ /dev/null @@ -1,93 +0,0 @@ -package org.nwapw.abacus.tree; - -import org.nwapw.abacus.Abacus; -import org.nwapw.abacus.function.NumberFunction; -import org.nwapw.abacus.function.NumberOperator; -import org.nwapw.abacus.function.TreeValueFunction; -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. - * This is not always guaranteed to work. - */ -public class NumberReducer implements Reducer { - - /** - * The plugin manager from which to draw the functions. - */ - private Abacus abacus; - - /** - * Creates a new number reducer. - * - * @param abacus the calculator instance. - */ - public NumberReducer(Abacus abacus) { - this.abacus = abacus; - } - - @Override - public NumberInterface reduceNode(TreeNode node, Object... children) { - PromotionManager manager = abacus.getPromotionManager(); - 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]; - NumberInterface right = (NumberInterface) children[1]; - NumberOperator operator = abacus.getPluginManager().operatorFor(((BinaryNode) node).getOperation()); - PromotionResult result = manager.promote(left, right); - if (result == null) return null; - return operator.apply(result.getPromotedTo(), result.getItems()); - } else if (node instanceof NumberUnaryNode) { - NumberInterface child = (NumberInterface) children[0]; - NumberOperator operator = abacus.getPluginManager().operatorFor(((UnaryNode) node).getOperation()); - return operator.apply(abacus.getPluginManager().interfaceImplementationFor(child.getClass()), child); - } else if (node instanceof FunctionNode) { - NumberInterface[] convertedChildren = new NumberInterface[children.length]; - for (int i = 0; i < convertedChildren.length; i++) { - convertedChildren[i] = (NumberInterface) children[i]; - } - NumberFunction function = abacus.getPluginManager().functionFor(((FunctionNode) node).getCallTo()); - if (function == null) return null; - PromotionResult result = manager.promote(convertedChildren); - if (result == null) return null; - return function.apply(result.getPromotedTo(), result.getItems()); - } else if (node instanceof TreeValueFunctionNode) { - CallNode callNode = (CallNode) node; - TreeNode[] realChildren = new TreeNode[callNode.getChildren().size()]; - for (int i = 0; i < realChildren.length; i++) { - realChildren[i] = callNode.getChildren().get(i); - } - TreeValueFunction function = - abacus.getPluginManager().treeValueFunctionFor(callNode.getCallTo()); - if (function == null) return null; - return function.applyWithReducer(abacus.getNumberImplementation(), this, realChildren); - } else if (node instanceof TreeValueBinaryNode) { - BinaryNode binaryNode = (BinaryNode) node; - TreeValueOperator operator = abacus.getPluginManager() - .treeValueOperatorFor(binaryNode.getOperation()); - if (operator == null) return null; - return operator.applyWithReducer(abacus.getNumberImplementation(), this, binaryNode.getLeft(), binaryNode.getRight()); - } else if (node instanceof TreeValueUnaryNode) { - UnaryNode unaryNode = (UnaryNode) node; - TreeValueOperator operator = abacus.getPluginManager() - .treeValueOperatorFor(unaryNode.getOperation()); - if (operator == null) return null; - return operator.applyWithReducer(abacus.getNumberImplementation(), this, unaryNode.getApplyTo()); - } - return null; - } - -} diff --git a/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt b/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt index 23e6f8e..c191133 100644 --- a/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt +++ b/core/src/main/kotlin/org/nwapw/abacus/Abacus.kt @@ -1,6 +1,8 @@ package org.nwapw.abacus import org.nwapw.abacus.config.Configuration +import org.nwapw.abacus.context.MutableReductionContext +import org.nwapw.abacus.context.ReductionContext import org.nwapw.abacus.number.NumberInterface import org.nwapw.abacus.number.PromotionManager import org.nwapw.abacus.parsing.LexerTokenizer @@ -36,10 +38,6 @@ class Abacus(val configuration: Configuration) { * 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. */ @@ -52,16 +50,16 @@ class Abacus(val configuration: Configuration) { * The database of variable definitions. */ val variableDatabase = VariableDatabase(this) + /** - * The number implementation used by default. + * The hidden, mutable implementation of the context. */ - val numberImplementation: NumberImplementation - get() { - val selectedImplementation = - pluginManager.numberImplementationFor(configuration.numberImplementation) - if (selectedImplementation != null) return selectedImplementation - return StandardPlugin.IMPLEMENTATION_NAIVE - } + private val mutableContext = MutableReductionContext(numberImplementation = StandardPlugin.IMPLEMENTATION_NAIVE) + /** + * The base context from which calculations are started. + */ + val context: ReductionContext + get() = mutableContext init { pluginManager.addListener(tokenizer) @@ -85,6 +83,7 @@ class Abacus(val configuration: Configuration) { * @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) + fun evaluateTree(tree: TreeNode): NumberInterface? = + tree.reduce(NumberReducer(this, context.mutableSubInstance())) } \ No newline at end of file diff --git a/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt new file mode 100644 index 0000000..4fc658e --- /dev/null +++ b/core/src/main/kotlin/org/nwapw/abacus/tree/NumberReducer.kt @@ -0,0 +1,57 @@ +package org.nwapw.abacus.tree + +import org.nwapw.abacus.Abacus +import org.nwapw.abacus.context.MutableReductionContext +import org.nwapw.abacus.function.NumberFunction +import org.nwapw.abacus.number.NumberInterface + +class NumberReducer(val abacus: Abacus, val context: MutableReductionContext) : Reducer { + + override fun reduceNode(treeNode: TreeNode, vararg children: Any): NumberInterface? { + val promotionManager = abacus.promotionManager + return when(treeNode){ + is NumberNode -> { + context.inheritedNumberImplementation?.instanceForString(treeNode.number) + } + is VariableNode -> { + val variable = context.getVariable(treeNode.variable) + if(variable != null) return variable + val definition = context.getDefinition(treeNode.variable) + if(definition != null) return definition.reduce(this) + null + } + is NumberUnaryNode -> { + val child = children[0] as NumberInterface + abacus.pluginManager.operatorFor(treeNode.operation) + .apply(abacus.pluginManager.interfaceImplementationFor(child.javaClass), child) + } + is NumberBinaryNode -> { + val left = children[0] as NumberInterface + val right = children[1] as NumberInterface + val promotionResult = promotionManager.promote(left, right) ?: return null + abacus.pluginManager.operatorFor(treeNode.operation).apply(promotionResult.promotedTo, *promotionResult.items) + } + is FunctionNode -> { + val promotionResult = promotionManager + .promote(*children.map { it as NumberInterface }.toTypedArray()) ?: return null + abacus.pluginManager.functionFor(treeNode.callTo).apply(promotionResult.promotedTo, *promotionResult.items) + } + is TreeValueUnaryNode -> { + abacus.pluginManager.treeValueOperatorFor(treeNode.operation) + .applyWithReducer(context.inheritedNumberImplementation!!, this, children[0] as TreeNode) + } + is TreeValueBinaryNode -> { + abacus.pluginManager.treeValueOperatorFor(treeNode.operation) + .applyWithReducer(context.inheritedNumberImplementation!!, this, + children[0] as TreeNode, children[1] as TreeNode) + } + is TreeValueFunctionNode -> { + abacus.pluginManager.treeValueFunctionFor(treeNode.callTo) + .applyWithReducer(context.inheritedNumberImplementation!!, this, + *children.map { it as TreeNode }.toTypedArray()) + } + else -> null + } + } + +} \ No newline at end of file