Add support for function/methods invocation on Environment + create call stack model
This commit is contained in:
@@ -2,10 +2,21 @@ package io.smnp.callable.function
|
|||||||
|
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
import io.smnp.error.FunctionInvocationException
|
import io.smnp.error.FunctionInvocationException
|
||||||
|
import io.smnp.error.RuntimeException
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
import io.smnp.type.module.Module
|
||||||
|
|
||||||
abstract class Function(val name: String) {
|
abstract class Function(val name: String) {
|
||||||
private var definitions: List<FunctionDefinition> = mutableListOf()
|
private var definitions: List<FunctionDefinition> = 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)
|
abstract fun define(new: FunctionDefinitionTool)
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,22 @@ package io.smnp.callable.method
|
|||||||
|
|
||||||
import io.smnp.environment.Environment
|
import io.smnp.environment.Environment
|
||||||
import io.smnp.error.MethodInvocationException
|
import io.smnp.error.MethodInvocationException
|
||||||
|
import io.smnp.error.RuntimeException
|
||||||
import io.smnp.type.matcher.Matcher
|
import io.smnp.type.matcher.Matcher
|
||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
import io.smnp.type.module.Module
|
||||||
|
|
||||||
abstract class Method(val typeMatcher: Matcher, val name: String) {
|
abstract class Method(val typeMatcher: Matcher, val name: String) {
|
||||||
private var definitions: List<MethodDefinition> = mutableListOf()
|
private var definitions: List<MethodDefinition> = 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)
|
abstract fun define(new: MethodDefinitionTool)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import io.smnp.type.enumeration.DataType
|
|||||||
import io.smnp.type.model.Value
|
import io.smnp.type.model.Value
|
||||||
|
|
||||||
object ActualSignatureFormatter {
|
object ActualSignatureFormatter {
|
||||||
|
fun format(value: Value): String {
|
||||||
|
return format(arrayOf(value), false)
|
||||||
|
}
|
||||||
|
|
||||||
fun format(arguments: Array<out Value>, parentheses: Boolean = true): String {
|
fun format(arguments: Array<out Value>, parentheses: Boolean = true): String {
|
||||||
val output = mutableListOf<String>()
|
val output = mutableListOf<String>()
|
||||||
for(argument in arguments) {
|
for(argument in arguments) {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package io.smnp.environment
|
package io.smnp.environment
|
||||||
|
|
||||||
import io.smnp.ext.ModuleRegistry
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.type.module.Module
|
|
||||||
|
|
||||||
interface Environment {
|
interface Environment {
|
||||||
fun loadModule(path: String)
|
fun loadModule(path: String)
|
||||||
fun printModules(printContent: Boolean)
|
fun printModules(printContent: Boolean)
|
||||||
|
fun invokeFunction(name: String, arguments: List<Value>): Value
|
||||||
|
fun invokeMethod(obj: Value, name: String, arguments: List<Value>): Value
|
||||||
|
fun printCallStack()
|
||||||
}
|
}
|
||||||
30
api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt
Normal file
30
api/src/main/kotlin/io/smnp/runtime/model/CallStack.kt
Normal file
@@ -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<CallStackItem>()
|
||||||
|
|
||||||
|
fun push(module: Module, name: String, arguments: List<Value>) {
|
||||||
|
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") }
|
||||||
|
}
|
||||||
|
}
|
||||||
13
api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt
Normal file
13
api/src/main/kotlin/io/smnp/runtime/model/CallStackItem.kt
Normal file
@@ -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<Value>
|
||||||
|
) {
|
||||||
|
override fun toString() = "${module.canonicalName}::$name${ActualSignatureFormatter.format(arguments.toTypedArray())}"
|
||||||
|
}
|
||||||
@@ -17,6 +17,8 @@ class Module(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
children.forEach { addSubmodule(it) }
|
children.forEach { addSubmodule(it) }
|
||||||
|
functions.forEach { it.module = this }
|
||||||
|
methods.forEach { it.module = this }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addSubmodule(module: Module) {
|
fun addSubmodule(module: Module) {
|
||||||
@@ -35,19 +37,19 @@ class Module(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val canonicalName: String
|
val canonicalName: String
|
||||||
get() {
|
get() {
|
||||||
val modules = mutableListOf(this)
|
val modules = mutableListOf(this)
|
||||||
var par = parent
|
var par = parent
|
||||||
while(par != null) {
|
while (par != null) {
|
||||||
modules.add(par)
|
modules.add(par)
|
||||||
par = par.parent
|
par = par.parent
|
||||||
|
}
|
||||||
|
|
||||||
|
return modules.reversed().joinToString(".") { it.name }
|
||||||
}
|
}
|
||||||
|
|
||||||
return modules.reversed().joinToString(".") { it.name }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun merge(module: Module): Module {
|
fun merge(module: Module): Module {
|
||||||
if(name != module.name) {
|
if (name != module.name) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +75,12 @@ class Module(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun findMethod(value: Value, name: String): List<Method> {
|
fun findMethod(value: Value, name: String): List<Method> {
|
||||||
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
|
override fun toString() = name
|
||||||
@@ -84,7 +91,7 @@ class Module(
|
|||||||
|
|
||||||
println(newPrefix + (if (first) "" else if (newLast) "└─ " else "├─ ") + name)
|
println(newPrefix + (if (first) "" else if (newLast) "└─ " else "├─ ") + name)
|
||||||
newPrefix += if (newLast) " " else "│ "
|
newPrefix += if (newLast) " " else "│ "
|
||||||
if(printContent) {
|
if (printContent) {
|
||||||
val contentPrefix = newPrefix + if (children.isNotEmpty()) "|" else ""
|
val contentPrefix = newPrefix + if (children.isNotEmpty()) "|" else ""
|
||||||
for ((index, function) in functions.withIndex()) {
|
for ((index, function) in functions.withIndex()) {
|
||||||
println(contentPrefix + (if (index == functions.size - 1 && methods.isEmpty()) "└ " else "├ ") + "${function.name}()")
|
println(contentPrefix + (if (index == functions.size - 1 && methods.isEmpty()) "└ " else "├ ") + "${function.name}()")
|
||||||
@@ -109,20 +116,22 @@ class Module(
|
|||||||
children: List<Module> = emptyList()
|
children: List<Module> = emptyList()
|
||||||
): Module {
|
): Module {
|
||||||
val modules = path.split(".")
|
val modules = path.split(".")
|
||||||
if(modules.isEmpty()) {
|
if (modules.isEmpty()) {
|
||||||
return Module(path, functions, methods, children)
|
return Module(path, functions, methods, children)
|
||||||
}
|
}
|
||||||
|
|
||||||
val root = modules.map { Module(it) }.reduceRight { m, n -> m.addSubmodule(n); m }
|
val root = modules.map { Module(it) }.reduceRight { m, n -> m.addSubmodule(n); m }
|
||||||
|
|
||||||
var youngest = root
|
var youngest = root
|
||||||
while(youngest.children.isNotEmpty()) {
|
while (youngest.children.isNotEmpty()) {
|
||||||
youngest = youngest.children[0]
|
youngest = youngest.children[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
youngest.functions.addAll(functions)
|
youngest.functions.addAll(functions)
|
||||||
youngest.methods.addAll(methods)
|
youngest.methods.addAll(methods)
|
||||||
youngest.children.addAll(children)
|
youngest.children.addAll(children)
|
||||||
|
youngest.functions.forEach { it.module = youngest }
|
||||||
|
youngest.methods.forEach { it.module = youngest }
|
||||||
|
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,19 @@
|
|||||||
package io.smnp.environment
|
package io.smnp.environment
|
||||||
|
|
||||||
|
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||||
import io.smnp.ext.DefaultModuleRegistry
|
import io.smnp.ext.DefaultModuleRegistry
|
||||||
|
import io.smnp.runtime.model.CallStack
|
||||||
|
import io.smnp.type.model.Value
|
||||||
import io.smnp.type.module.Module
|
import io.smnp.type.module.Module
|
||||||
|
|
||||||
class DefaultEnvironment : Environment {
|
class DefaultEnvironment : Environment {
|
||||||
private val rootModule = Module("<root>")
|
private val rootModule = Module("<root>")
|
||||||
private val loadedModules = mutableListOf<String>()
|
private val loadedModules = mutableListOf<String>()
|
||||||
|
private val callStack = CallStack()
|
||||||
|
|
||||||
|
init {
|
||||||
|
callStack.push(rootModule, "<entrypoint>", emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
override fun loadModule(path: String) {
|
override fun loadModule(path: String) {
|
||||||
DefaultModuleRegistry.requestModulesForPath(path).forEach {
|
DefaultModuleRegistry.requestModulesForPath(path).forEach {
|
||||||
@@ -17,4 +25,46 @@ class DefaultEnvironment : Environment {
|
|||||||
override fun printModules(printContent: Boolean) {
|
override fun printModules(printContent: Boolean) {
|
||||||
rootModule.pretty(printContent)
|
rootModule.pretty(printContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun invokeFunction(name: String, arguments: List<Value>): 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>): 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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package io.smnp.ext.lang
|
package io.smnp.ext.lang
|
||||||
|
|
||||||
import io.smnp.callable.function.Function
|
|
||||||
import io.smnp.ext.ModuleDefinition
|
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.ListAccessMethod
|
||||||
import io.smnp.ext.lang.method.MapAccessMethod
|
import io.smnp.ext.lang.method.MapAccessMethod
|
||||||
import org.pf4j.Extension
|
import org.pf4j.Extension
|
||||||
@@ -10,7 +10,7 @@ import org.pf4j.Extension
|
|||||||
class LangModule : ModuleDefinition {
|
class LangModule : ModuleDefinition {
|
||||||
override fun modulePath() = "smnp.lang"
|
override fun modulePath() = "smnp.lang"
|
||||||
|
|
||||||
override fun functions() = emptyList<Function>()
|
override fun functions() = listOf(DebugFunction())
|
||||||
|
|
||||||
override fun methods() = listOf(ListAccessMethod(), MapAccessMethod())
|
override fun methods() = listOf(ListAccessMethod(), MapAccessMethod())
|
||||||
}
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user