Convert Eval to kotlin
This commit is contained in:
parent
24343b1543
commit
75f02a89cc
2
pom.xml
2
pom.xml
|
@ -10,7 +10,7 @@
|
|||
|
||||
<properties>
|
||||
<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>
|
||||
<skipTests>false</skipTests>
|
||||
</properties>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package function
|
||||
|
||||
import error.LispException
|
||||
import function.builtin.EVAL.eval
|
||||
import function.builtin.Eval.Companion.eval
|
||||
import sexpression.Cons
|
||||
import sexpression.Nil
|
||||
import sexpression.SExpression
|
||||
|
|
|
@ -7,8 +7,8 @@ import sexpression.Cons;
|
|||
import sexpression.SExpression;
|
||||
import table.FunctionTable;
|
||||
|
||||
import static function.builtin.EVAL.applyFunction;
|
||||
import static function.builtin.EVAL.lookupFunctionOrLambda;
|
||||
import static function.builtin.Eval.applyFunction;
|
||||
import static function.builtin.Eval.lookupFunctionOrLambda;
|
||||
|
||||
@FunctionNames({ "APPLY" })
|
||||
public class APPLY extends LispFunction {
|
||||
|
|
|
@ -9,7 +9,7 @@ import sexpression.Cons;
|
|||
import sexpression.Nil;
|
||||
import sexpression.SExpression;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
|
||||
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.util.Stack;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
import static java.text.MessageFormat.format;
|
||||
|
||||
@FunctionNames({ "LOAD" })
|
||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
|||
import sexpression.SExpression;
|
||||
import sexpression.Symbol;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import sexpression.Nil;
|
|||
import sexpression.SExpression;
|
||||
import sexpression.Symbol;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
import static function.builtin.predicate.EQUAL.isEqual;
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
|||
import sexpression.Nil;
|
||||
import sexpression.SExpression;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import function.LispSpecialFunction;
|
|||
import sexpression.Cons;
|
||||
import sexpression.SExpression;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
|
||||
@FunctionNames({ "IF" })
|
||||
public class IF extends LispSpecialFunction {
|
||||
|
|
|
@ -10,7 +10,7 @@ import sexpression.Symbol;
|
|||
import table.ExecutionContext;
|
||||
import table.SymbolTable;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
|
||||
@FunctionNames({ "LET" })
|
||||
public class LET extends LispSpecialFunction {
|
||||
|
|
|
@ -7,7 +7,7 @@ import recursion.TailCall;
|
|||
import sexpression.Cons;
|
||||
import sexpression.SExpression;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
|||
import sexpression.Nil;
|
||||
import sexpression.SExpression;
|
||||
|
||||
import static function.builtin.EVAL.eval;
|
||||
import static function.builtin.Eval.eval;
|
||||
import static recursion.TailCalls.done;
|
||||
import static recursion.TailCalls.tailCall;
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import sexpression.Cons;
|
|||
import sexpression.SExpression;
|
||||
import table.ExecutionContext;
|
||||
|
||||
import static function.builtin.EVAL.evaluateFunctionArgumentList;
|
||||
import static function.builtin.Eval.evaluateFunctionArgumentList;
|
||||
|
||||
@FunctionNames({ "RECUR" })
|
||||
public class RECUR extends LispSpecialFunction {
|
||||
|
|
|
@ -7,7 +7,7 @@ import sexpression.Cons;
|
|||
import sexpression.SExpression;
|
||||
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.cons.LIST.makeList;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import environment.RuntimeEnvironment.input
|
|||
import environment.RuntimeEnvironment.inputName
|
||||
import environment.RuntimeEnvironment.output
|
||||
import error.LispException
|
||||
import function.builtin.EVAL.eval
|
||||
import function.builtin.Eval.Companion.eval
|
||||
import parser.LispParser
|
||||
import sexpression.SExpression
|
||||
import java.io.InputStream
|
||||
|
|
|
@ -4,7 +4,7 @@ import error.CriticalLispException
|
|||
import function.FunctionNames
|
||||
import function.LispFunction
|
||||
import function.builtin.APPLY
|
||||
import function.builtin.EVAL
|
||||
import function.builtin.Eval
|
||||
import function.builtin.Exit
|
||||
import function.builtin.FUNCALL
|
||||
import function.builtin.FUSE
|
||||
|
@ -68,7 +68,7 @@ object FunctionTable {
|
|||
EQ::class.java,
|
||||
EQUAL::class.java,
|
||||
NumericEqual::class.java,
|
||||
EVAL::class.java,
|
||||
Eval::class.java,
|
||||
Exit::class.java,
|
||||
FIRST::class.java,
|
||||
FUNCALL::class.java,
|
||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException;
|
|||
import function.ArgumentValidator.DottedArgumentListException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.EVAL.UndefinedFunctionException;
|
||||
import function.builtin.Eval.UndefinedFunctionException;
|
||||
import org.junit.Test;
|
||||
import sexpression.Cons;
|
||||
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.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.EVAL.UndefinedSymbolException;
|
||||
import function.builtin.Eval.UndefinedSymbolException;
|
||||
import org.junit.Test;
|
||||
import sexpression.LispNumber;
|
||||
import table.SymbolTable;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package function.builtin.special;
|
||||
|
||||
import function.builtin.EVAL.UndefinedSymbolException;
|
||||
import function.builtin.Eval.UndefinedSymbolException;
|
||||
import org.junit.Test;
|
||||
import sexpression.LispNumber;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
|
|
@ -2,7 +2,7 @@ package function.builtin.special;
|
|||
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.EVAL.UndefinedSymbolException;
|
||||
import function.builtin.Eval.UndefinedSymbolException;
|
||||
import org.junit.Test;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException;
|
|||
import function.ArgumentValidator.DottedArgumentListException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.EVAL.UndefinedSymbolException;
|
||||
import function.builtin.Eval.UndefinedSymbolException;
|
||||
import org.junit.Test;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException;
|
|||
import function.ArgumentValidator.DottedArgumentListException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.EVAL.UndefinedSymbolException;
|
||||
import function.builtin.Eval.UndefinedSymbolException;
|
||||
import org.junit.Test;
|
||||
import sexpression.Cons;
|
||||
import sexpression.LispNumber;
|
||||
|
|
|
@ -4,7 +4,7 @@ import function.ArgumentValidator.BadArgumentTypeException
|
|||
import function.ArgumentValidator.DottedArgumentListException
|
||||
import function.ArgumentValidator.TooFewArgumentsException
|
||||
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.isLambdaExpression
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package function.builtin.special;
|
||||
|
||||
import function.builtin.EVAL.UndefinedSymbolException;
|
||||
import function.builtin.Eval.UndefinedSymbolException;
|
||||
import org.junit.Test;
|
||||
import sexpression.LispNumber;
|
||||
import testutil.SymbolAndFunctionCleaner;
|
||||
|
|
|
@ -3,7 +3,7 @@ package function.builtin.special;
|
|||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import function.ArgumentValidator.TooFewArgumentsException;
|
||||
import function.ArgumentValidator.TooManyArgumentsException;
|
||||
import function.builtin.EVAL.UndefinedSymbolException;
|
||||
import function.builtin.Eval.UndefinedSymbolException;
|
||||
import org.junit.Test;
|
||||
import sexpression.LispNumber;
|
||||
import table.SymbolTable;
|
||||
|
|
|
@ -15,7 +15,7 @@ abstract class SymbolAndFunctionCleaner {
|
|||
@Before
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
executionContext.clearContext()
|
||||
ExecutionContext.clearContext()
|
||||
FunctionTable.resetFunctionTable()
|
||||
additionalSetUp()
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ abstract class SymbolAndFunctionCleaner {
|
|||
@After
|
||||
@AfterEach
|
||||
fun tearDown() {
|
||||
executionContext.clearContext()
|
||||
ExecutionContext.clearContext()
|
||||
FunctionTable.resetFunctionTable()
|
||||
additionalTearDown()
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package testutil
|
|||
|
||||
import error.LispException
|
||||
import error.Severity.ERROR
|
||||
import function.builtin.EVAL.eval
|
||||
import function.builtin.Eval.Companion.eval
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import parser.LispParser
|
||||
import sexpression.Cons
|
||||
|
|
Loading…
Reference in New Issue