diff --git a/src/function/builtin/cons/LENGTH.java b/src/function/builtin/cons/LENGTH.java
index 00bbbae..b32d5d1 100644
--- a/src/function/builtin/cons/LENGTH.java
+++ b/src/function/builtin/cons/LENGTH.java
@@ -2,62 +2,49 @@ package function.builtin.cons;
import java.math.BigInteger;
-import function.LispFunction;
+import function.*;
import sexpression.*;
-/**
- * LENGTH
represents the LENGTH function in Lisp.
- */
public class LENGTH extends LispFunction {
- /**
- * Returns the length of the given list.
- *
- * @param list
- * the list to determine the length of
- * @return the length of list
- */
public static int getLength(Cons list) {
LENGTH lengthFunction = new LENGTH();
- LispNumber length = lengthFunction.call(LIST.makeList(list));
+ LispNumber length = lengthFunction.callWithoutArgumentValidation(LIST.makeList(list));
return length.getValue().intValue(); // TODO - return BigInteger when all built-ins use
// ArgumentValidator
}
- public LispNumber call(Cons argList) {
- // make sure we have received at least one argument
- if (argList.nullp()) {
- Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
+ private ArgumentValidator argumentValidator;
- throw new RuntimeException("too few arguments given to LENGTH: " + originalSExpr);
- }
+ public LENGTH() {
+ this.argumentValidator = new ArgumentValidator("LENGTH");
+ this.argumentValidator.setExactNumberOfArguments(1);
+ this.argumentValidator.setEveryArgumentExpectedType(Cons.class);
+ }
- SExpression argCar = argList.getCar();
- SExpression argCdr = argList.getCdr();
+ public LispNumber call(Cons argumentList) {
+ argumentValidator.validate(argumentList);
- // make sure we have received only one argument
- if (!argCdr.nullp()) {
- Cons originalSExpr = new Cons(new Symbol("LENGTH"), argList);
+ return callTailRecursive(BigInteger.ZERO, argumentList);
+ }
- throw new RuntimeException("too many arguments given to LENGTH: " + originalSExpr);
- }
+ private LispNumber callWithoutArgumentValidation(Cons argumentList) {
+ return callTailRecursive(BigInteger.ZERO, argumentList);
+ }
- // make sure that the argument is a list
- if (argCar.listp()) {
- Cons arg = (Cons) argCar;
+ private LispNumber callTailRecursive(BigInteger accumulatedLength, Cons argumentList) {
+ Cons list = (Cons) argumentList.getCar();
+ Cons restOfList = LIST.makeList(list.getCdr());
- if (arg.nullp()) {
- return new LispNumber(BigInteger.ZERO);
- }
+ if (list.nullp())
+ return new LispNumber(accumulatedLength);
- Cons cdr = LIST.makeList(arg.getCdr());
- LispNumber cdrLength = call(cdr);
+ return callTailRecursive(increment(accumulatedLength), restOfList);
+ }
- return new LispNumber(BigInteger.ONE.add(cdrLength.getValue()));
- }
-
- throw new RuntimeException("LENGTH: a proper list must not end with " + argCar);
+ private BigInteger increment(BigInteger number) {
+ return number.add(BigInteger.ONE);
}
}
diff --git a/src/function/builtin/math/DIVIDE.java b/src/function/builtin/math/DIVIDE.java
index 394dfa7..e2bd744 100644
--- a/src/function/builtin/math/DIVIDE.java
+++ b/src/function/builtin/math/DIVIDE.java
@@ -17,7 +17,7 @@ public class DIVIDE extends LispFunction {
this.mathFunction = new MathFunction(this::getReciprocal, this::divide);
}
- public SExpression call(Cons argumentList) {
+ public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(argumentList);
diff --git a/src/function/builtin/math/MINUS.java b/src/function/builtin/math/MINUS.java
index 807e5a6..a84f101 100644
--- a/src/function/builtin/math/MINUS.java
+++ b/src/function/builtin/math/MINUS.java
@@ -17,7 +17,7 @@ public class MINUS extends LispFunction {
this.mathFunction = new MathFunction(this::additiveInverse, this::subtract);
}
- public SExpression call(Cons argumentList) {
+ public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(argumentList);
diff --git a/src/function/builtin/math/MULTIPLY.java b/src/function/builtin/math/MULTIPLY.java
index c48981d..75de4d6 100644
--- a/src/function/builtin/math/MULTIPLY.java
+++ b/src/function/builtin/math/MULTIPLY.java
@@ -14,7 +14,7 @@ public class MULTIPLY extends LispFunction {
this.mathFunction = new MathFunction(number -> number, this::multiply);
}
- public SExpression call(Cons argumentList) {
+ public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(new Cons(LispNumber.ONE, argumentList));
diff --git a/src/function/builtin/math/MathFunction.java b/src/function/builtin/math/MathFunction.java
index e211e5a..5a5d443 100644
--- a/src/function/builtin/math/MathFunction.java
+++ b/src/function/builtin/math/MathFunction.java
@@ -15,7 +15,7 @@ class MathFunction {
this.multipleValueOperation = multipleValueOperation;
}
- public SExpression callTailRecursive(Cons argumentList) {
+ public LispNumber callTailRecursive(Cons argumentList) {
Cons remainingArguments = (Cons) argumentList.getCdr();
SExpression firstArgument = argumentList.getCar();
LispNumber number1 = (LispNumber) firstArgument;
diff --git a/src/function/builtin/math/PLUS.java b/src/function/builtin/math/PLUS.java
index a96a450..3efb2a5 100644
--- a/src/function/builtin/math/PLUS.java
+++ b/src/function/builtin/math/PLUS.java
@@ -14,7 +14,7 @@ public class PLUS extends LispFunction {
this.mathFunction = new MathFunction(number -> number, this::add);
}
- public SExpression call(Cons argumentList) {
+ public LispNumber call(Cons argumentList) {
argumentValidator.validate(argumentList);
return mathFunction.callTailRecursive(new Cons(LispNumber.ZERO, argumentList));
diff --git a/test/function/builtin/cons/LENGTHTester.java b/test/function/builtin/cons/LENGTHTester.java
new file mode 100644
index 0000000..5786680
--- /dev/null
+++ b/test/function/builtin/cons/LENGTHTester.java
@@ -0,0 +1,47 @@
+package function.builtin.cons;
+
+import static testutil.TestUtilities.*;
+
+import org.junit.Test;
+
+import function.ArgumentValidator.*;
+
+public class LENGTHTester {
+
+ @Test
+ public void testLengthOfNil() {
+ String input = "(length '())";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("0"));
+ }
+
+ @Test
+ public void testLengthOfListOfOneElement() {
+ String input = "(length '(1))";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("1"));
+ }
+
+ @Test
+ public void testLengthOfListOfManyElements() {
+ String input = "(length '(1 2 3 4 5))";
+
+ assertSExpressionsMatch(evaluateString(input), parseString("5"));
+ }
+
+ @Test(expected = BadArgumentTypeException.class)
+ public void testLengthWithNonList() {
+ evaluateString("(length 'x)");
+ }
+
+ @Test(expected = TooManyArgumentsException.class)
+ public void testLengthWithTooManyArguments() {
+ evaluateString("(length '(1 2) '(1 2))");
+ }
+
+ @Test(expected = TooFewArgumentsException.class)
+ public void testLengthWithTooFewArguments() {
+ evaluateString("(length)");
+ }
+
+}