From ba6418a9777773b5de7f1c218128e53efe5ab851 Mon Sep 17 00:00:00 2001 From: Mike Cifelli Date: Sat, 29 Sep 2018 08:18:56 -0400 Subject: [PATCH] Convert UserDefinedFunction to kotlin --- src/main/kotlin/application/LispMain.kt | 2 +- .../kotlin/function/UserDefinedFunction.java | 169 ------------------ .../kotlin/function/UserDefinedFunction.kt | 112 ++++++++++++ src/main/kotlin/function/builtin/Exit.kt | 6 +- .../builtin/predicate/NumericEqual.kt | 8 +- .../kotlin/function/builtin/special/Lambda.kt | 12 +- src/main/kotlin/table/SymbolTable.kt | 2 +- 7 files changed, 125 insertions(+), 186 deletions(-) delete mode 100644 src/main/kotlin/function/UserDefinedFunction.java create mode 100644 src/main/kotlin/function/UserDefinedFunction.kt diff --git a/src/main/kotlin/application/LispMain.kt b/src/main/kotlin/application/LispMain.kt index 75347a1..f16bdfb 100644 --- a/src/main/kotlin/application/LispMain.kt +++ b/src/main/kotlin/application/LispMain.kt @@ -12,7 +12,7 @@ import java.io.PipedOutputStream import java.io.PrintStream import java.text.MessageFormat.format -class LispMain @JvmOverloads constructor(var configuration: TerminalConfiguration = TerminalConfiguration()) { +class LispMain constructor(var configuration: TerminalConfiguration = TerminalConfiguration()) { init { if (configuration.terminal == null) { diff --git a/src/main/kotlin/function/UserDefinedFunction.java b/src/main/kotlin/function/UserDefinedFunction.java deleted file mode 100644 index 36a37ff..0000000 --- a/src/main/kotlin/function/UserDefinedFunction.java +++ /dev/null @@ -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 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 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); - } - } -} diff --git a/src/main/kotlin/function/UserDefinedFunction.kt b/src/main/kotlin/function/UserDefinedFunction.kt new file mode 100644 index 0000000..f35e290 --- /dev/null +++ b/src/main/kotlin/function/UserDefinedFunction.kt @@ -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() + 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" + } +} diff --git a/src/main/kotlin/function/builtin/Exit.kt b/src/main/kotlin/function/builtin/Exit.kt index 814273d..b0c94a5 100644 --- a/src/main/kotlin/function/builtin/Exit.kt +++ b/src/main/kotlin/function/builtin/Exit.kt @@ -11,10 +11,8 @@ import sexpression.SExpression @FunctionNames("EXIT") class Exit(name: String) : LispFunction() { - private val argumentValidator: ArgumentValidator = ArgumentValidator(name) - - init { - this.argumentValidator.setMaximumNumberOfArguments(0) + private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply { + setMaximumNumberOfArguments(0) } override fun call(argumentList: Cons): SExpression { diff --git a/src/main/kotlin/function/builtin/predicate/NumericEqual.kt b/src/main/kotlin/function/builtin/predicate/NumericEqual.kt index ed75ba9..5f50fcc 100644 --- a/src/main/kotlin/function/builtin/predicate/NumericEqual.kt +++ b/src/main/kotlin/function/builtin/predicate/NumericEqual.kt @@ -12,11 +12,9 @@ import sexpression.Symbol.Companion.T @FunctionNames("=") class NumericEqual(name: String) : LispFunction() { - private val argumentValidator: ArgumentValidator = ArgumentValidator(name) - - init { - this.argumentValidator.setMinimumNumberOfArguments(1) - this.argumentValidator.setEveryArgumentExpectedType(LispNumber::class.java) + private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply { + setMinimumNumberOfArguments(1) + setEveryArgumentExpectedType(LispNumber::class.java) } override fun call(argumentList: Cons): SExpression { diff --git a/src/main/kotlin/function/builtin/special/Lambda.kt b/src/main/kotlin/function/builtin/special/Lambda.kt index 17c51ce..55b3a71 100644 --- a/src/main/kotlin/function/builtin/special/Lambda.kt +++ b/src/main/kotlin/function/builtin/special/Lambda.kt @@ -13,13 +13,13 @@ import sexpression.Symbol @FunctionNames("LAMBDA", "Λ") class Lambda(name: String) : LispSpecialFunction() { - private val argumentValidator: ArgumentValidator = ArgumentValidator(name) - private val lambdaListValidator: ArgumentValidator = ArgumentValidator("$name|lambda-list|") + private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply { + setFirstArgumentExpectedType(Cons::class.java) + setMinimumNumberOfArguments(1) + } - init { - this.argumentValidator.setFirstArgumentExpectedType(Cons::class.java) - this.argumentValidator.setMinimumNumberOfArguments(1) - this.lambdaListValidator.setEveryArgumentExpectedType(Symbol::class.java) + private val lambdaListValidator: ArgumentValidator = ArgumentValidator("$name|lambda-list|").apply { + setEveryArgumentExpectedType(Symbol::class.java) } override fun call(argumentList: Cons): LambdaExpression { diff --git a/src/main/kotlin/table/SymbolTable.kt b/src/main/kotlin/table/SymbolTable.kt index 50da9e6..41e9a14 100644 --- a/src/main/kotlin/table/SymbolTable.kt +++ b/src/main/kotlin/table/SymbolTable.kt @@ -8,7 +8,7 @@ import sexpression.SExpression import sexpression.Symbol import kotlin.collections.Map.Entry -open class SymbolTable @JvmOverloads constructor(open val parent: SymbolTable? = NullSymbolTable) : Iterable { +open class SymbolTable(open val parent: SymbolTable? = NullSymbolTable) : Iterable { private val table = mutableMapOf()