diff --git a/core/src/main/java/org/nwapw/abacus/context/ChainAccumulateDelegate.kt b/core/src/main/java/org/nwapw/abacus/context/ChainAccumulateDelegate.kt new file mode 100644 index 0000000..e2f1017 --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/context/ChainAccumulateDelegate.kt @@ -0,0 +1,26 @@ +package org.nwapw.abacus.context + +import kotlin.reflect.KProperty + +/** + * A delegate to accumulate a collection of elements in a [ReductionContext] hierarchy. + * + * ChainAccumulateDelegate is similar to the [ChainSearchDelegate], however, it operates only on collections. + * Instead of returning the most recent collection, it merges them into a [Set]. + * + * @param T the type of element in the collection. + * @property valueGetter the getter used to access the collection from the context. + */ +class ChainAccumulateDelegate(private val valueGetter: ReductionContext.() -> Collection) { + + operator fun getValue(selfRef: Any, property: KProperty<*>): Set { + val set = mutableSetOf() + var currentRef: ReductionContext = selfRef as? ReductionContext ?: return set + while(true) { + set.addAll(currentRef.valueGetter()) + currentRef = currentRef.parent ?: break + } + return set + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/nwapw/abacus/context/ChainSearchDelegate.kt b/core/src/main/java/org/nwapw/abacus/context/ChainSearchDelegate.kt new file mode 100644 index 0000000..f69725f --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/context/ChainSearchDelegate.kt @@ -0,0 +1,29 @@ +package org.nwapw.abacus.context + +import kotlin.reflect.KProperty + +/** + * A delegate to search a hierarchy made up of [ReductionContext]. + * + * ChainSearchDelegate is a variable delegate written specifically for use in [ReductionContext], because + * of its hierarchical structure. Variables not found in the current context are searched + * for in its parent, which continues recursively until the context being examined has no parent. + * This class assists that logic, which is commonly re-used with different variable types, by calling + * [valueGetter] on the current context, then its parent, etc. + * + * @param V the type of the property to search recursively. + * @property valueGetter the getter lambda to access the value from the context. + */ +class ChainSearchDelegate(private val valueGetter: ReductionContext.() -> V?) { + + operator fun getValue(selfRef: Any, property: KProperty<*>): V? { + var currentRef = selfRef as? ReductionContext ?: return null + var returnedValue = currentRef.valueGetter() + while (returnedValue == null) { + currentRef = currentRef.parent ?: break + returnedValue = currentRef.valueGetter() + } + return returnedValue + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/nwapw/abacus/context/MutableReductionContext.kt b/core/src/main/java/org/nwapw/abacus/context/MutableReductionContext.kt new file mode 100644 index 0000000..fa96c55 --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/context/MutableReductionContext.kt @@ -0,0 +1,69 @@ +package org.nwapw.abacus.context + +import org.nwapw.abacus.number.NumberInterface +import org.nwapw.abacus.plugin.NumberImplementation +import org.nwapw.abacus.tree.Reducer +import org.nwapw.abacus.tree.TreeNode + +/** + * A reduction context that is mutable. + * @param parent the parent of this context. + * @param numberImplementation the number implementation used in this context. + * @param reducer the reducer used in this context + */ +class MutableReductionContext(parent: ReductionContext? = null, + numberImplementation: NumberImplementation? = null, + reducer: Reducer? = null) : + ReductionContext(parent, numberImplementation, reducer) { + + override var numberImplementation: NumberImplementation? = super.numberImplementation + override var reducer: Reducer? = super.reducer + + /** + * Writes data stored in the [other] context over data stored in this one. + * @param other the context from which to copy data. + */ + fun apply(other: ReductionContext) { + if(other.numberImplementation != null) numberImplementation = other.numberImplementation + if(other.reducer != null) reducer = other.reducer + for(name in other.variables) { + setVariable(name, other.getVariable(name) ?: continue) + } + for(name in other.definitions) { + setDefinition(name, other.getDefinition(name) ?: continue) + } + } + + /** + * Sets a variable to a certain [value]. + * @param name the name of the variable. + * @param value the value of the variable. + */ + fun setVariable(name: String, value: NumberInterface) { + variableMap[name] = value + } + + /** + * Set a definition to a certain [value]. + * @param name the name of the definition. + * @param value the value of the definition. + */ + fun setDefinition(name: String, value: TreeNode) { + definitionMap[name] = value + } + + /** + * Clears the variables defined in this context. + */ + fun clearVariables(){ + variableMap.clear() + } + + /** + * Clears the definitions defined in this context. + */ + fun clearDefinitions(){ + definitionMap.clear() + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/nwapw/abacus/context/ReductionContext.kt b/core/src/main/java/org/nwapw/abacus/context/ReductionContext.kt new file mode 100644 index 0000000..957e067 --- /dev/null +++ b/core/src/main/java/org/nwapw/abacus/context/ReductionContext.kt @@ -0,0 +1,84 @@ +package org.nwapw.abacus.context + +import org.nwapw.abacus.number.NumberInterface +import org.nwapw.abacus.plugin.NumberImplementation +import org.nwapw.abacus.tree.Reducer +import org.nwapw.abacus.tree.TreeNode + +/** + * A context for the reduction of a [org.nwapw.abacus.tree.TreeNode] into a number. + * + * The reduction context is used to carry important state information captured at the beginning + * of the reduction of an expression, such as the variables and the implementation in use. + * + * @property parent the parent of this context. + * @property numberImplementation the implementation for numbers of this context. + * @property reducer the reducer used by this context. + */ +open class ReductionContext(val parent: ReductionContext? = null, + open val numberImplementation: NumberImplementation? = null, + open val reducer: Reducer? = null) { + + /** + * The map of variables in this context. + */ + protected val variableMap = mutableMapOf() + /** + * The map of definitions in this context. + */ + protected val definitionMap = mutableMapOf() + + /** + * The set of all variable names defined in this context. + */ + val variables: Set + get() = variableMap.keys + + /** + * The set of all definition names defined in this context. + */ + val definitions: Set + get() = definitionMap.keys + + /** + * The implementation inherited from this context's parent. + */ + val inheritedNumberImplementation: NumberImplementation? + by ChainSearchDelegate { numberImplementation} + + /** + * The reducer inherited from this context's parent. + */ + val inheritedReducer: Reducer? + by ChainSearchDelegate { reducer } + + /** + * The set of all variables in this context and its parents. + */ + val inheritedVariables: Set by ChainAccumulateDelegate { variables } + + /** + * The set of all definition in this context and its parents. + */ + val inheritedDefinitions: Set by ChainAccumulateDelegate { definitions } + + /** + * Create a new child instance of this context that is mutable. + * @return the new child instance. + */ + fun mutableSubInstance(): MutableReductionContext = MutableReductionContext(this) + + /** + * Gets a variable stored in this context. + */ + fun getVariable(name: String): NumberInterface? { + return variableMap[name] ?: parent?.getVariable(name) + } + /** + * Gets the definition stored in this context. + */ + fun getDefinition(name: String): TreeNode? { + return definitionMap[name] ?: parent?.getDefinition(name) + } + +}