diff --git a/src/function/builtin/SYMBOL_FUNCTION.java b/src/function/builtin/SYMBOL_FUNCTION.java
index e4eac3a..42c310b 100644
--- a/src/function/builtin/SYMBOL_FUNCTION.java
+++ b/src/function/builtin/SYMBOL_FUNCTION.java
@@ -1,61 +1,50 @@
package function.builtin;
+import java.text.MessageFormat;
+
+import error.LispException;
import function.*;
-import function.builtin.cons.LENGTH;
import sexpression.*;
-/**
- * SYMBOL_FUNCTION
represents the SYMBOL-FUNCTION function in
- * Lisp.
- */
public class SYMBOL_FUNCTION extends LispFunction {
- // The number of arguments that SYMBOL-FUNCTION takes.
- private static final int NUM_ARGS = 1;
+ private ArgumentValidator argumentValidator;
- public SExpression call(Cons argList) {
- // retrieve the number of arguments passed to SYMBOL-FUNCTION
- int argListLength = LENGTH.getLength(argList);
+ public SYMBOL_FUNCTION() {
+ this.argumentValidator = new ArgumentValidator("SYMBOL-FUNCTION");
+ this.argumentValidator.setExactNumberOfArguments(1);
+ this.argumentValidator.setEveryArgumentExpectedType(Symbol.class);
+ }
- // make sure we have received the proper number of arguments
- if (argListLength != NUM_ARGS) {
- Cons originalSExpr = new Cons(new Symbol("SYMBOL-FUNCTION"),
- argList);
- String errMsg = "too " +
- ((argListLength > NUM_ARGS) ? "many" : "few") +
- " arguments given to SYMBOL-FUNCTION: " +
- originalSExpr;
+ public SExpression call(Cons argumentList) {
+ argumentValidator.validate(argumentList);
- throw new RuntimeException(errMsg);
+ SExpression symbol = argumentList.getCar();
+ LispFunction function = EVAL.lookupFunction(symbol.toString());
+
+ if (function != null) {
+ if (function instanceof UserDefinedFunction)
+ return ((UserDefinedFunction) function).getLambdaExpression();
+
+ return new Symbol(MessageFormat.format("#", symbol.toString()));
+ }
+
+ throw new UndefinedSymbolFunctionException(symbol);
+ }
+
+ public static class UndefinedSymbolFunctionException extends LispException {
+
+ private static final long serialVersionUID = 1L;
+ private SExpression function;
+
+ public UndefinedSymbolFunctionException(SExpression function) {
+ this.function = function;
}
- SExpression arg = argList.getCar();
-
- // make sure the argument is a symbol
- if (arg.symbolp()) {
- LispFunction function = EVAL.lookupFunction(arg.toString());
-
- // make sure the function actually exists
- if (function != null) {
- if (function instanceof UserDefinedFunction) {
- // this is a user-defined function
-
- UserDefinedFunction udFunction = (UserDefinedFunction) function;
-
- return udFunction.getLambdaExpression();
- }
-
- // this is a built-in function
-
- return new Symbol("SUBR-" + arg.toString());
- }
-
- throw new RuntimeException("SYMBOL-FUNCTION: undefined function " +
- arg);
+ @Override
+ public String getMessage() {
+ return MessageFormat.format("SYMBOL-FUNCTION: undefined function: {0}", function);
}
-
- throw new RuntimeException("SYMBOL-FUNCTION: " + arg +
- " is not a symbol");
}
}
diff --git a/test/function/builtin/EXITTester.java b/test/function/builtin/EXITTester.java
new file mode 100644
index 0000000..f0e8d0a
--- /dev/null
+++ b/test/function/builtin/EXITTester.java
@@ -0,0 +1,48 @@
+package function.builtin;
+
+import static org.junit.Assert.*;
+import static testutil.TestUtilities.evaluateString;
+
+import java.util.*;
+
+import org.junit.*;
+
+import environment.Environment;
+import function.ArgumentValidator.TooManyArgumentsException;
+
+public class EXITTester {
+
+ private static final String TERMINATED = "terminated";
+ private Set indicatorSet;
+
+ private void assertTerminated() {
+ assertTrue(indicatorSet.contains(TERMINATED));
+ }
+
+ private void assertNotTerminated() {
+ assertFalse(indicatorSet.contains(TERMINATED));
+ }
+
+ @Before
+ public void setUp() {
+ indicatorSet = new HashSet<>();
+ Environment.getInstance().setTerminationFunction(() -> indicatorSet.add(TERMINATED));
+ }
+
+ @Test
+ public void exitWorks() {
+ evaluateString("(exit)");
+ assertTerminated();
+ }
+
+ @Test
+ public void exitNotCalled_IndicatorSetIsClean() {
+ assertNotTerminated();
+ }
+
+ @Test(expected = TooManyArgumentsException.class)
+ public void testExitWithTooManyArguments() {
+ evaluateString("(exit 1)");
+ }
+
+}
diff --git a/test/function/builtin/SYMBOL_FUNCTIONTester.java b/test/function/builtin/SYMBOL_FUNCTIONTester.java
new file mode 100644
index 0000000..593fbe0
--- /dev/null
+++ b/test/function/builtin/SYMBOL_FUNCTIONTester.java
@@ -0,0 +1,60 @@
+package function.builtin;
+
+import static org.junit.Assert.*;
+import static testutil.TestUtilities.evaluateString;
+
+import org.junit.Test;
+
+import function.ArgumentValidator.*;
+import function.builtin.SYMBOL_FUNCTION.UndefinedSymbolFunctionException;
+import sexpression.Nil;
+
+public class SYMBOL_FUNCTIONTester {
+
+ @Test
+ public void testSymbolFunction_BuiltinFunction() {
+ String input = "(symbol-function '+)";
+
+ assertEquals("#", evaluateString(input).toString());
+ }
+
+ @Test
+ public void testSymbolFunction_UserDefinedFunction() {
+ String defineUserFunction = "(defun y (n m) (+ n m))";
+ String input = "(symbol-function 'y)";
+
+ evaluateString(defineUserFunction);
+ assertEquals("(Y (N M) (+ N M))", evaluateString(input).toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testSymbolFunction_NonFunction() {
+ String input = "(symbol-function 'a)";
+
+ evaluateString(input);
+ }
+
+ @Test(expected = BadArgumentTypeException.class)
+ public void testSymbolFunctionWithBadArgumentType() {
+ evaluateString("(symbol-function 2)");
+ }
+
+ @Test(expected = TooManyArgumentsException.class)
+ public void testSymbolFunctionWithTooManyArguments() {
+ evaluateString("(symbol-function 'a 'b)");
+ }
+
+ @Test(expected = TooFewArgumentsException.class)
+ public void testSymbolFunctionWithTooFewArguments() {
+ evaluateString("(symbol-function)");
+ }
+
+ @Test
+ public void undefinedSymbolFunctionException_HasMessageText() {
+ UndefinedSymbolFunctionException e = new UndefinedSymbolFunctionException(Nil.getInstance());
+
+ assertNotNull(e.getMessage());
+ assertTrue(e.getMessage().length() > 0);
+ }
+
+}