Enable disposing environment when code execution is done
This commit is contained in:
@@ -19,6 +19,7 @@ interface Environment {
|
||||
fun printScopes()
|
||||
fun setVariable(name: String, value: Value)
|
||||
fun getVariable(name: String): Value
|
||||
fun dispose()
|
||||
|
||||
fun getRootModule(): Module
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import org.pf4j.ExtensionPoint
|
||||
|
||||
abstract class ModuleProvider(val path: String) : ExtensionPoint {
|
||||
open fun onModuleLoad(environment: Environment) {}
|
||||
open fun beforeModuleDisposal(environment: Environment) {}
|
||||
open fun dependencies(): List<String> = emptyList()
|
||||
abstract fun provideModule(interpreter: Interpreter): Module
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package io.smnp.ext
|
||||
|
||||
import io.smnp.environment.Environment
|
||||
|
||||
interface ModuleRegistry {
|
||||
fun requestModuleProviderForPath(path: String): ModuleProvider?
|
||||
fun registeredModules(): List<String>
|
||||
fun disposeModules(environment: Environment)
|
||||
}
|
||||
@@ -18,6 +18,9 @@ class CallStack {
|
||||
|
||||
fun top() = items.top()
|
||||
|
||||
val size: Int
|
||||
get() = items.size
|
||||
|
||||
fun pretty() {
|
||||
items.asReversed().forEachIndexed { index, item -> println("[${items.size - index - 1}] $item") }
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ data class CallStackFrame(
|
||||
|
||||
fun popScope() = scopes.pop()
|
||||
|
||||
val scopesCount: Int
|
||||
get() = scopes.size
|
||||
|
||||
fun setVariable(name: String, value: Value) {
|
||||
val scope = scopes.lastOrNull { it.containsKey(name) } ?: scopes.top()
|
||||
scope[name] = value
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.smnp.callable.function.Function
|
||||
import io.smnp.callable.method.Method
|
||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.error.EvaluationException
|
||||
import io.smnp.ext.DefaultModuleRegistry
|
||||
import io.smnp.ext.DefaultModuleRegistry.requestModuleProviderForPath
|
||||
import io.smnp.ext.ModuleProvider
|
||||
import io.smnp.interpreter.LanguageModuleInterpreter
|
||||
@@ -15,18 +16,27 @@ class DefaultEnvironment : Environment {
|
||||
private val rootModule = Module.create("<root>")
|
||||
private val loadedModules = mutableListOf<String>()
|
||||
private val callStack = CallStack()
|
||||
var disposed = false
|
||||
private set
|
||||
|
||||
init {
|
||||
callStack.push(rootModule, "<entrypoint>", emptyList())
|
||||
}
|
||||
|
||||
override fun loadModule(path: String) {
|
||||
assertDisposal()
|
||||
requestModuleProviderForPath(path).let {
|
||||
loadModule(it)
|
||||
loadDependencies(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertDisposal() {
|
||||
if(disposed) {
|
||||
throw RuntimeException("Disposed environment is immutable and cannot be further used as environment for evaluating program")
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadModule(moduleProvider: ModuleProvider, consumer: (ModuleProvider) -> Unit = {}) {
|
||||
if (!loadedModules.contains(moduleProvider.path)) {
|
||||
rootModule.addSubmodule(moduleProvider.provideModule(LanguageModuleInterpreter()))
|
||||
@@ -49,13 +59,14 @@ class DefaultEnvironment : Environment {
|
||||
}
|
||||
|
||||
override fun invokeFunction(name: String, arguments: List<Value>): Value {
|
||||
assertDisposal()
|
||||
val foundFunctions = rootModule.findFunction(name)
|
||||
if (foundFunctions.isEmpty()) {
|
||||
throw EvaluationException("No function found with name of '$name'")
|
||||
}
|
||||
|
||||
if (foundFunctions.size > 1) {
|
||||
throw EvaluationException("Found ${foundFunctions.size} functions with name of $name: [${foundFunctions.map { it.module.canonicalName }.joinToString()}]")
|
||||
throw EvaluationException("Found ${foundFunctions.size} functions with name of $name: [${foundFunctions.joinToString { it.module.canonicalName }}]")
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +80,7 @@ class DefaultEnvironment : Environment {
|
||||
}
|
||||
|
||||
override fun invokeMethod(obj: Value, name: String, arguments: List<Value>): Value {
|
||||
assertDisposal()
|
||||
val foundMethods = rootModule.findMethod(obj, name)
|
||||
if (foundMethods.isEmpty()) {
|
||||
throw EvaluationException(
|
||||
@@ -100,26 +112,51 @@ class DefaultEnvironment : Environment {
|
||||
}
|
||||
|
||||
override fun stackTrace(): String {
|
||||
return callStack.stackTrace();
|
||||
return callStack.stackTrace()
|
||||
}
|
||||
|
||||
override fun defineFunction(function: Function) {
|
||||
assertDisposal()
|
||||
rootModule.addFunction(function)
|
||||
}
|
||||
|
||||
override fun defineMethod(method: Method) {
|
||||
assertDisposal()
|
||||
rootModule.addMethod(method)
|
||||
}
|
||||
|
||||
override fun pushScope(scope: MutableMap<String, Value>) = callStack.top().pushScope(scope)
|
||||
override fun pushScope(scope: MutableMap<String, Value>) {
|
||||
assertDisposal()
|
||||
callStack.top().pushScope(scope)
|
||||
}
|
||||
|
||||
override fun popScope() = callStack.top().popScope()
|
||||
override fun popScope(): MutableMap<String, Value>? {
|
||||
assertDisposal()
|
||||
return callStack.top().popScope()
|
||||
}
|
||||
|
||||
override fun printScopes() = callStack.top().prettyScope()
|
||||
|
||||
override fun setVariable(name: String, value: Value) = callStack.top().setVariable(name, value)
|
||||
override fun setVariable(name: String, value: Value) {
|
||||
assertDisposal()
|
||||
callStack.top().setVariable(name, value)
|
||||
}
|
||||
|
||||
override fun getVariable(name: String) = callStack.top().getVariable(name)
|
||||
|
||||
override fun dispose() {
|
||||
assertDisposal()
|
||||
disposed = true
|
||||
DefaultModuleRegistry.disposeModules(this)
|
||||
}
|
||||
|
||||
override fun getRootModule() = rootModule
|
||||
|
||||
override fun toString(): String {
|
||||
var string = "Default environment ${if(disposed) "[DISPOSED]" else ""}\n"
|
||||
string += "- loaded modules: ${loadedModules.size}\n"
|
||||
string += "- call stack frames: ${callStack.size}\n"
|
||||
string += "- top frame scopes: ${callStack.top().scopesCount}\n"
|
||||
return string
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ class RootEvaluator : Evaluator() {
|
||||
FunctionDefinitionEvaluator(),
|
||||
ExtendEvaluator(),
|
||||
DefaultEvaluator()
|
||||
), "correct statement")).evaluate(node, environment)
|
||||
), "correct statement")).evaluate(node, environment).let {
|
||||
environment.dispose()
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.smnp.ext
|
||||
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.ModuleException
|
||||
import org.pf4j.DefaultPluginManager
|
||||
|
||||
object DefaultModuleRegistry : ModuleRegistry {
|
||||
private val pluginManager = DefaultPluginManager()
|
||||
private val modules = mutableMapOf<String, ModuleProvider>()
|
||||
init {
|
||||
val pluginManager = DefaultPluginManager()
|
||||
pluginManager.loadPlugins()
|
||||
pluginManager.startPlugins()
|
||||
|
||||
@@ -20,4 +21,9 @@ object DefaultModuleRegistry : ModuleRegistry {
|
||||
}
|
||||
|
||||
override fun registeredModules() = modules.keys.toList()
|
||||
|
||||
override fun disposeModules(environment: Environment) {
|
||||
modules.forEach { (_, module) -> module.beforeModuleDisposal(environment) }
|
||||
pluginManager.stopPlugins()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user