Experimental: add support for function/method overloading
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package io.smnp.callable.function
|
||||
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.FunctionInvocationException
|
||||
import io.smnp.type.model.Value
|
||||
import io.smnp.type.module.Module
|
||||
|
||||
@@ -20,15 +19,17 @@ abstract class Function(val name: String) {
|
||||
definitions = FunctionDefinitionTool().apply { define(this) }.definitions
|
||||
}
|
||||
|
||||
fun matches(arguments: List<Value>) = matchArguments(arguments) != null
|
||||
|
||||
private fun matchArguments(arguments: List<Value>) = definitions
|
||||
.map { it to it.signature.parse(arguments.toList()) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
|
||||
fun call(environment: Environment, vararg arguments: Value): Value {
|
||||
val (definition, args) = definitions
|
||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
?: throw FunctionInvocationException(this, arguments, environment)
|
||||
val (definition, args) = matchArguments(arguments.toList())!! // todo?
|
||||
|
||||
return definition.body(environment, args.arguments)
|
||||
}
|
||||
|
||||
val signature: String
|
||||
get() = definitions.joinToString("\nor\n") { "$name${it.signature}" }
|
||||
val signature = definitions.map { "$name${it.signature}" }
|
||||
}
|
||||
@@ -1,37 +1,38 @@
|
||||
package io.smnp.callable.method
|
||||
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.MethodInvocationException
|
||||
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<MethodDefinition> = mutableListOf()
|
||||
private var _module: Module? = null
|
||||
var module: Module
|
||||
get() = _module ?: throw RuntimeException("Method has not set module yet")
|
||||
set(value) {
|
||||
_module = value
|
||||
}
|
||||
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) {
|
||||
_module = value
|
||||
}
|
||||
|
||||
abstract fun define(new: MethodDefinitionTool)
|
||||
abstract fun define(new: MethodDefinitionTool)
|
||||
|
||||
init {
|
||||
definitions = MethodDefinitionTool().apply { define(this) }.definitions
|
||||
}
|
||||
init {
|
||||
definitions = MethodDefinitionTool().apply { define(this) }.definitions
|
||||
}
|
||||
|
||||
fun verifyType(type: Value) = typeMatcher.match(type)
|
||||
fun verifyType(type: Value) = typeMatcher.match(type)
|
||||
|
||||
fun call(environment: Environment, obj: Value, vararg arguments: Value): Value {
|
||||
val (definition, args) = definitions
|
||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
?: throw MethodInvocationException(this, obj, arguments, environment)
|
||||
fun matches(obj: Value, arguments: List<Value>) = verifyType(obj) && matchArguments(arguments) != null
|
||||
|
||||
return definition.body(environment, obj, args.arguments)
|
||||
}
|
||||
private fun matchArguments(arguments: List<Value>) = definitions
|
||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
|
||||
val signature: String
|
||||
get() = definitions.joinToString("\nor\n") { "$typeMatcher.$name${it.signature}" }
|
||||
fun call(environment: Environment, obj: Value, vararg arguments: Value): Value {
|
||||
val (definition, args) = matchArguments(arguments.toList())!! // todo?
|
||||
|
||||
return definition.body(environment, obj, args.arguments)
|
||||
}
|
||||
|
||||
val signature = definitions.map { "$typeMatcher.$name${it.signature}" }
|
||||
}
|
||||
@@ -1,15 +1,3 @@
|
||||
package io.smnp.error
|
||||
|
||||
import io.smnp.callable.function.Function
|
||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class FunctionInvocationException(
|
||||
function: Function,
|
||||
passedArguments: Array<out Value>,
|
||||
val environment: Environment
|
||||
) : SmnpException(
|
||||
"Function invocation error",
|
||||
"Invalid signature: ${function.name}${format(passedArguments)}\nAllowed signatures:\n${function.signature}"
|
||||
)
|
||||
class FunctionInvocationException(message: String?) : SmnpException("Function invocation error", message)
|
||||
@@ -1,16 +1,3 @@
|
||||
package io.smnp.error
|
||||
|
||||
import io.smnp.callable.method.Method
|
||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class MethodInvocationException(
|
||||
method: Method,
|
||||
obj: Value,
|
||||
passedArguments: Array<out Value>,
|
||||
val environment: Environment
|
||||
) : SmnpException(
|
||||
"Method invocation error",
|
||||
"Invalid signature: $obj.${method.name}${format(passedArguments)}\nAllowed signatures:\n${method.signature}"
|
||||
)
|
||||
class MethodInvocationException(message: String?) : SmnpException("Method invocation error", message)
|
||||
@@ -3,7 +3,8 @@ package io.smnp.environment
|
||||
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.error.FunctionInvocationException
|
||||
import io.smnp.error.MethodInvocationException
|
||||
import io.smnp.ext.DefaultModuleRegistry
|
||||
import io.smnp.ext.DefaultModuleRegistry.requestModuleProviderForPath
|
||||
import io.smnp.ext.ModuleProvider
|
||||
@@ -62,15 +63,24 @@ class DefaultEnvironment : Environment {
|
||||
assertDisposal()
|
||||
val foundFunctions = rootModule.findFunction(name)
|
||||
if (foundFunctions.isEmpty()) {
|
||||
throw EvaluationException("No function found with name of '$name'")
|
||||
throw FunctionInvocationException("No function found with name of '$name'")
|
||||
}
|
||||
|
||||
if (foundFunctions.size > 1) {
|
||||
throw EvaluationException("Found ${foundFunctions.size} functions with name of $name: [${foundFunctions.joinToString { it.module.canonicalName }}]")
|
||||
|
||||
val matchedFunctions = foundFunctions.filter { it.matches(arguments) }
|
||||
if (matchedFunctions.size > 1) {
|
||||
throw FunctionInvocationException("Found ${matchedFunctions.size} functions with name of $name, that matched provided arguments: [${matchedFunctions.joinToString { it.module.canonicalName }}]")
|
||||
}
|
||||
|
||||
val function = foundFunctions[0]
|
||||
if(matchedFunctions.isEmpty()) {
|
||||
var message = "No function matches following signature:\n $name${format(arguments.toTypedArray())}"
|
||||
if(foundFunctions.isNotEmpty()) {
|
||||
message += "\nDid you mean:\n"
|
||||
message += foundFunctions.flatMap { it.signature }.joinToString("\n") { " $it" }
|
||||
}
|
||||
throw FunctionInvocationException(message)
|
||||
}
|
||||
|
||||
val function = matchedFunctions[0]
|
||||
|
||||
callStack.push(function.module, function.name, arguments)
|
||||
val value = function.call(this, *arguments.toTypedArray())
|
||||
@@ -83,22 +93,28 @@ class DefaultEnvironment : Environment {
|
||||
assertDisposal()
|
||||
val foundMethods = rootModule.findMethod(obj, name)
|
||||
if (foundMethods.isEmpty()) {
|
||||
throw EvaluationException(
|
||||
throw MethodInvocationException(
|
||||
"No method found with name of '$name' for ${format(
|
||||
obj
|
||||
)}"
|
||||
)
|
||||
}
|
||||
|
||||
if (foundMethods.size > 1) {
|
||||
throw EvaluationException(
|
||||
"Found ${foundMethods.size} methods with name of $name for ${format(
|
||||
obj
|
||||
)}: [${foundMethods.map { it.module.canonicalName }.joinToString()}]"
|
||||
)
|
||||
val matchedMethods = foundMethods.filter { it.matches(obj, arguments) }
|
||||
if (matchedMethods.size > 1) {
|
||||
throw MethodInvocationException("Found ${matchedMethods.size} functions with name of $name, that matched provided arguments: [${matchedMethods.joinToString { it.module.canonicalName }}]")
|
||||
}
|
||||
|
||||
val method = foundMethods[0]
|
||||
if(matchedMethods.isEmpty()) {
|
||||
var message = "No method matches following signature:\n ${format(obj)}.$name${format(arguments.toTypedArray())}"
|
||||
if(foundMethods.isNotEmpty()) {
|
||||
message += "\nDid you mean:\n"
|
||||
message += foundMethods.flatMap { it.signature }.joinToString("\n") { " $it" }
|
||||
}
|
||||
throw FunctionInvocationException(message)
|
||||
}
|
||||
|
||||
val method = matchedMethods[0]
|
||||
|
||||
callStack.push(method.module, "${method.typeMatcher}.${method.name}", arguments)
|
||||
val value = method.call(this, obj, *arguments.toTypedArray())
|
||||
|
||||
Reference in New Issue
Block a user