Added more unit tests and refactored the code for several built in functions
This commit is contained in:
parent
e543de6f12
commit
ba59b2a8cf
|
@ -2,54 +2,46 @@ package function.builtin;
|
|||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import function.LispFunction;
|
||||
import function.*;
|
||||
import sexpression.*;
|
||||
|
||||
/**
|
||||
* <code>MINUS</code> represents the '-' function in Lisp.
|
||||
*/
|
||||
public class MINUS 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 MINUS() {
|
||||
this.argumentValidator = new ArgumentValidator("-");
|
||||
this.argumentValidator.setMinimumNumberOfArguments(1);
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
}
|
||||
|
||||
SExpression argFirst = 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 (argFirst.numberp()) {
|
||||
LispNumber num1 = (LispNumber) argFirst;
|
||||
return callTailRecursive(argumentList);
|
||||
}
|
||||
|
||||
if (argRest.nullp()) {
|
||||
// there is only one argument, so return the additive
|
||||
// inverse of the number
|
||||
return new LispNumber(BigInteger.ZERO.subtract(num1.getValue()));
|
||||
}
|
||||
private SExpression callTailRecursive(Cons argumentList) {
|
||||
Cons remainingArguments = (Cons) argumentList.getCdr();
|
||||
SExpression firstArgument = argumentList.getCar();
|
||||
LispNumber number1 = (LispNumber) firstArgument;
|
||||
|
||||
SExpression argSecond = argRest.getCar();
|
||||
if (remainingArguments.nullp())
|
||||
return additiveInverse(number1);
|
||||
|
||||
// make sure that the next argument is a number as well
|
||||
if (argSecond.numberp()) {
|
||||
LispNumber num2 = (LispNumber) argSecond;
|
||||
LispNumber difference = new LispNumber(num1.getValue().subtract(num2.getValue()));
|
||||
SExpression argCddr = argRest.getCdr();
|
||||
SExpression secondArgument = remainingArguments.getCar();
|
||||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
LispNumber difference = new LispNumber(number1.getValue().subtract(number2.getValue()));
|
||||
SExpression remainingNumbers = remainingArguments.getCdr();
|
||||
|
||||
if (argCddr.consp()) {
|
||||
return call(new Cons(difference, argCddr));
|
||||
}
|
||||
if (!remainingNumbers.consp())
|
||||
return difference;
|
||||
|
||||
return difference;
|
||||
}
|
||||
return callTailRecursive(new Cons(difference, remainingNumbers));
|
||||
}
|
||||
|
||||
throw new RuntimeException("-: " + argSecond + " is not a number");
|
||||
}
|
||||
|
||||
throw new RuntimeException("-: " + argFirst + " is not a number");
|
||||
private LispNumber additiveInverse(LispNumber number) {
|
||||
return new LispNumber(BigInteger.ZERO.subtract(number.getValue()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,31 +1,40 @@
|
|||
package function.builtin;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import function.LispFunction;
|
||||
import function.*;
|
||||
import sexpression.*;
|
||||
|
||||
/**
|
||||
* <code>MULTIPLY</code> represents the '*' function in Lisp.
|
||||
*/
|
||||
public class MULTIPLY extends LispFunction {
|
||||
|
||||
public LispNumber call(Cons argList) {
|
||||
if (argList.nullp()) {
|
||||
return new LispNumber(BigInteger.ONE);
|
||||
}
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
SExpression argFirst = argList.getCar();
|
||||
Cons argRest = (Cons) argList.getCdr();
|
||||
public MULTIPLY() {
|
||||
this.argumentValidator = new ArgumentValidator("*");
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
}
|
||||
|
||||
if (argFirst.numberp()) {
|
||||
LispNumber num1 = (LispNumber) argFirst;
|
||||
LispNumber num2 = call(argRest);
|
||||
public LispNumber call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
return new LispNumber(num1.getValue().multiply(num2.getValue()));
|
||||
}
|
||||
return callTailRecursive(new Cons(LispNumber.ONE, argumentList));
|
||||
}
|
||||
|
||||
throw new RuntimeException("*: " + argFirst + " is not a number");
|
||||
private LispNumber callTailRecursive(Cons argumentList) {
|
||||
Cons remainingArguments = (Cons) argumentList.getCdr();
|
||||
SExpression firstArgument = argumentList.getCar();
|
||||
LispNumber number1 = (LispNumber) firstArgument;
|
||||
|
||||
if (remainingArguments.nullp())
|
||||
return number1;
|
||||
|
||||
SExpression secondArgument = remainingArguments.getCar();
|
||||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
LispNumber product = new LispNumber(number1.getValue().multiply(number2.getValue()));
|
||||
SExpression remainingNumbers = remainingArguments.getCdr();
|
||||
|
||||
if (!remainingNumbers.consp())
|
||||
return product;
|
||||
|
||||
return callTailRecursive(new Cons(product, remainingNumbers));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,32 +1,21 @@
|
|||
package function.builtin;
|
||||
|
||||
import function.LispFunction;
|
||||
import function.*;
|
||||
import sexpression.*;
|
||||
|
||||
/**
|
||||
* <code>NULL</code> represents the NULL function in Lisp.
|
||||
*/
|
||||
public class NULL extends LispFunction {
|
||||
|
||||
// The number of arguments that NULL takes.
|
||||
private static final int NUM_ARGS = 1;
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
public SExpression call(Cons argList) {
|
||||
// retrieve the number of arguments passed to NULL
|
||||
int argListLength = LENGTH.getLength(argList);
|
||||
public NULL() {
|
||||
this.argumentValidator = new ArgumentValidator("NULL");
|
||||
this.argumentValidator.setExactNumberOfArguments(1);
|
||||
}
|
||||
|
||||
// make sure we have received the proper number of arguments
|
||||
if (argListLength != NUM_ARGS) {
|
||||
Cons originalSExpr = new Cons(new Symbol("NULL"), argList);
|
||||
String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to NULL: "
|
||||
+ originalSExpr;
|
||||
public SExpression call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
throw new RuntimeException(errMsg);
|
||||
}
|
||||
|
||||
SExpression arg = argList.getCar();
|
||||
|
||||
return (arg.nullp() ? Symbol.T : Nil.getInstance());
|
||||
return argumentList.getCar().nullp() ? Symbol.T : Nil.getInstance();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,31 +1,40 @@
|
|||
package function.builtin;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import function.LispFunction;
|
||||
import function.*;
|
||||
import sexpression.*;
|
||||
|
||||
public class PLUS extends LispFunction {
|
||||
|
||||
public LispNumber call(Cons argList) {
|
||||
if (argList.nullp()) {
|
||||
return new LispNumber(BigInteger.ZERO);
|
||||
}
|
||||
private ArgumentValidator argumentValidator;
|
||||
|
||||
if (!argList.getCdr().listp())
|
||||
throw new RuntimeException("+: " + argList + " is dotted");
|
||||
public PLUS() {
|
||||
this.argumentValidator = new ArgumentValidator("+");
|
||||
this.argumentValidator.setEveryArgumentExpectedType(LispNumber.class);
|
||||
}
|
||||
|
||||
SExpression argFirst = argList.getCar();
|
||||
Cons argRest = (Cons) argList.getCdr();
|
||||
public LispNumber call(Cons argumentList) {
|
||||
argumentValidator.validate(argumentList);
|
||||
|
||||
if (argFirst.numberp()) {
|
||||
LispNumber num1 = (LispNumber) argFirst;
|
||||
LispNumber num2 = call(argRest);
|
||||
return callTailRecursive(new Cons(LispNumber.ZERO, argumentList));
|
||||
}
|
||||
|
||||
return new LispNumber(num1.getValue().add(num2.getValue()));
|
||||
}
|
||||
private LispNumber callTailRecursive(Cons argumentList) {
|
||||
Cons remainingArguments = (Cons) argumentList.getCdr();
|
||||
SExpression firstArgument = argumentList.getCar();
|
||||
LispNumber number1 = (LispNumber) firstArgument;
|
||||
|
||||
throw new RuntimeException("+: " + argFirst + " is not a number");
|
||||
if (remainingArguments.nullp())
|
||||
return number1;
|
||||
|
||||
SExpression secondArgument = remainingArguments.getCar();
|
||||
LispNumber number2 = (LispNumber) secondArgument;
|
||||
LispNumber sum = new LispNumber(number1.getValue().add(number2.getValue()));
|
||||
SExpression remainingNumbers = remainingArguments.getCdr();
|
||||
|
||||
if (!remainingNumbers.consp())
|
||||
return sum;
|
||||
|
||||
return callTailRecursive(new Cons(sum, remainingNumbers));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@ import error.LispException;
|
|||
|
||||
@DisplayName("number")
|
||||
public class LispNumber extends Atom {
|
||||
|
||||
public static final LispNumber ZERO = new LispNumber(BigInteger.ZERO);
|
||||
public static final LispNumber ONE = new LispNumber(BigInteger.ONE);
|
||||
|
||||
private BigInteger value;
|
||||
|
||||
|
|
|
@ -24,16 +24,12 @@ public class LISTPTester {
|
|||
|
||||
@Test(expected = TooFewArgumentsException.class)
|
||||
public void testListpWithTooFewArguments() {
|
||||
String input = "(listp)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), parseString("NIL"));
|
||||
evaluateString("(listp)");
|
||||
}
|
||||
|
||||
@Test(expected = TooManyArgumentsException.class)
|
||||
public void testListpWithTooManyArguments() {
|
||||
String input = "(listp '() '())";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), parseString("NIL"));
|
||||
evaluateString("(listp '() '())");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package function.builtin;
|
||||
|
||||
import static testutil.TestUtilities.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import function.ArgumentValidator.*;
|
||||
import sexpression.LispNumber;
|
||||
|
||||
public class MINUSTester {
|
||||
|
||||
@Test
|
||||
public void testMinusWithOneNumber() {
|
||||
String input = "(- 27)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("-27"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinusWithTwoNumbers() {
|
||||
String input = "(- 5 3)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinusWithManyNumbers_PositiveResult() {
|
||||
String input = "(- 200 100 10 5)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("85"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinusWithManyNumbers_NegativeResult() {
|
||||
String input = "(- 100 200 20 5)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("-125"));
|
||||
}
|
||||
|
||||
@Test(expected = BadArgumentTypeException.class)
|
||||
public void testMinusWithNonNumber() {
|
||||
evaluateString("(- 'a 'b)");
|
||||
}
|
||||
|
||||
@Test(expected = TooFewArgumentsException.class)
|
||||
public void testMinusWithTooFewArguments() {
|
||||
evaluateString("(-)");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package function.builtin;
|
||||
|
||||
import static testutil.TestUtilities.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import sexpression.LispNumber;
|
||||
|
||||
public class MULTIPLYTester {
|
||||
|
||||
@Test
|
||||
public void testMultiplyWithNoArguments() {
|
||||
String input = "(*)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplyWithOneNumber() {
|
||||
String input = "(* 8)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplyWithTwoNumbers() {
|
||||
String input = "(* 5 3)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("15"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplyWithManyNumbers_PositiveResult() {
|
||||
String input = "(* 2 3 5 1)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("30"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplyWithManyNumbers_NegativeResult() {
|
||||
String input = "(* 3 (- 2) 10 2)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("-120"));
|
||||
}
|
||||
|
||||
@Test(expected = BadArgumentTypeException.class)
|
||||
public void testMultiplyWithNonNumber() {
|
||||
evaluateString("(* 'a 'b)");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package function.builtin;
|
||||
|
||||
import static testutil.TestUtilities.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import function.ArgumentValidator.*;
|
||||
|
||||
public class NULLTester {
|
||||
|
||||
@Test
|
||||
public void testNilIsNull() {
|
||||
String input = "(null ())";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), parseString("T"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListIsNotNull() {
|
||||
String input = "(null '(1))";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), parseString("NIL"));
|
||||
}
|
||||
|
||||
@Test(expected = TooFewArgumentsException.class)
|
||||
public void testNullWithTooFewArguments() {
|
||||
evaluateString("(null)");
|
||||
}
|
||||
|
||||
@Test(expected = TooManyArgumentsException.class)
|
||||
public void testNullWithTooManyArguments() {
|
||||
evaluateString("(null 1 2)");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package function.builtin;
|
||||
|
||||
import static testutil.TestUtilities.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import function.ArgumentValidator.BadArgumentTypeException;
|
||||
import sexpression.LispNumber;
|
||||
|
||||
public class PLUSTester {
|
||||
|
||||
@Test
|
||||
public void testPlusWithNoArguments() {
|
||||
String input = "(+)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlusWithOneNumber() {
|
||||
String input = "(+ 27)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("27"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlusWithTwoNumbers() {
|
||||
String input = "(+ 5 3)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlusWithManyNumbers_PositiveResult() {
|
||||
String input = "(+ 200 100 10 5)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("315"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlusWithManyNumbers_NegativeResult() {
|
||||
String input = "(+ 100 (- 200) 20 5)";
|
||||
|
||||
assertSExpressionsMatch(evaluateString(input), new LispNumber("-75"));
|
||||
}
|
||||
|
||||
@Test(expected = BadArgumentTypeException.class)
|
||||
public void testPlusWithNonNumber() {
|
||||
evaluateString("(+ 'a 'b)");
|
||||
}
|
||||
|
||||
}
|
|
@ -66,8 +66,7 @@ public class SExpressionTester {
|
|||
public void testComplexConsToString() {
|
||||
String expected = "(1 A \"string\")";
|
||||
Cons list = new Cons(new LispNumber("1"),
|
||||
new Cons(new Symbol("a"),
|
||||
new Cons(new LispString("\"string\""), Nil.getInstance())));
|
||||
new Cons(new Symbol("a"), new Cons(new LispString("\"string\""), Nil.getInstance())));
|
||||
|
||||
assertSExpressionMatchesString(expected, list);
|
||||
}
|
||||
|
@ -99,10 +98,8 @@ public class SExpressionTester {
|
|||
@Test
|
||||
public void testLambdaExpressionGetFunction() {
|
||||
String expected = "(LAMBDA)";
|
||||
UserDefinedFunction function = new UserDefinedFunction(expected, Nil.getInstance(),
|
||||
Nil.getInstance());
|
||||
LambdaExpression lambda = new LambdaExpression(new Cons(new Symbol("lambda"), Nil.getInstance()),
|
||||
function);
|
||||
UserDefinedFunction function = new UserDefinedFunction(expected, Nil.getInstance(), Nil.getInstance());
|
||||
LambdaExpression lambda = new LambdaExpression(new Cons(new Symbol("lambda"), Nil.getInstance()), function);
|
||||
|
||||
assertEquals(function, lambda.getFunction());
|
||||
}
|
||||
|
@ -167,4 +164,9 @@ public class SExpressionTester {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLispNumberConstants() {
|
||||
assertEquals(LispNumber.ONE.getValue(), new BigInteger("1"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue