From 9bea0e6533449919383d118604a67a8ed624df13 Mon Sep 17 00:00:00 2001 From: Mike Cifelli Date: Thu, 22 Dec 2016 10:32:48 -0500 Subject: [PATCH] Started writing tests for the builtin functions --- src/function/builtin/APPLY.java | 57 ++++++----------- src/function/builtin/ATOM.java | 31 ++++------ test/function/UserDefinedFunctionTester.java | 5 +- test/function/builtin/APPLYTester.java | 61 +++++++++++++++++++ test/function/builtin/ATOMTester.java | 35 +++++++++++ test/parser/LispParserTester.java | 2 +- .../SExpressionTypeAssertions.java | 5 +- test/testutil/TestUtilities.java | 22 ++++++- 8 files changed, 153 insertions(+), 65 deletions(-) create mode 100644 test/function/builtin/APPLYTester.java create mode 100644 test/function/builtin/ATOMTester.java rename test/{parser => testutil}/SExpressionTypeAssertions.java (96%) 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()); + } + }