diff --git a/src/function/builtin/FUNCALL.java b/src/function/builtin/FUNCALL.java
index 3a68677..84811cd 100644
--- a/src/function/builtin/FUNCALL.java
+++ b/src/function/builtin/FUNCALL.java
@@ -1,23 +1,20 @@
package function.builtin;
-import function.LispFunction;
+import function.*;
import sexpression.*;
-/**
- * FUNCALL
represents the FUNCALL function in Lisp.
- */
public class FUNCALL extends LispFunction {
- public SExpression call(Cons argList) {
- // make sure we have received at least one argument
- if (argList.nullp()) {
- Cons originalSExpr = new Cons(new Symbol("FUNCALL"), argList);
+ private ArgumentValidator argumentValidator;
- throw new RuntimeException("too few arguments given to FUNCALL: " + originalSExpr);
- }
+ public FUNCALL() {
+ this.argumentValidator = new ArgumentValidator("FUNCALL");
+ this.argumentValidator.setMinimumNumberOfArguments(1);
+ }
- SExpression cdr = argList.getCdr();
- Cons applyArgs = new Cons(argList.getCar(), LIST.makeList(cdr));
+ public SExpression call(Cons argumentList) {
+ argumentValidator.validate(argumentList);
+ Cons applyArgs = new Cons(argumentList.getCar(), LIST.makeList(argumentList.getCdr()));
return APPLY.apply(applyArgs);
}
diff --git a/src/function/builtin/GREATERP.java b/src/function/builtin/GREATERP.java
index a4ed18a..974107f 100644
--- a/src/function/builtin/GREATERP.java
+++ b/src/function/builtin/GREATERP.java
@@ -1,49 +1,43 @@
package function.builtin;
-import function.LispFunction;
+import function.*;
import sexpression.*;
-/**
- * GREATERP
represents the '>' function in Lisp.
- */
public class GREATERP extends LispFunction {
- public SExpression call(Cons argList) {
- // make sure we have received at least one argument
- if (argList.nullp()) {
- Cons originalSExpr = new Cons(new Symbol(">"), argList);
+ private ArgumentValidator argumentValidator;
- throw new RuntimeException("too few arguments given to >: " + originalSExpr);
- }
+ public GREATERP() {
+ this.argumentValidator = new ArgumentValidator("GREATERP");
+ this.argumentValidator.setMinimumNumberOfArguments(2);
+ this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
+ }
- SExpression firstArg = argList.getCar();
- Cons argRest = (Cons) argList.getCdr();
+ public SExpression call(Cons argumentList) {
+ argumentValidator.validate(argumentList);
- // make sure that the first argument is a number
- if (firstArg.numberp()) {
- LispNumber num1 = (LispNumber) firstArg;
+ return callTailRecursive(argumentList);
+ }
- if (argRest.nullp()) {
- return Symbol.T;
- }
+ private SExpression callTailRecursive(Cons argumentList) {
+ Cons remainingArguments = (Cons) argumentList.getCdr();
- SExpression secondArg = argRest.getCar();
+ if (remainingArguments.nullp())
+ return Symbol.T;
- // make sure that the second argument is a number as well
- if (secondArg.numberp()) {
- LispNumber num2 = (LispNumber) secondArg;
+ SExpression firstArgument = argumentList.getCar();
+ SExpression secondArgument = remainingArguments.getCar();
+ LispNumber number1 = (LispNumber) firstArgument;
+ LispNumber number2 = (LispNumber) secondArgument;
- if (num1.getValue().compareTo(num2.getValue()) > 0) {
- return call(argRest);
- }
+ if (!isFirstGreater(number1, number2))
+ return Nil.getInstance();
- return Nil.getInstance();
- }
+ return callTailRecursive(remainingArguments);
+ }
- throw new RuntimeException(">: " + secondArg + " is not a number");
- }
-
- throw new RuntimeException(">: " + firstArg + " is not a number");
+ private boolean isFirstGreater(LispNumber number1, LispNumber number2) {
+ return number1.getValue().compareTo(number2.getValue()) > 0;
}
}
diff --git a/test/function/builtin/FUNCALLTester.java b/test/function/builtin/FUNCALLTester.java
new file mode 100644
index 0000000..71d4e06
--- /dev/null
+++ b/test/function/builtin/FUNCALLTester.java
@@ -0,0 +1,32 @@
+package function.builtin;
+
+import static testutil.TestUtilities.*;
+
+import org.junit.Test;
+
+import function.ArgumentValidator.TooFewArgumentsException;
+
+public class FUNCALLTester {
+
+ @Test
+ public void testFuncallWithNumbers() {
+ String input = "(funcall '+ 1 2 3)";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("6"));
+ }
+
+ @Test
+ public void testFuncallWithUserDefinedFunction() {
+ String defineUserFunction = "(defun x (n m) (+ n m))";
+ String input = "(funcall 'x 2 30)";
+
+ evaluateString(defineUserFunction);
+ assertSExpressionsMatch(evaluateString(input), parseString("32"));
+ }
+
+ @Test(expected = TooFewArgumentsException.class)
+ public void testFuncallWithTooFewArguments() {
+ evaluateString("(funcall)");
+ }
+
+}
diff --git a/test/function/builtin/GREATERPTester.java b/test/function/builtin/GREATERPTester.java
new file mode 100644
index 0000000..9de0446
--- /dev/null
+++ b/test/function/builtin/GREATERPTester.java
@@ -0,0 +1,49 @@
+package function.builtin;
+
+import static testutil.TestUtilities.*;
+
+import org.junit.Test;
+
+import function.ArgumentValidator.*;
+
+public class GREATERPTester {
+
+ @Test
+ public void testGreaterpWithTwoNumbers_ReturnsNil() {
+ String input = "(> 1 2)";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("nil"));
+ }
+
+ @Test
+ public void testGreaterpWithTwoNumbers_ReturnsT() {
+ String input = "(> 3 2)";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("t"));
+ }
+
+ @Test
+ public void testGreaterpWithManyNumbers_ReturnsNil() {
+ String input = "(> 4 3 2 5 1)";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("nil"));
+ }
+
+ @Test
+ public void testGreaterpWithManyNumbers_ReturnsT() {
+ String input = "(> 4 3 2 1 0)";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("t"));
+ }
+
+ @Test(expected = BadArgumentTypeException.class)
+ public void testGreaterpWithNonNumbers() {
+ evaluateString("(> 'x 'x)");
+ }
+
+ @Test(expected = TooFewArgumentsException.class)
+ public void testGreaterpWithTooFewArguments() {
+ evaluateString("(> 1)");
+ }
+
+}