diff --git a/src/function/builtin/APPLY.java b/src/function/builtin/APPLY.java
index 8e148f9..da9623b 100644
--- a/src/function/builtin/APPLY.java
+++ b/src/function/builtin/APPLY.java
@@ -3,65 +3,46 @@ package function.builtin;
import function.*;
import sexpression.*;
-/**
- * APPLY
represents the APPLY function in Lisp.
- */
public class APPLY extends LispFunction {
- /**
- * Call APPLY with the specified argument list.
- *
- * @param argList
- * the list of arguments to be sent to APPLY (MUST BE A PROPER LIST)
- * @return the result of evaluating APPLY on argList
- */
+ private static final int NUMBER_OF_ARGUMENTS = 2;
+ private ArgumentValidator argumentValidator;
+
public static SExpression apply(Cons argList) {
return new APPLY().call(argList);
}
- // The number of arguments that APPLY takes.
- private static final int NUM_ARGS = 2;
+ public APPLY() {
+ this.argumentValidator = new ArgumentValidator("APPLY");
+ this.argumentValidator.setExactNumberOfArguments(NUMBER_OF_ARGUMENTS);
+ }
public SExpression call(Cons argList) {
- // retrieve the number of arguments passed to APPLY
- int argListLength = LENGTH.getLength(argList);
+ argumentValidator.validate(argList);
- // make sure we have received the proper number of arguments
- if (argListLength != NUM_ARGS) {
- Cons originalSExpr = new Cons(new Symbol("APPLY"), argList);
- String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to APPLY: "
- + originalSExpr;
-
- throw new RuntimeException(errMsg);
- }
-
- SExpression car = argList.getCar(); // function name
Cons cdr = (Cons) argList.getCdr();
- SExpression cadr = cdr.getCar(); // argument list
+ SExpression functionName = argList.getCar();
+ SExpression argumentList = cdr.getCar();
- // make sure the second argument is a list
- if (cadr.listp()) {
- LispFunction function = EVAL.lookupFunction(car.toString());
+ if (argumentList.listp()) {
+ LispFunction function = EVAL.lookupFunction(functionName.toString());
if (function == null) {
- // check if the car of the list is a lambda expression
- if (car.functionp()) {
- function = ((LambdaExpression) car).getFunction();
- } else if (LAMBDA.isLambdaExpression(car)) {
- Cons lexpr = (Cons) car;
+ if (functionName.functionp()) {
+ function = ((LambdaExpression) functionName).getFunction();
+ } else if (LAMBDA.isLambdaExpression(functionName)) {
+ Cons lexpr = (Cons) functionName;
function = LAMBDA.createFunction(lexpr);
} else {
- throw new RuntimeException("undefined function " + car);
+ throw new RuntimeException("undefined function " + functionName);
}
}
- // apply the given function to the given argument list
- return function.call((Cons) cadr);
+ return function.call((Cons) argumentList);
}
- // the second argument is not a list
- throw new RuntimeException("APPLY: " + cadr + " is not a list");
+ throw new RuntimeException("APPLY: " + argumentList + " is not a list");
}
}
diff --git a/src/function/builtin/ATOM.java b/src/function/builtin/ATOM.java
index d8d39c7..785f680 100644
--- a/src/function/builtin/ATOM.java
+++ b/src/function/builtin/ATOM.java
@@ -1,32 +1,23 @@
package function.builtin;
-import function.LispFunction;
+import function.*;
import sexpression.*;
-/**
- * ATOM
represents the ATOM function in Lisp.
- */
public class ATOM extends LispFunction {
- // The number of arguments that ATOM takes.
- private static final int NUM_ARGS = 1;
+ private static final int NUMBER_OF_ARGUMENTS = 1;
+ private ArgumentValidator argumentValidator;
- public SExpression call(Cons argList) {
- // retrieve the number of arguments passed to ATOM
- int argListLength = LENGTH.getLength(argList);
+ public ATOM() {
+ this.argumentValidator = new ArgumentValidator("ATOM");
+ this.argumentValidator.setExactNumberOfArguments(NUMBER_OF_ARGUMENTS);
+ }
- // make sure we have received the proper number of arguments
- if (argListLength != NUM_ARGS) {
- Cons originalSExpr = new Cons(new Symbol("ATOM"), argList);
- String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to ATOM: "
- + originalSExpr;
+ public SExpression call(Cons argumentList) {
+ argumentValidator.validate(argumentList);
+ SExpression argument = argumentList.getCar();
- throw new RuntimeException(errMsg);
- }
-
- SExpression arg = argList.getCar();
-
- return (arg.atomp() ? Symbol.T : Nil.getUniqueInstance());
+ return argument.atomp() ? Symbol.T : Nil.getUniqueInstance();
}
}
diff --git a/test/function/UserDefinedFunctionTester.java b/test/function/UserDefinedFunctionTester.java
index ae8e69d..4b75e43 100644
--- a/test/function/UserDefinedFunctionTester.java
+++ b/test/function/UserDefinedFunctionTester.java
@@ -1,6 +1,7 @@
package function;
import static org.junit.Assert.assertEquals;
+import static testutil.TestUtilities.*;
import org.junit.Test;
@@ -35,7 +36,7 @@ public class UserDefinedFunctionTester {
new Cons(Nil.getUniqueInstance(),
new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance())));
- assertEquals(expected.toString(), function.getLambdaExpression().toString());
+ assertSExpressionsMatch(expected, function.getLambdaExpression());
}
@Test
@@ -44,7 +45,7 @@ public class UserDefinedFunctionTester {
SExpression argument = new LispNumber(23);
Cons argumentList = new Cons(argument, Nil.getUniqueInstance());
- assertEquals(argument.toString(), function.call(argumentList).toString());
+ assertSExpressionsMatch(argument, function.call(argumentList));
}
@Test(expected = TooManyArgumentsException.class)
diff --git a/test/function/builtin/APPLYTester.java b/test/function/builtin/APPLYTester.java
new file mode 100644
index 0000000..beac56d
--- /dev/null
+++ b/test/function/builtin/APPLYTester.java
@@ -0,0 +1,61 @@
+package function.builtin;
+
+import static testutil.TestUtilities.*;
+
+import org.junit.Test;
+
+import function.ArgumentValidator.*;
+import sexpression.Cons;
+
+public class APPLYTester {
+
+ @Test
+ public void testApply() {
+ String input = "(apply '+ '(1 2 3))";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("6"));
+ }
+
+ @Test
+ public void testApplyWithLambdaExpression() {
+ String input = "(apply (lambda (x) (+ x 1)) '(25))";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("26"));
+ }
+
+ @Test
+ public void testApplyWithQuotedLambdaExpression() {
+ String input = "(apply '(lambda (x) (+ x 1)) '(25))";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("26"));
+ }
+
+ @Test
+ public void testStaticApplyCall() {
+ String argumentList = "(+ (25 10))";
+ Cons parsedArgumentList = (Cons) parseString(argumentList);
+
+ assertSExpressionsMatch(APPLY.apply(parsedArgumentList), parseString("35"));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testApplyWithUndefinedFunction() {
+ evaluateString("(apply 'f '(1 2 3))");
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testApplyWithNonListSecondArgument() {
+ evaluateString("(apply '+ '2)");
+ }
+
+ @Test(expected = TooManyArgumentsException.class)
+ public void testApplyWithTooManyArguments() {
+ evaluateString("(apply '1 '2 '3)");
+ }
+
+ @Test(expected = TooFewArgumentsException.class)
+ public void testApplyWithTooFewArguments() {
+ evaluateString("(apply '1)");
+ }
+
+}
diff --git a/test/function/builtin/ATOMTester.java b/test/function/builtin/ATOMTester.java
new file mode 100644
index 0000000..dac4db0
--- /dev/null
+++ b/test/function/builtin/ATOMTester.java
@@ -0,0 +1,35 @@
+package function.builtin;
+
+import static testutil.TestUtilities.*;
+
+import org.junit.Test;
+
+import function.ArgumentValidator.*;
+
+public class ATOMTester {
+
+ @Test
+ public void testAtom_ReturnsTrue() {
+ String input = "(atom 'a)";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("T"));
+ }
+
+ @Test
+ public void testAtom_ReturnsFalse() {
+ String input = "(atom '(1 2 3))";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("()"));
+ }
+
+ @Test(expected = TooManyArgumentsException.class)
+ public void testApplyWithTooManyArguments() {
+ evaluateString("(atom '1 '2)");
+ }
+
+ @Test(expected = TooFewArgumentsException.class)
+ public void testApplyWithTooFewArguments() {
+ evaluateString("(atom)");
+ }
+
+}
diff --git a/test/parser/LispParserTester.java b/test/parser/LispParserTester.java
index 241d294..7df9350 100644
--- a/test/parser/LispParserTester.java
+++ b/test/parser/LispParserTester.java
@@ -1,7 +1,7 @@
package parser;
import static org.junit.Assert.*;
-import static parser.SExpressionTypeAssertions.*;
+import static testutil.SExpressionTypeAssertions.*;
import static testutil.TestUtilities.createIOExceptionThrowingInputStream;
import static testutil.TestUtilities.createInputStreamFromString;
diff --git a/test/parser/SExpressionTypeAssertions.java b/test/testutil/SExpressionTypeAssertions.java
similarity index 96%
rename from test/parser/SExpressionTypeAssertions.java
rename to test/testutil/SExpressionTypeAssertions.java
index a173b8f..6d8acf8 100644
--- a/test/parser/SExpressionTypeAssertions.java
+++ b/test/testutil/SExpressionTypeAssertions.java
@@ -1,9 +1,8 @@
-package parser;
+package testutil;
import static org.junit.Assert.*;
-import sexpression.Nil;
-import sexpression.SExpression;
+import sexpression.*;
public final class SExpressionTypeAssertions {
diff --git a/test/testutil/TestUtilities.java b/test/testutil/TestUtilities.java
index 4081007..4e88dd3 100644
--- a/test/testutil/TestUtilities.java
+++ b/test/testutil/TestUtilities.java
@@ -1,13 +1,19 @@
package testutil;
+import static org.junit.Assert.assertEquals;
+
import java.io.*;
+import function.builtin.EVAL;
+import parser.LispParser;
+import sexpression.SExpression;
+
public final class TestUtilities {
public static InputStream createInputStreamFromString(String string) {
return new ByteArrayInputStream(string.getBytes());
}
-
+
public static InputStream createIOExceptionThrowingInputStream() {
return new InputStream() {
@@ -17,4 +23,18 @@ public final class TestUtilities {
};
}
+ public static SExpression evaluateString(String input) {
+ return EVAL.eval(parseString(input));
+ }
+
+ public static SExpression parseString(String input) {
+ InputStream stringInputStream = TestUtilities.createInputStreamFromString(input);
+
+ return new LispParser(stringInputStream, "testFile").getNextSExpression();
+ }
+
+ public static void assertSExpressionsMatch(SExpression one, SExpression two) {
+ assertEquals(one.toString(), two.toString());
+ }
+
}