transcendental-lisp/src/main/kotlin/function/UserDefinedFunction.kt
Mike Cifelli 347857fbb5 Restore main function
- Remove MessageFormat usages in kotlin where possible
 - Code cleanup
2018-10-20 10:09:53 -04:00

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