Experimental: add support for function/method overloading

This commit is contained in:
2020-03-14 15:10:17 +01:00
parent ba23545e1b
commit 07fc9f3dd2
5 changed files with 63 additions and 70 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -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())