From 9b79d6ef7df0a525292d68df879b08c7c1213754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Fri, 13 Mar 2020 12:16:33 +0100 Subject: [PATCH] Add support for providing modules written in SMNP language (LanguageModuleProvider extension class) --- .../io/smnp/ext/LanguageModuleProvider.kt | 20 +++++ .../kotlin/io/smnp/ext/ModuleDefinition.kt | 14 --- .../main/kotlin/io/smnp/ext/ModuleProvider.kt | 10 +++ .../main/kotlin/io/smnp/ext/ModuleRegistry.kt | 2 +- .../io/smnp/ext/NativeModuleProvider.kt | 13 +++ .../kotlin/io/smnp/interpreter/Interpreter.kt | 8 ++ .../io/smnp/environment/DefaultEnvironment.kt | 16 ++-- .../io/smnp/evaluation/evaluator/Evaluator.kt | 87 +++++++++++-------- .../evaluation/evaluator/RootEvaluator.kt | 14 +-- .../io/smnp/ext/DefaultModuleRegistry.kt | 6 +- .../{Interpreter.kt => DefaultInterpreter.kt} | 4 +- .../interpreter/LanguageModuleInterpreter.kt | 47 ++++++++++ .../main/kotlin/io/smnp/ext/io/IoModule.kt | 4 +- .../kotlin/io/smnp/ext/lang/LangModule.kt | 4 +- 14 files changed, 171 insertions(+), 78 deletions(-) create mode 100644 api/src/main/kotlin/io/smnp/ext/LanguageModuleProvider.kt delete mode 100644 api/src/main/kotlin/io/smnp/ext/ModuleDefinition.kt create mode 100644 api/src/main/kotlin/io/smnp/ext/ModuleProvider.kt create mode 100644 api/src/main/kotlin/io/smnp/ext/NativeModuleProvider.kt create mode 100644 api/src/main/kotlin/io/smnp/interpreter/Interpreter.kt rename app/src/main/kotlin/io/smnp/interpreter/{Interpreter.kt => DefaultInterpreter.kt} (91%) create mode 100644 app/src/main/kotlin/io/smnp/interpreter/LanguageModuleInterpreter.kt diff --git a/api/src/main/kotlin/io/smnp/ext/LanguageModuleProvider.kt b/api/src/main/kotlin/io/smnp/ext/LanguageModuleProvider.kt new file mode 100644 index 0000000..44127ce --- /dev/null +++ b/api/src/main/kotlin/io/smnp/ext/LanguageModuleProvider.kt @@ -0,0 +1,20 @@ +package io.smnp.ext + +import io.smnp.interpreter.Interpreter +import io.smnp.type.module.Module + +abstract class LanguageModuleProvider(path: String) : ModuleProvider(path) { + open fun files() = listOf("main.mus") + + override fun provideModule(interpreter: Interpreter): Module { + val module = Module.create(path) + interpreter.updateRootModule(module) + + files() + .map { javaClass.classLoader.getResource(it) } + .map { it.readText() } + .forEach { interpreter.run(it) } + + return module + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/ext/ModuleDefinition.kt b/api/src/main/kotlin/io/smnp/ext/ModuleDefinition.kt deleted file mode 100644 index 9ef891a..0000000 --- a/api/src/main/kotlin/io/smnp/ext/ModuleDefinition.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.smnp.ext - -import io.smnp.callable.function.Function -import io.smnp.callable.method.Method -import io.smnp.type.module.Module -import org.pf4j.ExtensionPoint - -abstract class ModuleDefinition(val path: String) : ExtensionPoint { - open fun functions(): List = emptyList() - open fun methods(): List = emptyList() - open fun dependencies(): List = emptyList() - - fun module(): Module = Module.create(path, functions(), methods()) -} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/ext/ModuleProvider.kt b/api/src/main/kotlin/io/smnp/ext/ModuleProvider.kt new file mode 100644 index 0000000..5d043db --- /dev/null +++ b/api/src/main/kotlin/io/smnp/ext/ModuleProvider.kt @@ -0,0 +1,10 @@ +package io.smnp.ext + +import io.smnp.interpreter.Interpreter +import io.smnp.type.module.Module +import org.pf4j.ExtensionPoint + +abstract class ModuleProvider(val path: String) : ExtensionPoint { + open fun dependencies(): List = emptyList() + abstract fun provideModule(interpreter: Interpreter): Module +} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/ext/ModuleRegistry.kt b/api/src/main/kotlin/io/smnp/ext/ModuleRegistry.kt index d14fb0c..4d44473 100644 --- a/api/src/main/kotlin/io/smnp/ext/ModuleRegistry.kt +++ b/api/src/main/kotlin/io/smnp/ext/ModuleRegistry.kt @@ -1,6 +1,6 @@ package io.smnp.ext interface ModuleRegistry { - fun requestModulesForPath(path: String): ModuleDefinition? + fun requestModulesForPath(path: String): ModuleProvider? fun registeredModules(): List } \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/ext/NativeModuleProvider.kt b/api/src/main/kotlin/io/smnp/ext/NativeModuleProvider.kt new file mode 100644 index 0000000..e50be5a --- /dev/null +++ b/api/src/main/kotlin/io/smnp/ext/NativeModuleProvider.kt @@ -0,0 +1,13 @@ +package io.smnp.ext + +import io.smnp.callable.function.Function +import io.smnp.callable.method.Method +import io.smnp.interpreter.Interpreter +import io.smnp.type.module.Module + +abstract class NativeModuleProvider(path: String) : ModuleProvider(path) { + open fun functions(): List = emptyList() + open fun methods(): List = emptyList() + + final override fun provideModule(interpreter: Interpreter) = Module.create(path, functions(), methods()) +} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/interpreter/Interpreter.kt b/api/src/main/kotlin/io/smnp/interpreter/Interpreter.kt new file mode 100644 index 0000000..bc7cd9e --- /dev/null +++ b/api/src/main/kotlin/io/smnp/interpreter/Interpreter.kt @@ -0,0 +1,8 @@ +package io.smnp.interpreter + +import io.smnp.type.module.Module + +interface Interpreter { + fun run(code: String) + fun updateRootModule(newRootModule: Module): Unit = throw RuntimeException("Replacing root module is not supported in this Interpreter implementation") +} \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt b/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt index bd17911..e4f2d86 100644 --- a/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt +++ b/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt @@ -4,13 +4,13 @@ import io.smnp.callable.function.Function import io.smnp.callable.method.Method import io.smnp.callable.signature.ActualSignatureFormatter.format import io.smnp.ext.DefaultModuleRegistry.requestModulesForPath -import io.smnp.ext.ModuleDefinition +import io.smnp.ext.ModuleProvider +import io.smnp.interpreter.LanguageModuleInterpreter import io.smnp.runtime.model.CallStack import io.smnp.type.model.Value import io.smnp.type.module.Module -class DefaultEnvironment : Environment { - private val rootModule = Module("") +class DefaultEnvironment(private val rootModule: Module = Module.create("")) : Environment { private val loadedModules = mutableListOf() private val callStack = CallStack() @@ -25,13 +25,13 @@ class DefaultEnvironment : Environment { } } - private fun loadModule(moduleDefinition: ModuleDefinition) { - rootModule.addSubmodule(moduleDefinition.module()) - loadedModules.add(moduleDefinition.path) + private fun loadModule(moduleProvider: ModuleProvider) { + rootModule.addSubmodule(moduleProvider.provideModule(LanguageModuleInterpreter())) + loadedModules.add(moduleProvider.path) } - private fun loadDependencies(moduleDefinition: ModuleDefinition) { - moduleDefinition.dependencies().forEach { dependencyPath -> + private fun loadDependencies(moduleProvider: ModuleProvider) { + moduleProvider.dependencies().forEach { dependencyPath -> if (!loadedModules.contains(dependencyPath)) { val dependency = requestModulesForPath(dependencyPath) loadModule(dependency) diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/Evaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/Evaluator.kt index 5e55004..9d231b3 100644 --- a/app/src/main/kotlin/io/smnp/evaluation/evaluator/Evaluator.kt +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/Evaluator.kt @@ -8,49 +8,66 @@ import io.smnp.evaluation.model.enumeration.EvaluationResult import kotlin.reflect.KClass abstract class Evaluator { - fun evaluate(node: Node, environment: Environment): EvaluatorOutput { - if(supportedNodes().any { it.isInstance(node) }) { - return tryToEvaluate(node, environment) - } + fun evaluate(node: Node, environment: Environment): EvaluatorOutput { + if (supportedNodes().any { it.isInstance(node) }) { + return tryToEvaluate(node, environment) + } - return EvaluatorOutput.fail() - } + return EvaluatorOutput.fail() + } - protected abstract fun supportedNodes(): List> - protected abstract fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput + protected abstract fun supportedNodes(): List> + protected abstract fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput - companion object { - fun oneOf(vararg evaluators: Evaluator): Evaluator { - return object : Evaluator() { - override fun supportedNodes() = listOf(Node::class) + companion object { + fun oneOf(vararg evaluators: Evaluator): Evaluator { + return object : Evaluator() { + override fun supportedNodes() = listOf(Node::class) - override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { - for(evaluator in evaluators) { - val output = evaluator.evaluate(node, environment) - if(output.result != EvaluationResult.FAILED) { - return output - } - } + override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { + for (evaluator in evaluators) { + val output = evaluator.evaluate(node, environment) + if (output.result != EvaluationResult.FAILED) { + return output + } + } - return EvaluatorOutput.fail() - } + return EvaluatorOutput.fail() } - } + } + } - fun assert(evaluator: Evaluator, expected: String): Evaluator { - return object : Evaluator() { - override fun supportedNodes() = listOf(Node::class) + fun repeat(evaluator: Evaluator): Evaluator { + return object : Evaluator() { + override fun supportedNodes() = listOf(Node::class) - override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { - val output = evaluator.evaluate(node, environment) + override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { + for (child in node.children) { + val output = evaluator.evaluate(child, environment) + if (output.result == EvaluationResult.FAILED) { + return EvaluatorOutput.fail() + } + } - if(output.result == EvaluationResult.FAILED) { - throw EvaluationException("Expected $expected", node.position) - } - - return output - } + return EvaluatorOutput.ok() } - } - } + } + } + + fun assert(evaluator: Evaluator, expected: String): Evaluator { + return object : Evaluator() { + override fun supportedNodes() = listOf(Node::class) + + override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { + val output = evaluator.evaluate(node, environment) + + if (output.result == EvaluationResult.FAILED) { + throw EvaluationException("Expected $expected", node.position) + } + + return output + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt b/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt index c8a92e5..51f5245 100644 --- a/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt +++ b/app/src/main/kotlin/io/smnp/evaluation/evaluator/RootEvaluator.kt @@ -4,27 +4,17 @@ import io.smnp.dsl.ast.model.node.Node import io.smnp.dsl.ast.model.node.RootNode import io.smnp.environment.Environment import io.smnp.evaluation.model.entity.EvaluatorOutput -import io.smnp.evaluation.model.enumeration.EvaluationResult class RootEvaluator : Evaluator() { override fun supportedNodes() = listOf(RootNode::class) override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { - val evaluator = assert(oneOf( + return repeat(assert(oneOf( ImportEvaluator(), ExpressionEvaluator(), FunctionDefinitionEvaluator(), ExtendEvaluator(), DefaultEvaluator() - ), "correct statement") - - for(child in node.children) { - val output = evaluator.evaluate(child, environment) - if(output.result == EvaluationResult.FAILED) { - return EvaluatorOutput.fail() - } - } - - return EvaluatorOutput.ok() + ), "correct statement")).evaluate(node, environment) } } \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/ext/DefaultModuleRegistry.kt b/app/src/main/kotlin/io/smnp/ext/DefaultModuleRegistry.kt index d120dbc..d17f031 100644 --- a/app/src/main/kotlin/io/smnp/ext/DefaultModuleRegistry.kt +++ b/app/src/main/kotlin/io/smnp/ext/DefaultModuleRegistry.kt @@ -3,18 +3,18 @@ package io.smnp.ext import org.pf4j.DefaultPluginManager object DefaultModuleRegistry : ModuleRegistry { - private val modules = mutableMapOf() + private val modules = mutableMapOf() init { val pluginManager = DefaultPluginManager() pluginManager.loadPlugins() pluginManager.startPlugins() - pluginManager.getExtensions(ModuleDefinition::class.java).forEach { + pluginManager.getExtensions(ModuleProvider::class.java).forEach { modules[it.path] = it } } - override fun requestModulesForPath(path: String): ModuleDefinition { + override fun requestModulesForPath(path: String): ModuleProvider { return modules[path] ?: throw RuntimeException("Module $path not found") } diff --git a/app/src/main/kotlin/io/smnp/interpreter/Interpreter.kt b/app/src/main/kotlin/io/smnp/interpreter/DefaultInterpreter.kt similarity index 91% rename from app/src/main/kotlin/io/smnp/interpreter/Interpreter.kt rename to app/src/main/kotlin/io/smnp/interpreter/DefaultInterpreter.kt index 53deb54..827427b 100644 --- a/app/src/main/kotlin/io/smnp/interpreter/Interpreter.kt +++ b/app/src/main/kotlin/io/smnp/interpreter/DefaultInterpreter.kt @@ -8,7 +8,7 @@ import io.smnp.evaluation.evaluator.RootEvaluator import io.smnp.evaluation.model.enumeration.EvaluationResult import java.io.File -class Interpreter { +class DefaultInterpreter : Interpreter { private val tokenizer = DefaultTokenizer() private val parser = RootParser() private val evaluator = RootEvaluator() @@ -46,4 +46,6 @@ class Interpreter { val lines = file.readLines() run(lines, printTokens, printAst, dryRun) } + + override fun run(code: String) = run(code, printTokens = false, printAst = false, dryRun = false) } \ No newline at end of file diff --git a/app/src/main/kotlin/io/smnp/interpreter/LanguageModuleInterpreter.kt b/app/src/main/kotlin/io/smnp/interpreter/LanguageModuleInterpreter.kt new file mode 100644 index 0000000..90c835b --- /dev/null +++ b/app/src/main/kotlin/io/smnp/interpreter/LanguageModuleInterpreter.kt @@ -0,0 +1,47 @@ +package io.smnp.interpreter + +import io.smnp.dsl.ast.parser.RootParser +import io.smnp.dsl.token.tokenizer.DefaultTokenizer +import io.smnp.environment.DefaultEnvironment +import io.smnp.environment.Environment +import io.smnp.evaluation.evaluator.Evaluator +import io.smnp.evaluation.evaluator.ExtendEvaluator +import io.smnp.evaluation.evaluator.FunctionDefinitionEvaluator +import io.smnp.evaluation.model.enumeration.EvaluationResult +import io.smnp.type.module.Module + +class LanguageModuleInterpreter : Interpreter { + private var rootModule = Module.create("") + private val tokenizer = DefaultTokenizer() + private val parser = RootParser() + private val evaluator = Evaluator.repeat( + Evaluator.assert(Evaluator.oneOf( + FunctionDefinitionEvaluator(), + ExtendEvaluator() + ), "function definition or extend statement") + ) + + override fun updateRootModule(newRootModule: Module) { + rootModule = newRootModule + } + + override fun run(code: String) { + val lines = code.split("\n") + val tokens = tokenizer.tokenize(lines) + val ast = parser.parse(tokens) + + val environment = createEnvironment() + val result = evaluator.evaluate(ast.node, environment) + + if (result.result == EvaluationResult.FAILED) { + throw RuntimeException("Evaluation failed") + } + } + + private fun createEnvironment(): Environment { + val environment = DefaultEnvironment(rootModule) + environment.loadModule("smnp.lang") + + return environment + } +} \ No newline at end of file diff --git a/modules/io/src/main/kotlin/io/smnp/ext/io/IoModule.kt b/modules/io/src/main/kotlin/io/smnp/ext/io/IoModule.kt index a42410b..9ccf414 100644 --- a/modules/io/src/main/kotlin/io/smnp/ext/io/IoModule.kt +++ b/modules/io/src/main/kotlin/io/smnp/ext/io/IoModule.kt @@ -1,10 +1,10 @@ package io.smnp.ext.io -import io.smnp.ext.ModuleDefinition +import io.smnp.ext.NativeModuleProvider import io.smnp.ext.io.function.PrintlnFunction import org.pf4j.Extension @Extension -class IoModule : ModuleDefinition("smnp.io") { +class IoModule : NativeModuleProvider("smnp.io") { override fun functions() = listOf(PrintlnFunction()) } \ No newline at end of file diff --git a/modules/lang/src/main/kotlin/io/smnp/ext/lang/LangModule.kt b/modules/lang/src/main/kotlin/io/smnp/ext/lang/LangModule.kt index aac849f..17883a8 100644 --- a/modules/lang/src/main/kotlin/io/smnp/ext/lang/LangModule.kt +++ b/modules/lang/src/main/kotlin/io/smnp/ext/lang/LangModule.kt @@ -1,13 +1,13 @@ package io.smnp.ext.lang -import io.smnp.ext.ModuleDefinition +import io.smnp.ext.NativeModuleProvider import io.smnp.ext.lang.function.DebugFunction import io.smnp.ext.lang.method.ListAccessMethod import io.smnp.ext.lang.method.MapAccessMethod import org.pf4j.Extension @Extension -class LangModule : ModuleDefinition("smnp.lang") { +class LangModule : NativeModuleProvider("smnp.lang") { override fun functions() = listOf(DebugFunction()) override fun methods() = listOf(ListAccessMethod(), MapAccessMethod()) } \ No newline at end of file