Add support for providing modules written in SMNP language (LanguageModuleProvider extension class)

This commit is contained in:
2020-03-13 12:16:33 +01:00
parent 9e4c9d3b11
commit 9b79d6ef7d
14 changed files with 171 additions and 78 deletions

View File

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

View File

@@ -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<Function> = emptyList()
open fun methods(): List<Method> = emptyList()
open fun dependencies(): List<String> = emptyList()
fun module(): Module = Module.create(path, functions(), methods())
}

View File

@@ -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<String> = emptyList()
abstract fun provideModule(interpreter: Interpreter): Module
}

View File

@@ -1,6 +1,6 @@
package io.smnp.ext package io.smnp.ext
interface ModuleRegistry { interface ModuleRegistry {
fun requestModulesForPath(path: String): ModuleDefinition? fun requestModulesForPath(path: String): ModuleProvider?
fun registeredModules(): List<String> fun registeredModules(): List<String>
} }

View File

@@ -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<Function> = emptyList()
open fun methods(): List<Method> = emptyList()
final override fun provideModule(interpreter: Interpreter) = Module.create(path, functions(), methods())
}

View File

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

View File

@@ -4,13 +4,13 @@ import io.smnp.callable.function.Function
import io.smnp.callable.method.Method import io.smnp.callable.method.Method
import io.smnp.callable.signature.ActualSignatureFormatter.format import io.smnp.callable.signature.ActualSignatureFormatter.format
import io.smnp.ext.DefaultModuleRegistry.requestModulesForPath 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.runtime.model.CallStack
import io.smnp.type.model.Value import io.smnp.type.model.Value
import io.smnp.type.module.Module import io.smnp.type.module.Module
class DefaultEnvironment : Environment { class DefaultEnvironment(private val rootModule: Module = Module.create("<root>")) : Environment {
private val rootModule = Module("<root>")
private val loadedModules = mutableListOf<String>() private val loadedModules = mutableListOf<String>()
private val callStack = CallStack() private val callStack = CallStack()
@@ -25,13 +25,13 @@ class DefaultEnvironment : Environment {
} }
} }
private fun loadModule(moduleDefinition: ModuleDefinition) { private fun loadModule(moduleProvider: ModuleProvider) {
rootModule.addSubmodule(moduleDefinition.module()) rootModule.addSubmodule(moduleProvider.provideModule(LanguageModuleInterpreter()))
loadedModules.add(moduleDefinition.path) loadedModules.add(moduleProvider.path)
} }
private fun loadDependencies(moduleDefinition: ModuleDefinition) { private fun loadDependencies(moduleProvider: ModuleProvider) {
moduleDefinition.dependencies().forEach { dependencyPath -> moduleProvider.dependencies().forEach { dependencyPath ->
if (!loadedModules.contains(dependencyPath)) { if (!loadedModules.contains(dependencyPath)) {
val dependency = requestModulesForPath(dependencyPath) val dependency = requestModulesForPath(dependencyPath)
loadModule(dependency) loadModule(dependency)

View File

@@ -9,7 +9,7 @@ import kotlin.reflect.KClass
abstract class Evaluator { abstract class Evaluator {
fun evaluate(node: Node, environment: Environment): EvaluatorOutput { fun evaluate(node: Node, environment: Environment): EvaluatorOutput {
if(supportedNodes().any { it.isInstance(node) }) { if (supportedNodes().any { it.isInstance(node) }) {
return tryToEvaluate(node, environment) return tryToEvaluate(node, environment)
} }
@@ -25,9 +25,9 @@ abstract class Evaluator {
override fun supportedNodes() = listOf(Node::class) override fun supportedNodes() = listOf(Node::class)
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
for(evaluator in evaluators) { for (evaluator in evaluators) {
val output = evaluator.evaluate(node, environment) val output = evaluator.evaluate(node, environment)
if(output.result != EvaluationResult.FAILED) { if (output.result != EvaluationResult.FAILED) {
return output return output
} }
} }
@@ -37,6 +37,23 @@ abstract class Evaluator {
} }
} }
fun repeat(evaluator: Evaluator): Evaluator {
return object : Evaluator() {
override fun supportedNodes() = listOf(Node::class)
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()
}
}
return EvaluatorOutput.ok()
}
}
}
fun assert(evaluator: Evaluator, expected: String): Evaluator { fun assert(evaluator: Evaluator, expected: String): Evaluator {
return object : Evaluator() { return object : Evaluator() {
override fun supportedNodes() = listOf(Node::class) override fun supportedNodes() = listOf(Node::class)
@@ -44,7 +61,7 @@ abstract class Evaluator {
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val output = evaluator.evaluate(node, environment) val output = evaluator.evaluate(node, environment)
if(output.result == EvaluationResult.FAILED) { if (output.result == EvaluationResult.FAILED) {
throw EvaluationException("Expected $expected", node.position) throw EvaluationException("Expected $expected", node.position)
} }

View File

@@ -4,27 +4,17 @@ import io.smnp.dsl.ast.model.node.Node
import io.smnp.dsl.ast.model.node.RootNode import io.smnp.dsl.ast.model.node.RootNode
import io.smnp.environment.Environment import io.smnp.environment.Environment
import io.smnp.evaluation.model.entity.EvaluatorOutput import io.smnp.evaluation.model.entity.EvaluatorOutput
import io.smnp.evaluation.model.enumeration.EvaluationResult
class RootEvaluator : Evaluator() { class RootEvaluator : Evaluator() {
override fun supportedNodes() = listOf(RootNode::class) override fun supportedNodes() = listOf(RootNode::class)
override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput { override fun tryToEvaluate(node: Node, environment: Environment): EvaluatorOutput {
val evaluator = assert(oneOf( return repeat(assert(oneOf(
ImportEvaluator(), ImportEvaluator(),
ExpressionEvaluator(), ExpressionEvaluator(),
FunctionDefinitionEvaluator(), FunctionDefinitionEvaluator(),
ExtendEvaluator(), ExtendEvaluator(),
DefaultEvaluator() DefaultEvaluator()
), "correct statement") ), "correct statement")).evaluate(node, environment)
for(child in node.children) {
val output = evaluator.evaluate(child, environment)
if(output.result == EvaluationResult.FAILED) {
return EvaluatorOutput.fail()
}
}
return EvaluatorOutput.ok()
} }
} }

View File

@@ -3,18 +3,18 @@ package io.smnp.ext
import org.pf4j.DefaultPluginManager import org.pf4j.DefaultPluginManager
object DefaultModuleRegistry : ModuleRegistry { object DefaultModuleRegistry : ModuleRegistry {
private val modules = mutableMapOf<String, ModuleDefinition>() private val modules = mutableMapOf<String, ModuleProvider>()
init { init {
val pluginManager = DefaultPluginManager() val pluginManager = DefaultPluginManager()
pluginManager.loadPlugins() pluginManager.loadPlugins()
pluginManager.startPlugins() pluginManager.startPlugins()
pluginManager.getExtensions(ModuleDefinition::class.java).forEach { pluginManager.getExtensions(ModuleProvider::class.java).forEach {
modules[it.path] = it 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") return modules[path] ?: throw RuntimeException("Module $path not found")
} }

View File

@@ -8,7 +8,7 @@ import io.smnp.evaluation.evaluator.RootEvaluator
import io.smnp.evaluation.model.enumeration.EvaluationResult import io.smnp.evaluation.model.enumeration.EvaluationResult
import java.io.File import java.io.File
class Interpreter { class DefaultInterpreter : Interpreter {
private val tokenizer = DefaultTokenizer() private val tokenizer = DefaultTokenizer()
private val parser = RootParser() private val parser = RootParser()
private val evaluator = RootEvaluator() private val evaluator = RootEvaluator()
@@ -46,4 +46,6 @@ class Interpreter {
val lines = file.readLines() val lines = file.readLines()
run(lines, printTokens, printAst, dryRun) run(lines, printTokens, printAst, dryRun)
} }
override fun run(code: String) = run(code, printTokens = false, printAst = false, dryRun = false)
} }

View File

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

View File

@@ -1,10 +1,10 @@
package io.smnp.ext.io package io.smnp.ext.io
import io.smnp.ext.ModuleDefinition import io.smnp.ext.NativeModuleProvider
import io.smnp.ext.io.function.PrintlnFunction import io.smnp.ext.io.function.PrintlnFunction
import org.pf4j.Extension import org.pf4j.Extension
@Extension @Extension
class IoModule : ModuleDefinition("smnp.io") { class IoModule : NativeModuleProvider("smnp.io") {
override fun functions() = listOf(PrintlnFunction()) override fun functions() = listOf(PrintlnFunction())
} }

View File

@@ -1,13 +1,13 @@
package io.smnp.ext.lang 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.function.DebugFunction
import io.smnp.ext.lang.method.ListAccessMethod import io.smnp.ext.lang.method.ListAccessMethod
import io.smnp.ext.lang.method.MapAccessMethod import io.smnp.ext.lang.method.MapAccessMethod
import org.pf4j.Extension import org.pf4j.Extension
@Extension @Extension
class LangModule : ModuleDefinition("smnp.lang") { class LangModule : NativeModuleProvider("smnp.lang") {
override fun functions() = listOf(DebugFunction()) override fun functions() = listOf(DebugFunction())
override fun methods() = listOf(ListAccessMethod(), MapAccessMethod()) override fun methods() = listOf(ListAccessMethod(), MapAccessMethod())
} }