Convert UserDefinedFunction to kotlin

This commit is contained in:
Mike Cifelli 2018-09-29 08:18:56 -04:00
parent e6e3965485
commit ba6418a977
7 changed files with 125 additions and 186 deletions

View File

@ -12,7 +12,7 @@ import java.io.PipedOutputStream
import java.io.PrintStream import java.io.PrintStream
import java.text.MessageFormat.format import java.text.MessageFormat.format
class LispMain @JvmOverloads constructor(var configuration: TerminalConfiguration = TerminalConfiguration()) { class LispMain constructor(var configuration: TerminalConfiguration = TerminalConfiguration()) {
init { init {
if (configuration.terminal == null) { if (configuration.terminal == null) {

View File

@ -1,169 +0,0 @@
package function;
import error.LispException;
import recursion.TailCall;
import recursion.TailCalls;
import sexpression.Cons;
import sexpression.Nil;
import sexpression.SExpression;
import sexpression.Symbol;
import table.ExecutionContext;
import table.SymbolTable;
import java.util.ArrayList;
import static function.builtin.EVAL.eval;
import static java.text.MessageFormat.format;
import static recursion.TailCalls.done;
public class UserDefinedFunction extends LispFunction {
private static final String KEYWORD_REST = "&REST";
private String name;
private Cons body;
private Cons lambdaExpression;
private ExecutionContext executionContext;
private SymbolTable functionScope;
private ArrayList<String> formalParameters;
private ArgumentValidator argumentValidator;
private String keywordRestParameter;
private boolean isKeywordRestPresent;
public UserDefinedFunction(String name, Cons lambdaList, Cons body) {
this.name = name;
this.body = body;
this.lambdaExpression = new Cons(new Symbol(name), new Cons(lambdaList, body));
this.executionContext = ExecutionContext.INSTANCE;
this.functionScope = executionContext.getScope();
this.keywordRestParameter = null;
this.isKeywordRestPresent = false;
createFormalParameters(lambdaList);
setupArgumentValidator();
}
private void createFormalParameters(Cons lambdaList) {
for (formalParameters = new ArrayList<>(); lambdaList.isCons(); lambdaList = advanceCons(lambdaList)) {
String parameter = lambdaList.getFirst().toString();
if (isKeywordRest(parameter))
lambdaList = extractKeywordRestParameter(lambdaList);
else
formalParameters.add(parameter);
}
}
private Cons advanceCons(Cons cons) {
return (Cons) cons.getRest();
}
private boolean isKeywordRest(String parameter) {
return KEYWORD_REST.equals(parameter);
}
private Cons extractKeywordRestParameter(Cons lambdaList) {
isKeywordRestPresent = true;
lambdaList = advanceCons(lambdaList);
keywordRestParameter = lambdaList.getFirst().toString();
lambdaList = advanceCons(lambdaList);
if (containsMoreParameters(lambdaList))
throw new IllegalKeywordRestPositionException(this.name, lambdaList);
return lambdaList;
}
private boolean containsMoreParameters(Cons lambdaList) {
return lambdaList.isCons();
}
private void setupArgumentValidator() {
argumentValidator = new ArgumentValidator(this.name);
if (isKeywordRestPresent)
argumentValidator.setMinimumNumberOfArguments(this.formalParameters.size());
else
argumentValidator.setExactNumberOfArguments(this.formalParameters.size());
}
@Override
public SExpression call(Cons argumentList) {
executionContext.pushFunctionCall(this);
SExpression result = callTailRecursive(argumentList).invoke();
executionContext.popFunctionCall();
return result;
}
private TailCall<SExpression> callTailRecursive(Cons argumentList) {
argumentValidator.validate(argumentList);
SExpression result = evaluateInFunctionScope(argumentList);
if (executionContext.isRecur()) {
executionContext.clearRecur();
return TailCalls.tailCall(() -> callTailRecursive((Cons) result));
}
return done(result);
}
private SExpression evaluateInFunctionScope(Cons argumentList) {
SymbolTable callingScope = executionContext.getScope();
SymbolTable executionScope = bindParameterValuesToFunctionScope(argumentList);
executionContext.setScope(executionScope);
SExpression lastEvaluation = evaluateBody();
executionContext.setScope(callingScope);
return lastEvaluation;
}
private SymbolTable bindParameterValuesToFunctionScope(Cons argumentList) {
SymbolTable executionScope = new SymbolTable(functionScope);
for (String parameter : formalParameters) {
SExpression currentArg = argumentList.getFirst();
executionScope.set(parameter, currentArg);
argumentList = (Cons) argumentList.getRest();
}
if (isKeywordRestPresent)
executionScope.set(keywordRestParameter, argumentList);
return executionScope;
}
private SExpression evaluateBody() {
SExpression lastEvaluation = Nil.INSTANCE;
for (Cons expression = body; expression.isCons(); expression = (Cons) expression.getRest())
lastEvaluation = eval(expression.getFirst());
return lastEvaluation;
}
public Cons getLambdaExpression() {
return lambdaExpression;
}
public static class IllegalKeywordRestPositionException extends LispException {
private static final long serialVersionUID = 1L;
private String functionName;
private Cons parameters;
public IllegalKeywordRestPositionException(String functionName, Cons parameters) {
this.functionName = functionName;
this.parameters = parameters;
}
@Override
public String getMessage() {
return format("unexpected parameters following ''&rest'' in definition of {0}: {1}",
functionName,
parameters);
}
}
}

View File

@ -0,0 +1,112 @@
package function
import error.LispException
import function.builtin.EVAL.eval
import sexpression.Cons
import sexpression.Nil
import sexpression.SExpression
import sexpression.Symbol
import table.ExecutionContext
import table.SymbolTable
import java.text.MessageFormat.format
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(private val functionName: String,
private val parameters: Cons) : LispException() {
override val message: String by lazy {
format("unexpected parameters following ''&rest'' in definition of {0}: {1}", functionName, parameters)
}
}
companion object {
private const val KEYWORD_REST = "&REST"
}
}

View File

@ -11,10 +11,8 @@ import sexpression.SExpression
@FunctionNames("EXIT") @FunctionNames("EXIT")
class Exit(name: String) : LispFunction() { class Exit(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name) private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setMaximumNumberOfArguments(0)
init {
this.argumentValidator.setMaximumNumberOfArguments(0)
} }
override fun call(argumentList: Cons): SExpression { override fun call(argumentList: Cons): SExpression {

View File

@ -12,11 +12,9 @@ import sexpression.Symbol.Companion.T
@FunctionNames("=") @FunctionNames("=")
class NumericEqual(name: String) : LispFunction() { class NumericEqual(name: String) : LispFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name) private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
setMinimumNumberOfArguments(1)
init { setEveryArgumentExpectedType(LispNumber::class.java)
this.argumentValidator.setMinimumNumberOfArguments(1)
this.argumentValidator.setEveryArgumentExpectedType(LispNumber::class.java)
} }
override fun call(argumentList: Cons): SExpression { override fun call(argumentList: Cons): SExpression {

View File

@ -13,13 +13,13 @@ import sexpression.Symbol
@FunctionNames("LAMBDA", "Λ") @FunctionNames("LAMBDA", "Λ")
class Lambda(name: String) : LispSpecialFunction() { class Lambda(name: String) : LispSpecialFunction() {
private val argumentValidator: ArgumentValidator = ArgumentValidator(name) private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
private val lambdaListValidator: ArgumentValidator = ArgumentValidator("$name|lambda-list|") setFirstArgumentExpectedType(Cons::class.java)
setMinimumNumberOfArguments(1)
}
init { private val lambdaListValidator: ArgumentValidator = ArgumentValidator("$name|lambda-list|").apply {
this.argumentValidator.setFirstArgumentExpectedType(Cons::class.java) setEveryArgumentExpectedType(Symbol::class.java)
this.argumentValidator.setMinimumNumberOfArguments(1)
this.lambdaListValidator.setEveryArgumentExpectedType(Symbol::class.java)
} }
override fun call(argumentList: Cons): LambdaExpression { override fun call(argumentList: Cons): LambdaExpression {

View File

@ -8,7 +8,7 @@ import sexpression.SExpression
import sexpression.Symbol import sexpression.Symbol
import kotlin.collections.Map.Entry import kotlin.collections.Map.Entry
open class SymbolTable @JvmOverloads constructor(open val parent: SymbolTable? = NullSymbolTable) : Iterable<SymbolTable> { open class SymbolTable(open val parent: SymbolTable? = NullSymbolTable) : Iterable<SymbolTable> {
private val table = mutableMapOf<String, SExpression>() private val table = mutableMapOf<String, SExpression>()