From d29ef612456cbe57ca4f4766d325847cbb503cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Tue, 10 Mar 2020 21:57:21 +0100 Subject: [PATCH] Add support for function/methods invocation on Environment + create call stack model --- .../io/smnp/callable/function/Function.kt | 11 ++++ .../kotlin/io/smnp/callable/method/Method.kt | 11 ++++ .../signature/ActualSignatureFormatter.kt | 4 ++ .../kotlin/io/smnp/environment/Environment.kt | 6 ++- .../kotlin/io/smnp/runtime/model/CallStack.kt | 30 +++++++++++ .../io/smnp/runtime/model/CallStackItem.kt | 13 +++++ .../main/kotlin/io/smnp/type/module/Module.kt | 37 ++++++++------ .../io/smnp/environment/DefaultEnvironment.kt | 50 +++++++++++++++++++ .../kotlin/io/smnp/ext/lang/LangModule.kt | 4 +- .../smnp/ext/lang/function/DebugFunction.kt | 15 ++++++ 10 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt create mode 100644 api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt create mode 100644 modules/lang/src/main/kotlin/io/smnp/ext/lang/function/DebugFunction.kt diff --git a/api/src/main/kotlin/io/smnp/callable/function/Function.kt b/api/src/main/kotlin/io/smnp/callable/function/Function.kt index bdffb34..caf65b8 100644 --- a/api/src/main/kotlin/io/smnp/callable/function/Function.kt +++ b/api/src/main/kotlin/io/smnp/callable/function/Function.kt @@ -2,10 +2,21 @@ package io.smnp.callable.function import io.smnp.environment.Environment import io.smnp.error.FunctionInvocationException +import io.smnp.error.RuntimeException import io.smnp.type.model.Value +import io.smnp.type.module.Module abstract class Function(val name: String) { private var definitions: List = mutableListOf() + private var _module: Module? = null + var module: Module + get() = _module ?: throw RuntimeException("Method has not set module yet") + set(value) { + if (_module != null) { + throw RuntimeException("Module of method is already set to ${module.canonicalName}") + } + _module = value + } abstract fun define(new: FunctionDefinitionTool) diff --git a/api/src/main/kotlin/io/smnp/callable/method/Method.kt b/api/src/main/kotlin/io/smnp/callable/method/Method.kt index 0e0c2e1..e92be87 100644 --- a/api/src/main/kotlin/io/smnp/callable/method/Method.kt +++ b/api/src/main/kotlin/io/smnp/callable/method/Method.kt @@ -2,11 +2,22 @@ package io.smnp.callable.method import io.smnp.environment.Environment import io.smnp.error.MethodInvocationException +import io.smnp.error.RuntimeException import io.smnp.type.matcher.Matcher import io.smnp.type.model.Value +import io.smnp.type.module.Module abstract class Method(val typeMatcher: Matcher, val name: String) { private var definitions: List = mutableListOf() + private var _module: Module? = null + var module: Module + get() = _module ?: throw RuntimeException("Method has not set module yet") + set(value) { + if (_module != null) { + throw RuntimeException("Module of method is already set to ${module.canonicalName}") + } + _module = value + } abstract fun define(new: MethodDefinitionTool) diff --git a/api/src/main/kotlin/io/smnp/callable/signature/ActualSignatureFormatter.kt b/api/src/main/kotlin/io/smnp/callable/signature/ActualSignatureFormatter.kt index 4817dd2..37daad6 100644 --- a/api/src/main/kotlin/io/smnp/callable/signature/ActualSignatureFormatter.kt +++ b/api/src/main/kotlin/io/smnp/callable/signature/ActualSignatureFormatter.kt @@ -4,6 +4,10 @@ import io.smnp.type.enumeration.DataType import io.smnp.type.model.Value object ActualSignatureFormatter { + fun format(value: Value): String { + return format(arrayOf(value), false) + } + fun format(arguments: Array, parentheses: Boolean = true): String { val output = mutableListOf() for(argument in arguments) { diff --git a/api/src/main/kotlin/io/smnp/environment/Environment.kt b/api/src/main/kotlin/io/smnp/environment/Environment.kt index 29b32b6..b8d96cd 100644 --- a/api/src/main/kotlin/io/smnp/environment/Environment.kt +++ b/api/src/main/kotlin/io/smnp/environment/Environment.kt @@ -1,9 +1,11 @@ package io.smnp.environment -import io.smnp.ext.ModuleRegistry -import io.smnp.type.module.Module +import io.smnp.type.model.Value interface Environment { fun loadModule(path: String) fun printModules(printContent: Boolean) + fun invokeFunction(name: String, arguments: List): Value + fun invokeMethod(obj: Value, name: String, arguments: List): Value + fun printCallStack() } \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt b/api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt new file mode 100644 index 0000000..90c9799 --- /dev/null +++ b/api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt @@ -0,0 +1,30 @@ +package io.smnp.runtime.model + +import io.smnp.type.model.Value +import io.smnp.type.module.Module + +class CallStack { + private val items = mutableListOf() + + fun push(module: Module, name: String, arguments: List) { + items.add(CallStackItem(module, name, arguments)) + } + + fun push(item: CallStackItem) { + items.add(item) + } + + fun pop(): CallStackItem? { + if(items.isEmpty()) { + return null + } + + val last = items.last() + items.removeAt(items.size-1) + return last + } + + fun pretty() { + items.asReversed().forEachIndexed { index, item -> println("[${items.size - index - 1}] $item") } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt b/api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt new file mode 100644 index 0000000..96ea18c --- /dev/null +++ b/api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt @@ -0,0 +1,13 @@ +package io.smnp.runtime.model + +import io.smnp.callable.signature.ActualSignatureFormatter +import io.smnp.type.model.Value +import io.smnp.type.module.Module + +data class CallStackItem( + val module: Module, + val name: String, + val arguments: List +) { + override fun toString() = "${module.canonicalName}::$name${ActualSignatureFormatter.format(arguments.toTypedArray())}" +} \ No newline at end of file diff --git a/api/src/main/kotlin/io/smnp/type/module/Module.kt b/api/src/main/kotlin/io/smnp/type/module/Module.kt index 3be6ce9..425deab 100644 --- a/api/src/main/kotlin/io/smnp/type/module/Module.kt +++ b/api/src/main/kotlin/io/smnp/type/module/Module.kt @@ -17,6 +17,8 @@ class Module( init { children.forEach { addSubmodule(it) } + functions.forEach { it.module = this } + methods.forEach { it.module = this } } fun addSubmodule(module: Module) { @@ -35,19 +37,19 @@ class Module( } val canonicalName: String - get() { - val modules = mutableListOf(this) - var par = parent - while(par != null) { - modules.add(par) - par = par.parent + get() { + val modules = mutableListOf(this) + var par = parent + while (par != null) { + modules.add(par) + par = par.parent + } + + return modules.reversed().joinToString(".") { it.name } } - return modules.reversed().joinToString(".") { it.name } - } - fun merge(module: Module): Module { - if(name != module.name) { + if (name != module.name) { return this } @@ -73,7 +75,12 @@ class Module( } fun findMethod(value: Value, name: String): List { - return methods.filter { it.name == name && it.verifyType(value) } + children.flatMap { it.findMethod(value, name) } + return methods.filter { it.name == name && it.verifyType(value) } + children.flatMap { + it.findMethod( + value, + name + ) + } } override fun toString() = name @@ -84,7 +91,7 @@ class Module( println(newPrefix + (if (first) "" else if (newLast) "└─ " else "├─ ") + name) newPrefix += if (newLast) " " else "│ " - if(printContent) { + if (printContent) { val contentPrefix = newPrefix + if (children.isNotEmpty()) "|" else "" for ((index, function) in functions.withIndex()) { println(contentPrefix + (if (index == functions.size - 1 && methods.isEmpty()) "└ " else "├ ") + "${function.name}()") @@ -109,20 +116,22 @@ class Module( children: List = emptyList() ): Module { val modules = path.split(".") - if(modules.isEmpty()) { + if (modules.isEmpty()) { return Module(path, functions, methods, children) } val root = modules.map { Module(it) }.reduceRight { m, n -> m.addSubmodule(n); m } var youngest = root - while(youngest.children.isNotEmpty()) { + while (youngest.children.isNotEmpty()) { youngest = youngest.children[0] } youngest.functions.addAll(functions) youngest.methods.addAll(methods) youngest.children.addAll(children) + youngest.functions.forEach { it.module = youngest } + youngest.methods.forEach { it.module = youngest } return root } diff --git a/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt b/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt index 1cde872..95da8c7 100644 --- a/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt +++ b/app/src/main/kotlin/io/smnp/environment/DefaultEnvironment.kt @@ -1,11 +1,19 @@ package io.smnp.environment +import io.smnp.callable.signature.ActualSignatureFormatter.format import io.smnp.ext.DefaultModuleRegistry +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("") private val loadedModules = mutableListOf() + private val callStack = CallStack() + + init { + callStack.push(rootModule, "", emptyList()) + } override fun loadModule(path: String) { DefaultModuleRegistry.requestModulesForPath(path).forEach { @@ -17,4 +25,46 @@ class DefaultEnvironment : Environment { override fun printModules(printContent: Boolean) { rootModule.pretty(printContent) } + + override fun invokeFunction(name: String, arguments: List): Value { + val foundFunctions = rootModule.findFunction(name) + if(foundFunctions.isEmpty()) { + throw RuntimeException("No function found with name of '$name'") + } + + if(foundFunctions.size > 1) { + throw RuntimeException("Found ${foundFunctions.size} functions with name of $foundFunctions") + } + + val function = foundFunctions[0] + + callStack.push(function.module, function.name, arguments) + val value = function.call(this, *arguments.toTypedArray()) + callStack.pop() + + return value + } + + override fun invokeMethod(obj: Value, name: String, arguments: List): Value { + val foundMethods = rootModule.findMethod(obj, name) + if(foundMethods.isEmpty()) { + throw RuntimeException("No method found with name of '$name' for ${format(obj)}") + } + + if(foundMethods.size > 1) { + throw RuntimeException("Found ${foundMethods.size} methods with name of '$foundMethods' for ${format(obj)}") + } + + val method = foundMethods[0] + + callStack.push(method.module, "${method.typeMatcher}.${method.name}", arguments) + val value = method.call(this, obj, *arguments.toTypedArray()) + callStack.pop() + + return value + } + + override fun printCallStack() { + callStack.pretty() + } } \ 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 b38b78b..5026952 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,7 +1,7 @@ package io.smnp.ext.lang -import io.smnp.callable.function.Function import io.smnp.ext.ModuleDefinition +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 @@ -10,7 +10,7 @@ import org.pf4j.Extension class LangModule : ModuleDefinition { override fun modulePath() = "smnp.lang" - override fun functions() = emptyList() + override fun functions() = listOf(DebugFunction()) override fun methods() = listOf(ListAccessMethod(), MapAccessMethod()) } \ No newline at end of file diff --git a/modules/lang/src/main/kotlin/io/smnp/ext/lang/function/DebugFunction.kt b/modules/lang/src/main/kotlin/io/smnp/ext/lang/function/DebugFunction.kt new file mode 100644 index 0000000..ac4cbd2 --- /dev/null +++ b/modules/lang/src/main/kotlin/io/smnp/ext/lang/function/DebugFunction.kt @@ -0,0 +1,15 @@ +package io.smnp.ext.lang.function + +import io.smnp.callable.function.Function +import io.smnp.callable.function.FunctionDefinitionTool +import io.smnp.callable.signature.Signature.Companion.simple +import io.smnp.type.model.Value + +class DebugFunction : Function("debug") { + override fun define(new: FunctionDefinitionTool) { + new function simple() define { env, _ -> + env.printCallStack() + Value.void() + } + } +} \ No newline at end of file