110 lines
3.5 KiB
Kotlin
110 lines
3.5 KiB
Kotlin
package function
|
|
|
|
import error.LispException
|
|
import function.builtin.Eval.Companion.eval
|
|
import sexpression.Cons
|
|
import sexpression.Nil
|
|
import sexpression.SExpression
|
|
import sexpression.Symbol
|
|
import table.ExecutionContext
|
|
import table.SymbolTable
|
|
|
|
open class UserDefinedFunction(private val name: String, lambdaList: Cons, private val body: Cons) : LispFunction() {
|
|
|
|
val lambdaExpression = Cons(Symbol(name), Cons(lambdaList, body))
|
|
private val functionScope = ExecutionContext.scope
|
|
private var formalParameters = mutableListOf<String>()
|
|
private var argumentValidator = ArgumentValidator(name)
|
|
private var keywordRestParameter = ""
|
|
private var isKeywordRestPresent = false
|
|
|
|
init {
|
|
createFormalParameters(lambdaList)
|
|
setupArgumentValidator()
|
|
}
|
|
|
|
private fun createFormalParameters(lambdaList: Cons) = lambdaList.forEach {
|
|
val parameter = it.first.toString()
|
|
|
|
when {
|
|
isKeywordRestPresent -> setupKeywordRestParameter(parameter, it)
|
|
KEYWORD_REST == parameter -> isKeywordRestPresent = true
|
|
else -> formalParameters.add(parameter)
|
|
}
|
|
}
|
|
|
|
private fun setupKeywordRestParameter(parameter: String, lambdaList: Cons) {
|
|
if (keywordRestParameter.isNotBlank())
|
|
throw IllegalKeywordRestPositionException(name, lambdaList)
|
|
|
|
keywordRestParameter = parameter
|
|
}
|
|
|
|
private fun setupArgumentValidator() {
|
|
if (isKeywordRestPresent)
|
|
argumentValidator.setMinimumNumberOfArguments(formalParameters.size)
|
|
else
|
|
argumentValidator.setExactNumberOfArguments(formalParameters.size)
|
|
}
|
|
|
|
override fun call(argumentList: Cons): SExpression {
|
|
ExecutionContext.pushFunctionCall(this)
|
|
val result = callTailRecursive(argumentList)
|
|
ExecutionContext.popFunctionCall()
|
|
|
|
return result
|
|
}
|
|
|
|
private tailrec fun callTailRecursive(argumentList: Cons): SExpression {
|
|
argumentValidator.validate(argumentList)
|
|
|
|
val result = evaluateInFunctionScope(argumentList)
|
|
|
|
if (ExecutionContext.isRecur) {
|
|
ExecutionContext.clearRecur()
|
|
return callTailRecursive(result as Cons)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
private fun evaluateInFunctionScope(argumentList: Cons): SExpression {
|
|
val callingScope = ExecutionContext.scope
|
|
val executionScope = bindParameterValuesToFunctionScope(argumentList)
|
|
|
|
ExecutionContext.scope = executionScope
|
|
val lastEvaluation = evaluateBody()
|
|
ExecutionContext.scope = callingScope
|
|
|
|
return lastEvaluation
|
|
}
|
|
|
|
private fun bindParameterValuesToFunctionScope(argumentList: Cons): SymbolTable {
|
|
var arguments = argumentList
|
|
val executionScope = SymbolTable(functionScope)
|
|
|
|
for (parameter in formalParameters) {
|
|
val currentArg = arguments.first
|
|
executionScope[parameter] = currentArg
|
|
arguments = arguments.rest as Cons
|
|
}
|
|
|
|
if (isKeywordRestPresent)
|
|
executionScope[keywordRestParameter] = arguments
|
|
|
|
return executionScope
|
|
}
|
|
|
|
private fun evaluateBody() = body.fold(Nil as SExpression) { _, cons -> eval(cons.first) }
|
|
|
|
class IllegalKeywordRestPositionException(functionName: String, parameters: Cons) : LispException() {
|
|
|
|
override val message =
|
|
"unexpected parameters following '$KEYWORD_REST' in definition of $functionName: $parameters"
|
|
}
|
|
|
|
companion object {
|
|
private const val KEYWORD_REST = "&REST"
|
|
}
|
|
}
|