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); + } + +}