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.text.MessageFormat.format
|
||||
|
||||
class LispMain @JvmOverloads constructor(var configuration: TerminalConfiguration = TerminalConfiguration()) {
|
||||
class LispMain constructor(var configuration: TerminalConfiguration = TerminalConfiguration()) {
|
||||
|
||||
init {
|
||||
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")
|
||||
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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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<SymbolTable> {
|
||||
open class SymbolTable(open val parent: SymbolTable? = NullSymbolTable) : Iterable<SymbolTable> {
|
||||
|
||||
private val table = mutableMapOf<String, SExpression>()
|
||||
|
||||
|
|
Loading…
Reference in New Issue