Convert UserDefinedFunction to kotlin
This commit is contained in:
parent
e6e3965485
commit
ba6418a977
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue