Enable disposing environment when code execution is done

This commit is contained in:
2020-03-14 12:52:04 +01:00
parent c7f251cbce
commit cc2d69e259
8 changed files with 64 additions and 7 deletions

View File

@@ -19,6 +19,7 @@ interface Environment {
fun printScopes()
fun setVariable(name: String, value: Value)
fun getVariable(name: String): Value
fun dispose()
fun getRootModule(): Module
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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") }
}

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -15,6 +15,9 @@ class RootEvaluator : Evaluator() {
FunctionDefinitionEvaluator(),
ExtendEvaluator(),
DefaultEvaluator()
), "correct statement")).evaluate(node, environment)
), "correct statement")).evaluate(node, environment).let {
environment.dispose()
it
}
}
}

View File

@@ -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()
}
}