diff --git a/src/function/ArgumentValidator.java b/src/function/ArgumentValidator.java index 115b3c2..545af26 100644 --- a/src/function/ArgumentValidator.java +++ b/src/function/ArgumentValidator.java @@ -173,22 +173,25 @@ public class ArgumentValidator { private static final long serialVersionUID = 1L; private String functionName; private String argument; - private Class expected; + private Class expectedType; public BadArgumentTypeException(String functionName, SExpression argument, - Class expected) { + Class expectedType) { this.functionName = functionName; this.argument = argument.toString(); - this.expected = expected; + this.expectedType = expectedType; } @Override public String getMessage() { - DisplayName displayName = expected.getAnnotation(DisplayName.class); - String expectedType = (displayName == null) ? "unknown" : displayName.value(); - return MessageFormat.format("{0}: {1} is not the expected type of ''{2}''", functionName, argument, - expectedType); + getExpectedTypeName()); + } + + private String getExpectedTypeName() { + DisplayName displayName = expectedType.getAnnotation(DisplayName.class); + + return (displayName == null) ? "unknown" : displayName.value(); } } diff --git a/src/function/UserDefinedFunction.java b/src/function/UserDefinedFunction.java index b276dde..a9fc27b 100644 --- a/src/function/UserDefinedFunction.java +++ b/src/function/UserDefinedFunction.java @@ -38,9 +38,7 @@ public class UserDefinedFunction extends LispFunction { // bind the values of the arguments to the formal parameter names for (String param : formalParameters) { SExpression currentArg = argList.getCar(); - environment.put(param, currentArg); - argList = (Cons) argList.getCdr(); } diff --git a/src/function/builtin/CONS.java b/src/function/builtin/CONS.java index 2913115..1703ec8 100644 --- a/src/function/builtin/CONS.java +++ b/src/function/builtin/CONS.java @@ -1,38 +1,25 @@ package function.builtin; -import function.LispFunction; +import function.*; import sexpression.*; -/** - * CONS represents the CONS function in Lisp. - */ public class CONS extends LispFunction { - // The number of arguments that CONS takes. - private static final int NUM_ARGS = 2; + private ArgumentValidator argumentValidator; - public Cons call(Cons argList) { - // retrieve the number of arguments passed to CONS - int argListLength = LENGTH.getLength(argList); + public CONS() { + this.argumentValidator = new ArgumentValidator("CONS"); + this.argumentValidator.setExactNumberOfArguments(2); + } - // make sure we have received the proper number of arguments - if (argListLength != NUM_ARGS) { - Cons originalSExpr = new Cons(new Symbol("CONS"), argList); - String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to CONS: " - + originalSExpr; + public Cons call(Cons argumentList) { + argumentValidator.validate(argumentList); - throw new RuntimeException(errMsg); - } + Cons cdr = (Cons) argumentList.getCdr(); + SExpression firstArgument = argumentList.getCar(); + SExpression secondArgument = cdr.getCar(); - // the car of the CONS cell we are going to create - SExpression argOne = argList.getCar(); - - Cons cdr = (Cons) argList.getCdr(); - - // the cdr of the CONS cell we are going to create - SExpression argTwo = cdr.getCar(); - - return new Cons(argOne, argTwo); + return new Cons(firstArgument, secondArgument); } } diff --git a/src/function/builtin/DEFUN.java b/src/function/builtin/DEFUN.java index ad167eb..6498b84 100644 --- a/src/function/builtin/DEFUN.java +++ b/src/function/builtin/DEFUN.java @@ -5,34 +5,21 @@ import java.util.HashMap; import function.*; import sexpression.*; -/** - * DEFUN represents the DEFUN form in Lisp. - */ public class DEFUN extends LispFunction { - // The minimum number of arguments that DEFUN takes. - private static final int MIN_ARGS = 3; + private ArgumentValidator argumentValidator; - public SExpression call(Cons argList) { - // retrieve the number of arguments passed to DEFUN - int argListLength = LENGTH.getLength(argList); + public DEFUN() { + this.argumentValidator = new ArgumentValidator("DEFUN"); + this.argumentValidator.setMinimumNumberOfArguments(3); + this.argumentValidator.setFirstArgumentExpectedType(Symbol.class); + } - // make sure we have received the proper number of arguments - if (argListLength < MIN_ARGS) { - Cons originalSExpr = new Cons(new Symbol("DEFUN"), argList); - String errMsg = "too few arguments given to DEFUN: " + originalSExpr; + public SExpression call(Cons argumentList) { + argumentValidator.validate(argumentList); - throw new RuntimeException(errMsg); - } - - SExpression name = argList.getCar(); // name of the function - - // make sure the function name is a symbol - if (!name.symbolp()) { - throw new RuntimeException("DEFUN: " + name + " is not a symbol"); - } - - Cons cdr = (Cons) argList.getCdr(); + Cons cdr = (Cons) argumentList.getCdr(); + SExpression name = argumentList.getCar(); // name of the function SExpression cadr = cdr.getCar(); // make sure the list of arguments (lambda list) is a proper list @@ -60,11 +47,6 @@ public class DEFUN extends LispFunction { return name; } - /** - * Determine if the arguments passed to this Lisp function should be evaluated. - * - * @return false - */ public boolean evaluateArguments() { return false; } diff --git a/test/function/builtin/CARTester.java b/test/function/builtin/CARTester.java index d084e7d..e619370 100644 --- a/test/function/builtin/CARTester.java +++ b/test/function/builtin/CARTester.java @@ -36,16 +36,12 @@ public class CARTester { @Test(expected = TooManyArgumentsException.class) public void testCarWithTooManyArguments() { - String input = "(car '(1 2) '(1 2) \"oh\")"; - - assertSExpressionsMatch(evaluateString(input), parseString("1")); + evaluateString("(car '(1 2) '(1 2) \"oh\")"); } @Test(expected = TooFewArgumentsException.class) public void testCarWithTooFewArguments() { - String input = "(car)"; - - assertSExpressionsMatch(evaluateString(input), parseString("1")); + evaluateString("(car)"); } } diff --git a/test/function/builtin/CDRTester.java b/test/function/builtin/CDRTester.java index 7fec962..36b135c 100644 --- a/test/function/builtin/CDRTester.java +++ b/test/function/builtin/CDRTester.java @@ -36,16 +36,12 @@ public class CDRTester { @Test(expected = TooManyArgumentsException.class) public void testCdrWithTooManyArguments() { - String input = "(cdr '(1 2) '(1 2) \"oh\")"; - - assertSExpressionsMatch(evaluateString(input), parseString("1")); + evaluateString("(cdr '(1 2) '(1 2) \"oh\")"); } @Test(expected = TooFewArgumentsException.class) - public void testCdrWithTooFewArguments() { - String input = "(cdr)"; - - assertSExpressionsMatch(evaluateString(input), parseString("1")); + public void testCdrWithTooFewArguments() { + evaluateString("(cdr)"); } } diff --git a/test/function/builtin/CONSTester.java b/test/function/builtin/CONSTester.java new file mode 100644 index 0000000..3ad96b5 --- /dev/null +++ b/test/function/builtin/CONSTester.java @@ -0,0 +1,61 @@ +package function.builtin; + +import static testutil.TestUtilities.*; + +import org.junit.Test; + +import function.ArgumentValidator.*; +import sexpression.*; + +public class CONSTester { + + @Test + public void testConsWithNilValues() { + String input = "(cons () nil)"; + + assertSExpressionsMatch(evaluateString(input), parseString("(())")); + } + + @Test + public void testConsWithTwoSymbols() { + String input = "(cons 'a 'b)"; + + assertSExpressionsMatch(evaluateString(input), new Cons(new Symbol("A"), new Symbol("B"))); + } + + @Test + public void testConsWithListAsCdr() { + String input = "(cons 1 '(2 3))"; + + assertSExpressionsMatch(evaluateString(input), parseString("(1 2 3)")); + } + @Test + public void testConsWithTwoLists() { + String input = "(cons '(1 2) '(3 4))"; + + assertSExpressionsMatch(evaluateString(input), parseString("((1 2) 3 4)")); + } + + + @Test + public void testConsWithList() { + String input = "(cons nil '(2 3))"; + + assertSExpressionsMatch(evaluateString(input), parseString("(nil 2 3)")); + } + + @Test(expected = TooManyArgumentsException.class) + public void testConsWithTooManyArguments() { + String input = "(cons 1 2 3)"; + + evaluateString(input); + } + + @Test(expected = TooFewArgumentsException.class) + public void testConsWithTooFewArguments() { + String input = "(cons 1)"; + + evaluateString(input); + } + +} diff --git a/test/function/builtin/DEFUNTester.java b/test/function/builtin/DEFUNTester.java new file mode 100644 index 0000000..1318a45 --- /dev/null +++ b/test/function/builtin/DEFUNTester.java @@ -0,0 +1,29 @@ +package function.builtin; + +import static testutil.TestUtilities.*; + +import org.junit.Test; + +import function.ArgumentValidator.*; + +public class DEFUNTester { + + @Test + public void testDefun() { + String input = "(defun f () nil)"; + + assertSExpressionsMatch(evaluateString(input), parseString("f")); + assertSExpressionsMatch(evaluateString("(f)"), parseString("()")); + } + + @Test(expected = BadArgumentTypeException.class) + public void testDefunWithNonSymbolName() { + evaluateString("(defun 1 () ())"); + } + + @Test(expected = TooFewArgumentsException.class) + public void testApplyWithTooFewArguments() { + evaluateString("(defun 1 ())"); + } + +}