Convert Eval to kotlin
This commit is contained in:
parent
24343b1543
commit
75f02a89cc
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<kotlin.version>1.2.70</kotlin.version>
|
<kotlin.version>1.2.71</kotlin.version>
|
||||||
<junit5.version>5.2.0</junit5.version>
|
<junit5.version>5.2.0</junit5.version>
|
||||||
<skipTests>false</skipTests>
|
<skipTests>false</skipTests>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package function
|
package function
|
||||||
|
|
||||||
import error.LispException
|
import error.LispException
|
||||||
import function.builtin.EVAL.eval
|
import function.builtin.Eval.Companion.eval
|
||||||
import sexpression.Cons
|
import sexpression.Cons
|
||||||
import sexpression.Nil
|
import sexpression.Nil
|
||||||
import sexpression.SExpression
|
import sexpression.SExpression
|
||||||
|
|
|
@ -7,8 +7,8 @@ import sexpression.Cons;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
import table.FunctionTable;
|
import table.FunctionTable;
|
||||||
|
|
||||||
import static function.builtin.EVAL.applyFunction;
|
import static function.builtin.Eval.applyFunction;
|
||||||
import static function.builtin.EVAL.lookupFunctionOrLambda;
|
import static function.builtin.Eval.lookupFunctionOrLambda;
|
||||||
|
|
||||||
@FunctionNames({ "APPLY" })
|
@FunctionNames({ "APPLY" })
|
||||||
public class APPLY extends LispFunction {
|
public class APPLY extends LispFunction {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import sexpression.Cons;
|
||||||
import sexpression.Nil;
|
import sexpression.Nil;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
|
|
||||||
class BackquoteEvaluator {
|
class BackquoteEvaluator {
|
||||||
|
|
||||||
|
|
|
@ -1,229 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import error.LispException;
|
|
||||||
import function.ArgumentValidator;
|
|
||||||
import function.FunctionNames;
|
|
||||||
import function.LispFunction;
|
|
||||||
import function.builtin.special.RECUR.RecurNotInTailPositionException;
|
|
||||||
import sexpression.BackquoteExpression;
|
|
||||||
import sexpression.Cons;
|
|
||||||
import sexpression.LambdaExpression;
|
|
||||||
import sexpression.Nil;
|
|
||||||
import sexpression.SExpression;
|
|
||||||
import sexpression.Symbol;
|
|
||||||
import table.ExecutionContext;
|
|
||||||
import table.FunctionTable;
|
|
||||||
|
|
||||||
import static function.builtin.cons.LIST.makeList;
|
|
||||||
import static function.builtin.special.Lambda.Lambda;
|
|
||||||
import static java.text.MessageFormat.format;
|
|
||||||
|
|
||||||
@FunctionNames({ "EVAL" })
|
|
||||||
public class EVAL extends LispFunction {
|
|
||||||
|
|
||||||
private static ExecutionContext executionContext = ExecutionContext.INSTANCE;
|
|
||||||
|
|
||||||
public static SExpression eval(SExpression sExpression) {
|
|
||||||
Cons argumentList = makeList(sExpression);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return lookupEval().call(argumentList);
|
|
||||||
} catch (LispException e) {
|
|
||||||
executionContext.restoreGlobalScope();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SExpression applyFunction(LispFunction function, Cons argumentList) {
|
|
||||||
return lookupEval().applyFunctionWithoutEvaluatingArguments(function, argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Cons evaluateFunctionArgumentList(Cons argumentList) {
|
|
||||||
return lookupEval().evaluateArgumentList(argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static EVAL lookupEval() {
|
|
||||||
return (EVAL) FunctionTable.INSTANCE.lookupFunction("EVAL");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LispFunction lookupFunctionOrLambda(SExpression functionExpression) {
|
|
||||||
LispFunction function = FunctionTable.INSTANCE.lookupFunction(functionExpression.toString());
|
|
||||||
|
|
||||||
if (function == null)
|
|
||||||
function = createLambdaFunction(functionExpression);
|
|
||||||
|
|
||||||
return function;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LispFunction createLambdaFunction(SExpression lambdaExpression) {
|
|
||||||
if (lambdaExpression.isFunction())
|
|
||||||
return ((LambdaExpression) lambdaExpression).getFunction();
|
|
||||||
else if (Lambda.isLambdaExpression(lambdaExpression))
|
|
||||||
return Lambda.createFunction((Cons) lambdaExpression);
|
|
||||||
else
|
|
||||||
throw new UndefinedFunctionException(lambdaExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SExpression lookupSymbol(String symbolName) {
|
|
||||||
if (symbolName.equals("NIL"))
|
|
||||||
return Nil.INSTANCE;
|
|
||||||
else if (symbolName.equals("T"))
|
|
||||||
return Symbol.Companion.getT();
|
|
||||||
else if (symbolName.startsWith(":"))
|
|
||||||
return new Symbol(symbolName);
|
|
||||||
|
|
||||||
return ExecutionContext.INSTANCE.lookupSymbolValue(symbolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ArgumentValidator argumentValidator;
|
|
||||||
|
|
||||||
public EVAL(String name) {
|
|
||||||
this.argumentValidator = new ArgumentValidator(name);
|
|
||||||
this.argumentValidator.setExactNumberOfArguments(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SExpression call(Cons argumentList) {
|
|
||||||
verifyNotRecurring();
|
|
||||||
argumentValidator.validate(argumentList);
|
|
||||||
SExpression argument = argumentList.getFirst();
|
|
||||||
|
|
||||||
return evaluateExpression(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyNotRecurring() {
|
|
||||||
if (executionContext.isRecur()) {
|
|
||||||
executionContext.clearRecur();
|
|
||||||
throw new RecurNotInTailPositionException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression evaluateExpression(SExpression argument) {
|
|
||||||
if (argument.isList())
|
|
||||||
return evaluateList(argument);
|
|
||||||
else if (argument.isSymbol())
|
|
||||||
return evaluateSymbol(argument);
|
|
||||||
else if (argument.isBackquote())
|
|
||||||
return evaluateBackTick(argument);
|
|
||||||
else if (argument.isComma())
|
|
||||||
throw new UnmatchedCommaException();
|
|
||||||
else if (argument.isAtSign())
|
|
||||||
throw new UnmatchedAtSignException();
|
|
||||||
|
|
||||||
return argument; // NUMBER or STRING
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression evaluateList(SExpression argument) {
|
|
||||||
if (argument.isCons())
|
|
||||||
return evaluateFunction((Cons) argument);
|
|
||||||
|
|
||||||
return argument; // NIL
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression evaluateFunction(Cons list) {
|
|
||||||
SExpression functionName = list.getFirst();
|
|
||||||
SExpression arguments = list.getRest();
|
|
||||||
LispFunction function = lookupFunctionOrLambda(functionName);
|
|
||||||
validateFunctionList(list, functionName);
|
|
||||||
|
|
||||||
return callFunction(function, (Cons) arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateFunctionList(Cons list, SExpression functionName) {
|
|
||||||
ArgumentValidator functionListValidator = new ArgumentValidator(functionName.toString());
|
|
||||||
functionListValidator.validate(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression callFunction(LispFunction function, Cons argumentList) {
|
|
||||||
if (function.isArgumentListEvaluated())
|
|
||||||
argumentList = evaluateArgumentList(argumentList);
|
|
||||||
|
|
||||||
return applyFunctionWithoutEvaluatingArguments(function, argumentList);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression applyFunctionWithoutEvaluatingArguments(LispFunction function, Cons argumentList) {
|
|
||||||
verifyNotRecurring();
|
|
||||||
|
|
||||||
SExpression result = function.call(argumentList);
|
|
||||||
|
|
||||||
if (function.isMacro())
|
|
||||||
result = eval(result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Cons evaluateArgumentList(Cons arguments) {
|
|
||||||
if (arguments.isNull())
|
|
||||||
return Nil.INSTANCE;
|
|
||||||
|
|
||||||
SExpression first = eval(arguments.getFirst());
|
|
||||||
SExpression rest = arguments.getRest();
|
|
||||||
|
|
||||||
return new Cons(first, evaluateArgumentList((Cons) rest));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression evaluateSymbol(SExpression argument) {
|
|
||||||
SExpression symbolValue = lookupSymbol(argument.toString());
|
|
||||||
|
|
||||||
if (symbolValue != null)
|
|
||||||
return symbolValue;
|
|
||||||
|
|
||||||
throw new UndefinedSymbolException(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SExpression evaluateBackTick(SExpression argument) {
|
|
||||||
BackquoteEvaluator evaluator = new BackquoteEvaluator((BackquoteExpression) argument);
|
|
||||||
|
|
||||||
return evaluator.evaluate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UndefinedFunctionException extends LispException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private SExpression function;
|
|
||||||
|
|
||||||
public UndefinedFunctionException(SExpression function) {
|
|
||||||
this.function = function;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return format("undefined function: {0}", function);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UndefinedSymbolException extends LispException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private SExpression symbol;
|
|
||||||
|
|
||||||
public UndefinedSymbolException(SExpression symbol) {
|
|
||||||
this.symbol = symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return format("symbol {0} has no value", symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UnmatchedCommaException extends LispException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return "unmatched comma";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UnmatchedAtSignException extends LispException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMessage() {
|
|
||||||
return "unmatched at sign";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import error.LispException
|
||||||
|
import function.ArgumentValidator
|
||||||
|
import function.FunctionNames
|
||||||
|
import function.LispFunction
|
||||||
|
import function.builtin.cons.LIST.makeList
|
||||||
|
import function.builtin.special.Lambda.Lambda.createFunction
|
||||||
|
import function.builtin.special.Lambda.Lambda.isLambdaExpression
|
||||||
|
import function.builtin.special.RECUR.RecurNotInTailPositionException
|
||||||
|
import sexpression.BackquoteExpression
|
||||||
|
import sexpression.Cons
|
||||||
|
import sexpression.LambdaExpression
|
||||||
|
import sexpression.Nil
|
||||||
|
import sexpression.SExpression
|
||||||
|
import sexpression.Symbol
|
||||||
|
import table.ExecutionContext
|
||||||
|
import table.FunctionTable
|
||||||
|
|
||||||
|
@FunctionNames("EVAL")
|
||||||
|
class Eval(name: String) : LispFunction() {
|
||||||
|
|
||||||
|
private val argumentValidator: ArgumentValidator = ArgumentValidator(name).apply {
|
||||||
|
setExactNumberOfArguments(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun call(argumentList: Cons): SExpression {
|
||||||
|
verifyNotRecurring()
|
||||||
|
argumentValidator.validate(argumentList)
|
||||||
|
|
||||||
|
return evaluateExpression(argumentList.first)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifyNotRecurring() {
|
||||||
|
if (ExecutionContext.isRecur) {
|
||||||
|
ExecutionContext.clearRecur()
|
||||||
|
throw RecurNotInTailPositionException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateExpression(argument: SExpression) = when {
|
||||||
|
argument.isList -> evaluateList(argument)
|
||||||
|
argument.isSymbol -> evaluateSymbol(argument)
|
||||||
|
argument.isBackquote -> evaluateBackTick(argument)
|
||||||
|
argument.isComma -> throw UnmatchedCommaException()
|
||||||
|
argument.isAtSign -> throw UnmatchedAtSignException()
|
||||||
|
else -> argument // NUMBER or STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateList(argument: SExpression) =
|
||||||
|
if (argument.isCons)
|
||||||
|
evaluateFunction(argument as Cons)
|
||||||
|
else
|
||||||
|
argument // NIL
|
||||||
|
|
||||||
|
private fun evaluateFunction(list: Cons): SExpression {
|
||||||
|
val functionName = list.first
|
||||||
|
val arguments = list.rest
|
||||||
|
val function = lookupFunctionOrLambda(functionName)
|
||||||
|
validateFunctionList(list, functionName)
|
||||||
|
|
||||||
|
return callFunction(function, arguments as Cons)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateFunctionList(list: Cons, functionName: SExpression) {
|
||||||
|
ArgumentValidator(functionName.toString()).validate(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun callFunction(function: LispFunction, argumentList: Cons) =
|
||||||
|
if (function.isArgumentListEvaluated)
|
||||||
|
applyFunctionWithoutEvaluatingArguments(function, evaluateArgumentList(argumentList))
|
||||||
|
else
|
||||||
|
applyFunctionWithoutEvaluatingArguments(function, argumentList)
|
||||||
|
|
||||||
|
private fun applyFunctionWithoutEvaluatingArguments(function: LispFunction, argumentList: Cons): SExpression {
|
||||||
|
verifyNotRecurring()
|
||||||
|
|
||||||
|
return function.call(argumentList).let {
|
||||||
|
if (function.isMacro) eval(it) else it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateArgumentList(arguments: Cons): Cons {
|
||||||
|
if (arguments.isNull)
|
||||||
|
return Nil
|
||||||
|
|
||||||
|
val first = eval(arguments.first)
|
||||||
|
val rest = arguments.rest as Cons
|
||||||
|
|
||||||
|
return Cons(first, evaluateArgumentList(rest))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateSymbol(argument: SExpression) =
|
||||||
|
lookupSymbol(argument.toString()) ?: throw UndefinedSymbolException(argument)
|
||||||
|
|
||||||
|
private fun evaluateBackTick(argument: SExpression) =
|
||||||
|
BackquoteEvaluator(argument as BackquoteExpression).evaluate()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private fun lookupEval() = FunctionTable.lookupFunction("EVAL") as Eval
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun eval(sExpression: SExpression): SExpression {
|
||||||
|
try {
|
||||||
|
return lookupEval().call(makeList(sExpression))
|
||||||
|
} catch (e: LispException) {
|
||||||
|
ExecutionContext.restoreGlobalScope()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun applyFunction(function: LispFunction, argumentList: Cons) =
|
||||||
|
lookupEval().applyFunctionWithoutEvaluatingArguments(function, argumentList)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun evaluateFunctionArgumentList(argumentList: Cons) = lookupEval().evaluateArgumentList(argumentList)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun lookupSymbol(symbolName: String) = when {
|
||||||
|
symbolName == "NIL" -> Nil
|
||||||
|
symbolName == "T" -> Symbol.T
|
||||||
|
symbolName.startsWith(":") -> Symbol(symbolName)
|
||||||
|
else -> ExecutionContext.lookupSymbolValue(symbolName)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun lookupFunctionOrLambda(functionExpression: SExpression): LispFunction {
|
||||||
|
val function = FunctionTable.lookupFunction(functionExpression.toString())
|
||||||
|
|
||||||
|
return function ?: createLambdaFunction(functionExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createLambdaFunction(lambdaExpression: SExpression) = when {
|
||||||
|
lambdaExpression.isFunction -> (lambdaExpression as LambdaExpression).function
|
||||||
|
isLambdaExpression(lambdaExpression) -> createFunction(lambdaExpression as Cons)
|
||||||
|
else -> throw UndefinedFunctionException(lambdaExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UndefinedFunctionException(function: SExpression) : LispException() {
|
||||||
|
|
||||||
|
override val message = "undefined function: $function"
|
||||||
|
}
|
||||||
|
|
||||||
|
class UndefinedSymbolException(symbol: SExpression) : LispException() {
|
||||||
|
|
||||||
|
override val message = "symbol $symbol has no value"
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnmatchedCommaException : LispException() {
|
||||||
|
|
||||||
|
override val message = "unmatched comma"
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnmatchedAtSignException : LispException() {
|
||||||
|
|
||||||
|
override val message = "unmatched at sign"
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static java.text.MessageFormat.format;
|
import static java.text.MessageFormat.format;
|
||||||
|
|
||||||
@FunctionNames({ "LOAD" })
|
@FunctionNames({ "LOAD" })
|
||||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
import sexpression.Symbol;
|
import sexpression.Symbol;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static recursion.TailCalls.done;
|
import static recursion.TailCalls.done;
|
||||||
import static recursion.TailCalls.tailCall;
|
import static recursion.TailCalls.tailCall;
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import sexpression.Nil;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
import sexpression.Symbol;
|
import sexpression.Symbol;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static function.builtin.predicate.EQUAL.isEqual;
|
import static function.builtin.predicate.EQUAL.isEqual;
|
||||||
import static recursion.TailCalls.done;
|
import static recursion.TailCalls.done;
|
||||||
import static recursion.TailCalls.tailCall;
|
import static recursion.TailCalls.tailCall;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
||||||
import sexpression.Nil;
|
import sexpression.Nil;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static recursion.TailCalls.done;
|
import static recursion.TailCalls.done;
|
||||||
import static recursion.TailCalls.tailCall;
|
import static recursion.TailCalls.tailCall;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import function.LispSpecialFunction;
|
||||||
import sexpression.Cons;
|
import sexpression.Cons;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
|
|
||||||
@FunctionNames({ "IF" })
|
@FunctionNames({ "IF" })
|
||||||
public class IF extends LispSpecialFunction {
|
public class IF extends LispSpecialFunction {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import sexpression.Symbol;
|
||||||
import table.ExecutionContext;
|
import table.ExecutionContext;
|
||||||
import table.SymbolTable;
|
import table.SymbolTable;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
|
|
||||||
@FunctionNames({ "LET" })
|
@FunctionNames({ "LET" })
|
||||||
public class LET extends LispSpecialFunction {
|
public class LET extends LispSpecialFunction {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import recursion.TailCall;
|
||||||
import sexpression.Cons;
|
import sexpression.Cons;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static recursion.TailCalls.done;
|
import static recursion.TailCalls.done;
|
||||||
import static recursion.TailCalls.tailCall;
|
import static recursion.TailCalls.tailCall;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
||||||
import sexpression.Nil;
|
import sexpression.Nil;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static recursion.TailCalls.done;
|
import static recursion.TailCalls.done;
|
||||||
import static recursion.TailCalls.tailCall;
|
import static recursion.TailCalls.tailCall;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
import table.ExecutionContext;
|
import table.ExecutionContext;
|
||||||
|
|
||||||
import static function.builtin.EVAL.evaluateFunctionArgumentList;
|
import static function.builtin.Eval.evaluateFunctionArgumentList;
|
||||||
|
|
||||||
@FunctionNames({ "RECUR" })
|
@FunctionNames({ "RECUR" })
|
||||||
public class RECUR extends LispSpecialFunction {
|
public class RECUR extends LispSpecialFunction {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import sexpression.Cons;
|
||||||
import sexpression.SExpression;
|
import sexpression.SExpression;
|
||||||
import sexpression.Symbol;
|
import sexpression.Symbol;
|
||||||
|
|
||||||
import static function.builtin.EVAL.eval;
|
import static function.builtin.Eval.eval;
|
||||||
import static function.builtin.SET.set;
|
import static function.builtin.SET.set;
|
||||||
import static function.builtin.cons.LIST.makeList;
|
import static function.builtin.cons.LIST.makeList;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import environment.RuntimeEnvironment.input
|
||||||
import environment.RuntimeEnvironment.inputName
|
import environment.RuntimeEnvironment.inputName
|
||||||
import environment.RuntimeEnvironment.output
|
import environment.RuntimeEnvironment.output
|
||||||
import error.LispException
|
import error.LispException
|
||||||
import function.builtin.EVAL.eval
|
import function.builtin.Eval.Companion.eval
|
||||||
import parser.LispParser
|
import parser.LispParser
|
||||||
import sexpression.SExpression
|
import sexpression.SExpression
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
|
@ -4,7 +4,7 @@ import error.CriticalLispException
|
||||||
import function.FunctionNames
|
import function.FunctionNames
|
||||||
import function.LispFunction
|
import function.LispFunction
|
||||||
import function.builtin.APPLY
|
import function.builtin.APPLY
|
||||||
import function.builtin.EVAL
|
import function.builtin.Eval
|
||||||
import function.builtin.Exit
|
import function.builtin.Exit
|
||||||
import function.builtin.FUNCALL
|
import function.builtin.FUNCALL
|
||||||
import function.builtin.FUSE
|
import function.builtin.FUSE
|
||||||
|
@ -68,7 +68,7 @@ object FunctionTable {
|
||||||
EQ::class.java,
|
EQ::class.java,
|
||||||
EQUAL::class.java,
|
EQUAL::class.java,
|
||||||
NumericEqual::class.java,
|
NumericEqual::class.java,
|
||||||
EVAL::class.java,
|
Eval::class.java,
|
||||||
Exit::class.java,
|
Exit::class.java,
|
||||||
FIRST::class.java,
|
FIRST::class.java,
|
||||||
FUNCALL::class.java,
|
FUNCALL::class.java,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException;
|
||||||
import function.ArgumentValidator.DottedArgumentListException;
|
import function.ArgumentValidator.DottedArgumentListException;
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
import function.ArgumentValidator.TooFewArgumentsException;
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
import function.ArgumentValidator.TooManyArgumentsException;
|
||||||
import function.builtin.EVAL.UndefinedFunctionException;
|
import function.builtin.Eval.UndefinedFunctionException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sexpression.Cons;
|
import sexpression.Cons;
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
import testutil.SymbolAndFunctionCleaner;
|
||||||
|
|
|
@ -1,256 +0,0 @@
|
||||||
package function.builtin;
|
|
||||||
|
|
||||||
import function.ArgumentValidator.DottedArgumentListException;
|
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
|
||||||
import function.builtin.BackquoteEvaluator.AtSignNotInCommaException;
|
|
||||||
import function.builtin.EVAL.UndefinedFunctionException;
|
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
|
||||||
import function.builtin.EVAL.UnmatchedAtSignException;
|
|
||||||
import function.builtin.EVAL.UnmatchedCommaException;
|
|
||||||
import function.builtin.special.RECUR.RecurNotInTailPositionException;
|
|
||||||
import org.junit.Test;
|
|
||||||
import sexpression.Nil;
|
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
|
||||||
|
|
||||||
import static function.builtin.EVAL.lookupSymbol;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
import static testutil.TestUtilities.assertIsErrorWithMessage;
|
|
||||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
||||||
import static testutil.TestUtilities.evaluateString;
|
|
||||||
import static testutil.TestUtilities.parseString;
|
|
||||||
|
|
||||||
public class EVALTest extends SymbolAndFunctionCleaner {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalNumber() {
|
|
||||||
String input = "(eval 9)";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("9"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalNil() {
|
|
||||||
String input = "(eval ())";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("()"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalUsesCurrentLexicalEnvironment() {
|
|
||||||
String input = "(let ((x 1)) (eval '(+ x 1)))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("2"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupKeywordSymbol() {
|
|
||||||
String symbol = ":symbol";
|
|
||||||
assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupT() {
|
|
||||||
String symbol = "T";
|
|
||||||
assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupNil() {
|
|
||||||
String symbol = "NIL";
|
|
||||||
assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupUndefinedSymbol() {
|
|
||||||
assertNull(EVAL.lookupSymbol("undefined"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = UndefinedFunctionException.class)
|
|
||||||
public void evalUndefinedFunction() {
|
|
||||||
String input = "(funcall 'eval '(undefined))";
|
|
||||||
|
|
||||||
evaluateString(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = UndefinedSymbolException.class)
|
|
||||||
public void evalUndefinedSymbol() {
|
|
||||||
String input = "(eval undefined)";
|
|
||||||
|
|
||||||
evaluateString(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = DottedArgumentListException.class)
|
|
||||||
public void evalWithDottedLambdaList() {
|
|
||||||
String input = "(funcall 'eval (cons '+ 1))";
|
|
||||||
|
|
||||||
evaluateString(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooManyArgumentsException.class)
|
|
||||||
public void evalWithTooManyArguments() {
|
|
||||||
evaluateString("(eval '1 '2 '3)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void evalWithTooFewArguments() {
|
|
||||||
evaluateString("(eval)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void undefinedFunctionException_HasCorrectAttributes() {
|
|
||||||
assertIsErrorWithMessage(new UndefinedFunctionException(Nil.INSTANCE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void undefinedSymbolException_HasCorrectAttributes() {
|
|
||||||
assertIsErrorWithMessage(new UndefinedSymbolException(Nil.INSTANCE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = UnmatchedCommaException.class)
|
|
||||||
public void evalComma() {
|
|
||||||
String input = ",a";
|
|
||||||
|
|
||||||
evaluateString(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = UnmatchedAtSignException.class)
|
|
||||||
public void evalAtSign() {
|
|
||||||
String input = "@a";
|
|
||||||
|
|
||||||
evaluateString(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalBackTick() {
|
|
||||||
String input = "`(a b c)";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("(a b c)"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalBackTickWithCommasAndAtSigns() {
|
|
||||||
String input = "(let ((x '(1 2 3)) (y '(4 5 6)) (z 'apple)) `(start ,x ,@y ,z end))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("(start (1 2 3) 4 5 6 apple end)"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalBackTickOnComma() {
|
|
||||||
String input = "`,9";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("9"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = AtSignNotInCommaException.class)
|
|
||||||
public void evalBackTickOnAtSign() {
|
|
||||||
evaluateString("`@9");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalNestedBackquotes() {
|
|
||||||
String input = "`,`,`,`,9";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("9"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void unmatchedCommaException_HasCorrectAttributes() {
|
|
||||||
assertIsErrorWithMessage(new UnmatchedCommaException());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void unmatchedAtSignException_HasCorrectAttributes() {
|
|
||||||
assertIsErrorWithMessage(new UnmatchedAtSignException());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalQuoteInNestedList() {
|
|
||||||
String input = "(let ((g 27)) `((,g)))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("((27))"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalAtSignInNestedList() {
|
|
||||||
String input = "(let ((g '(1 2 3))) `((,@g)))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("((1 2 3))"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void evalNestedBackquotesInList() {
|
|
||||||
String input = "`(,`(1 ,`2 ,@`(3)))";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("((1 2 3))"), evaluateString(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void scopeRestoredAfterFailure_Let() {
|
|
||||||
evaluateString("(setq n 100)");
|
|
||||||
|
|
||||||
try {
|
|
||||||
evaluateString("(let ((n 200)) (begin 1 2 3 y))");
|
|
||||||
fail("expected exception");
|
|
||||||
} catch (UndefinedSymbolException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("100"), evaluateString("n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void scopeRestoredAfterFailure_Defun() {
|
|
||||||
evaluateString("(setq n 100)");
|
|
||||||
|
|
||||||
try {
|
|
||||||
evaluateString("(defun test (n) (begin 1 2 3 y))");
|
|
||||||
evaluateString("(test 200)");
|
|
||||||
fail("expected exception");
|
|
||||||
} catch (UndefinedSymbolException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("100"), evaluateString("n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void scopeRestoredAfterFailure_Lambda() {
|
|
||||||
evaluateString("(setq n 100)");
|
|
||||||
|
|
||||||
try {
|
|
||||||
evaluateString("((lambda (n) (begin 1 2 3 y)) 200)");
|
|
||||||
fail("expected exception");
|
|
||||||
} catch (UndefinedSymbolException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("100"), evaluateString("n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void scopeRestoredAfterFailure_Recur() {
|
|
||||||
evaluateString("(setq n 100)");
|
|
||||||
|
|
||||||
try {
|
|
||||||
evaluateString("(defun tail-recursive (n) (begin (recur) 2))");
|
|
||||||
evaluateString("(tail-recursive 200)");
|
|
||||||
fail("expected exception");
|
|
||||||
} catch (RecurNotInTailPositionException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("100"), evaluateString("n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void scopeRestoredAfterFailure_Apply() {
|
|
||||||
evaluateString("(setq n 100)");
|
|
||||||
|
|
||||||
try {
|
|
||||||
evaluateString("(defun tail-recursive (n) (begin (recur) 2))");
|
|
||||||
evaluateString("(apply 'tail-recursive '(200))");
|
|
||||||
fail("expected exception");
|
|
||||||
} catch (RecurNotInTailPositionException e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("100"), evaluateString("n"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,278 @@
|
||||||
|
package function.builtin
|
||||||
|
|
||||||
|
import function.ArgumentValidator.DottedArgumentListException
|
||||||
|
import function.ArgumentValidator.TooFewArgumentsException
|
||||||
|
import function.ArgumentValidator.TooManyArgumentsException
|
||||||
|
import function.builtin.BackquoteEvaluator.AtSignNotInCommaException
|
||||||
|
import function.builtin.Eval.Companion.lookupSymbol
|
||||||
|
import function.builtin.Eval.UndefinedFunctionException
|
||||||
|
import function.builtin.Eval.UndefinedSymbolException
|
||||||
|
import function.builtin.Eval.UnmatchedAtSignException
|
||||||
|
import function.builtin.Eval.UnmatchedCommaException
|
||||||
|
import function.builtin.special.RECUR.RecurNotInTailPositionException
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
|
||||||
|
import org.junit.jupiter.api.fail
|
||||||
|
import sexpression.Nil
|
||||||
|
import testutil.SymbolAndFunctionCleaner
|
||||||
|
import testutil.TestUtilities.assertIsErrorWithMessage
|
||||||
|
import testutil.TestUtilities.assertSExpressionsMatch
|
||||||
|
import testutil.TestUtilities.evaluateString
|
||||||
|
import testutil.TestUtilities.parseString
|
||||||
|
|
||||||
|
@TestInstance(PER_CLASS)
|
||||||
|
class EvalTest : SymbolAndFunctionCleaner() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalNumber() {
|
||||||
|
val input = "(eval 9)"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("9"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalNil() {
|
||||||
|
val input = "(eval ())"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("()"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalUsesCurrentLexicalEnvironment() {
|
||||||
|
val input = "(let ((x 1)) (eval '(+ x 1)))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("2"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lookupKeywordSymbol() {
|
||||||
|
val symbol = ":symbol"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lookupT() {
|
||||||
|
val symbol = "T"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lookupNil() {
|
||||||
|
val symbol = "NIL"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun lookupUndefinedSymbol() {
|
||||||
|
assertThat(Eval.lookupSymbol("undefined")).isNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalUndefinedFunction() {
|
||||||
|
val input = "(funcall 'eval '(undefined))"
|
||||||
|
|
||||||
|
assertThrows(UndefinedFunctionException::class.java) {
|
||||||
|
evaluateString(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalUndefinedSymbol() {
|
||||||
|
val input = "(eval undefined)"
|
||||||
|
|
||||||
|
assertThrows(UndefinedSymbolException::class.java) {
|
||||||
|
evaluateString(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalWithDottedLambdaList() {
|
||||||
|
val input = "(funcall 'eval (cons '+ 1))"
|
||||||
|
|
||||||
|
assertThrows(DottedArgumentListException::class.java) {
|
||||||
|
evaluateString(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalWithTooManyArguments() {
|
||||||
|
assertThrows(TooManyArgumentsException::class.java) {
|
||||||
|
evaluateString("(eval '1 '2 '3)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalWithTooFewArguments() {
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(eval)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun undefinedFunctionException_HasCorrectAttributes() {
|
||||||
|
assertIsErrorWithMessage(UndefinedFunctionException(Nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun undefinedSymbolException_HasCorrectAttributes() {
|
||||||
|
assertIsErrorWithMessage(UndefinedSymbolException(Nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalComma() {
|
||||||
|
val input = ",a"
|
||||||
|
|
||||||
|
assertThrows(UnmatchedCommaException::class.java) {
|
||||||
|
evaluateString(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalAtSign() {
|
||||||
|
val input = "@a"
|
||||||
|
|
||||||
|
assertThrows(UnmatchedAtSignException::class.java) {
|
||||||
|
evaluateString(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalBackTick() {
|
||||||
|
val input = "`(a b c)"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("(a b c)"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalBackTickWithCommasAndAtSigns() {
|
||||||
|
val input = "(let ((x '(1 2 3)) (y '(4 5 6)) (z 'apple)) `(start ,x ,@y ,z end))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("(start (1 2 3) 4 5 6 apple end)"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalBackTickOnComma() {
|
||||||
|
val input = "`,9"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("9"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalBackTickOnAtSign() {
|
||||||
|
assertThrows(AtSignNotInCommaException::class.java) {
|
||||||
|
evaluateString("`@9")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalNestedBackquotes() {
|
||||||
|
val input = "`,`,`,`,9"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("9"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unmatchedCommaException_HasCorrectAttributes() {
|
||||||
|
assertIsErrorWithMessage(UnmatchedCommaException())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unmatchedAtSignException_HasCorrectAttributes() {
|
||||||
|
assertIsErrorWithMessage(UnmatchedAtSignException())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalQuoteInNestedList() {
|
||||||
|
val input = "(let ((g 27)) `((,g)))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("((27))"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalAtSignInNestedList() {
|
||||||
|
val input = "(let ((g '(1 2 3))) `((,@g)))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("((1 2 3))"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun evalNestedBackquotesInList() {
|
||||||
|
val input = "`(,`(1 ,`2 ,@`(3)))"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("((1 2 3))"), evaluateString(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scopeRestoredAfterFailure_Let() {
|
||||||
|
evaluateString("(setq n 100)")
|
||||||
|
|
||||||
|
try {
|
||||||
|
evaluateString("(let ((n 200)) (begin 1 2 3 y))")
|
||||||
|
fail("expected exception")
|
||||||
|
} catch (e: UndefinedSymbolException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("100"), evaluateString("n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scopeRestoredAfterFailure_Defun() {
|
||||||
|
evaluateString("(setq n 100)")
|
||||||
|
|
||||||
|
try {
|
||||||
|
evaluateString("(defun test (n) (begin 1 2 3 y))")
|
||||||
|
evaluateString("(test 200)")
|
||||||
|
fail("expected exception")
|
||||||
|
} catch (e: UndefinedSymbolException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("100"), evaluateString("n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scopeRestoredAfterFailure_Lambda() {
|
||||||
|
evaluateString("(setq n 100)")
|
||||||
|
|
||||||
|
try {
|
||||||
|
evaluateString("((lambda (n) (begin 1 2 3 y)) 200)")
|
||||||
|
fail("expected exception")
|
||||||
|
} catch (e: UndefinedSymbolException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("100"), evaluateString("n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scopeRestoredAfterFailure_Recur() {
|
||||||
|
evaluateString("(setq n 100)")
|
||||||
|
|
||||||
|
try {
|
||||||
|
evaluateString("(defun tail-recursive (n) (begin (recur) 2))")
|
||||||
|
evaluateString("(tail-recursive 200)")
|
||||||
|
fail("expected exception")
|
||||||
|
} catch (e: RecurNotInTailPositionException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("100"), evaluateString("n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun scopeRestoredAfterFailure_Apply() {
|
||||||
|
evaluateString("(setq n 100)")
|
||||||
|
|
||||||
|
try {
|
||||||
|
evaluateString("(defun tail-recursive (n) (begin (recur) 2))")
|
||||||
|
evaluateString("(apply 'tail-recursive '(200))")
|
||||||
|
fail("expected exception")
|
||||||
|
} catch (e: RecurNotInTailPositionException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("100"), evaluateString("n"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ package function.builtin;
|
||||||
import function.ArgumentValidator.BadArgumentTypeException;
|
import function.ArgumentValidator.BadArgumentTypeException;
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
import function.ArgumentValidator.TooFewArgumentsException;
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
import function.ArgumentValidator.TooManyArgumentsException;
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.Eval.UndefinedSymbolException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sexpression.LispNumber;
|
import sexpression.LispNumber;
|
||||||
import table.SymbolTable;
|
import table.SymbolTable;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package function.builtin.special;
|
package function.builtin.special;
|
||||||
|
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.Eval.UndefinedSymbolException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sexpression.LispNumber;
|
import sexpression.LispNumber;
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
import testutil.SymbolAndFunctionCleaner;
|
||||||
|
|
|
@ -2,7 +2,7 @@ package function.builtin.special;
|
||||||
|
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
import function.ArgumentValidator.TooFewArgumentsException;
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
import function.ArgumentValidator.TooManyArgumentsException;
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.Eval.UndefinedSymbolException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
import testutil.SymbolAndFunctionCleaner;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException;
|
||||||
import function.ArgumentValidator.DottedArgumentListException;
|
import function.ArgumentValidator.DottedArgumentListException;
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
import function.ArgumentValidator.TooFewArgumentsException;
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
import function.ArgumentValidator.TooManyArgumentsException;
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.Eval.UndefinedSymbolException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sexpression.Cons;
|
import sexpression.Cons;
|
||||||
import sexpression.LispNumber;
|
import sexpression.LispNumber;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException;
|
||||||
import function.ArgumentValidator.DottedArgumentListException;
|
import function.ArgumentValidator.DottedArgumentListException;
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
import function.ArgumentValidator.TooFewArgumentsException;
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
import function.ArgumentValidator.TooManyArgumentsException;
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.Eval.UndefinedSymbolException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sexpression.Cons;
|
import sexpression.Cons;
|
||||||
import sexpression.LispNumber;
|
import sexpression.LispNumber;
|
||||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException
|
||||||
import function.ArgumentValidator.DottedArgumentListException
|
import function.ArgumentValidator.DottedArgumentListException
|
||||||
import function.ArgumentValidator.TooFewArgumentsException
|
import function.ArgumentValidator.TooFewArgumentsException
|
||||||
import function.ArgumentValidator.TooManyArgumentsException
|
import function.ArgumentValidator.TooManyArgumentsException
|
||||||
import function.builtin.EVAL.UndefinedFunctionException
|
import function.builtin.Eval.UndefinedFunctionException
|
||||||
import function.builtin.special.Lambda.Lambda.createFunction
|
import function.builtin.special.Lambda.Lambda.createFunction
|
||||||
import function.builtin.special.Lambda.Lambda.isLambdaExpression
|
import function.builtin.special.Lambda.Lambda.isLambdaExpression
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package function.builtin.special;
|
package function.builtin.special;
|
||||||
|
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.Eval.UndefinedSymbolException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sexpression.LispNumber;
|
import sexpression.LispNumber;
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
import testutil.SymbolAndFunctionCleaner;
|
||||||
|
|
|
@ -3,7 +3,7 @@ package function.builtin.special;
|
||||||
import function.ArgumentValidator.BadArgumentTypeException;
|
import function.ArgumentValidator.BadArgumentTypeException;
|
||||||
import function.ArgumentValidator.TooFewArgumentsException;
|
import function.ArgumentValidator.TooFewArgumentsException;
|
||||||
import function.ArgumentValidator.TooManyArgumentsException;
|
import function.ArgumentValidator.TooManyArgumentsException;
|
||||||
import function.builtin.EVAL.UndefinedSymbolException;
|
import function.builtin.Eval.UndefinedSymbolException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sexpression.LispNumber;
|
import sexpression.LispNumber;
|
||||||
import table.SymbolTable;
|
import table.SymbolTable;
|
||||||
|
|
|
@ -15,7 +15,7 @@ abstract class SymbolAndFunctionCleaner {
|
||||||
@Before
|
@Before
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
executionContext.clearContext()
|
ExecutionContext.clearContext()
|
||||||
FunctionTable.resetFunctionTable()
|
FunctionTable.resetFunctionTable()
|
||||||
additionalSetUp()
|
additionalSetUp()
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ abstract class SymbolAndFunctionCleaner {
|
||||||
@After
|
@After
|
||||||
@AfterEach
|
@AfterEach
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
executionContext.clearContext()
|
ExecutionContext.clearContext()
|
||||||
FunctionTable.resetFunctionTable()
|
FunctionTable.resetFunctionTable()
|
||||||
additionalTearDown()
|
additionalTearDown()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package testutil
|
||||||
|
|
||||||
import error.LispException
|
import error.LispException
|
||||||
import error.Severity.ERROR
|
import error.Severity.ERROR
|
||||||
import function.builtin.EVAL.eval
|
import function.builtin.Eval.Companion.eval
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import parser.LispParser
|
import parser.LispParser
|
||||||
import sexpression.Cons
|
import sexpression.Cons
|
||||||
|
|
Loading…
Reference in New Issue