Started writing tests for the builtin functions

This commit is contained in:
Mike Cifelli 2016-12-22 10:32:48 -05:00
parent 715de2f220
commit 9bea0e6533
8 changed files with 153 additions and 65 deletions

View File

@ -3,65 +3,46 @@ package function.builtin;
import function.*; import function.*;
import sexpression.*; import sexpression.*;
/**
* <code>APPLY</code> represents the APPLY function in Lisp.
*/
public class APPLY extends LispFunction { public class APPLY extends LispFunction {
/** private static final int NUMBER_OF_ARGUMENTS = 2;
* Call APPLY with the specified argument list. private ArgumentValidator argumentValidator;
*
* @param argList
* the list of arguments to be sent to APPLY (MUST BE A PROPER LIST)
* @return the result of evaluating APPLY on <code>argList</code>
*/
public static SExpression apply(Cons argList) { public static SExpression apply(Cons argList) {
return new APPLY().call(argList); return new APPLY().call(argList);
} }
// The number of arguments that APPLY takes. public APPLY() {
private static final int NUM_ARGS = 2; 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);
// 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 public SExpression call(Cons argList) {
Cons cdr = (Cons) argList.getCdr(); argumentValidator.validate(argList);
SExpression cadr = cdr.getCar(); // argument list
// make sure the second argument is a list Cons cdr = (Cons) argList.getCdr();
if (cadr.listp()) { SExpression functionName = argList.getCar();
LispFunction function = EVAL.lookupFunction(car.toString()); SExpression argumentList = cdr.getCar();
if (argumentList.listp()) {
LispFunction function = EVAL.lookupFunction(functionName.toString());
if (function == null) { if (function == null) {
// check if the car of the list is a lambda expression if (functionName.functionp()) {
if (car.functionp()) { function = ((LambdaExpression) functionName).getFunction();
function = ((LambdaExpression) car).getFunction(); } else if (LAMBDA.isLambdaExpression(functionName)) {
} else if (LAMBDA.isLambdaExpression(car)) { Cons lexpr = (Cons) functionName;
Cons lexpr = (Cons) car;
function = LAMBDA.createFunction(lexpr); function = LAMBDA.createFunction(lexpr);
} else { } 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) argumentList);
return function.call((Cons) cadr);
} }
// the second argument is not a list throw new RuntimeException("APPLY: " + argumentList + " is not a list");
throw new RuntimeException("APPLY: " + cadr + " is not a list");
} }
} }

View File

@ -1,32 +1,23 @@
package function.builtin; package function.builtin;
import function.LispFunction; import function.*;
import sexpression.*; import sexpression.*;
/**
* <code>ATOM</code> represents the ATOM function in Lisp.
*/
public class ATOM extends LispFunction { public class ATOM extends LispFunction {
// The number of arguments that ATOM takes. private static final int NUMBER_OF_ARGUMENTS = 1;
private static final int NUM_ARGS = 1; private ArgumentValidator argumentValidator;
public SExpression call(Cons argList) { public ATOM() {
// retrieve the number of arguments passed to ATOM this.argumentValidator = new ArgumentValidator("ATOM");
int argListLength = LENGTH.getLength(argList); 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;
throw new RuntimeException(errMsg);
} }
SExpression arg = argList.getCar(); public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
SExpression argument = argumentList.getCar();
return (arg.atomp() ? Symbol.T : Nil.getUniqueInstance()); return argument.atomp() ? Symbol.T : Nil.getUniqueInstance();
} }
} }

View File

@ -1,6 +1,7 @@
package function; package function;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static testutil.TestUtilities.*;
import org.junit.Test; import org.junit.Test;
@ -35,7 +36,7 @@ public class UserDefinedFunctionTester {
new Cons(Nil.getUniqueInstance(), new Cons(Nil.getUniqueInstance(),
new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance()))); new Cons(Nil.getUniqueInstance(), Nil.getUniqueInstance())));
assertEquals(expected.toString(), function.getLambdaExpression().toString()); assertSExpressionsMatch(expected, function.getLambdaExpression());
} }
@Test @Test
@ -44,7 +45,7 @@ public class UserDefinedFunctionTester {
SExpression argument = new LispNumber(23); SExpression argument = new LispNumber(23);
Cons argumentList = new Cons(argument, Nil.getUniqueInstance()); Cons argumentList = new Cons(argument, Nil.getUniqueInstance());
assertEquals(argument.toString(), function.call(argumentList).toString()); assertSExpressionsMatch(argument, function.call(argumentList));
} }
@Test(expected = TooManyArgumentsException.class) @Test(expected = TooManyArgumentsException.class)

View File

@ -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)");
}
}

View File

@ -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)");
}
}

View File

@ -1,7 +1,7 @@
package parser; package parser;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static parser.SExpressionTypeAssertions.*; import static testutil.SExpressionTypeAssertions.*;
import static testutil.TestUtilities.createIOExceptionThrowingInputStream; import static testutil.TestUtilities.createIOExceptionThrowingInputStream;
import static testutil.TestUtilities.createInputStreamFromString; import static testutil.TestUtilities.createInputStreamFromString;

View File

@ -1,9 +1,8 @@
package parser; package testutil;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import sexpression.Nil; import sexpression.*;
import sexpression.SExpression;
public final class SExpressionTypeAssertions { public final class SExpressionTypeAssertions {

View File

@ -1,7 +1,13 @@
package testutil; package testutil;
import static org.junit.Assert.assertEquals;
import java.io.*; import java.io.*;
import function.builtin.EVAL;
import parser.LispParser;
import sexpression.SExpression;
public final class TestUtilities { public final class TestUtilities {
public static InputStream createInputStreamFromString(String string) { public static InputStream createInputStreamFromString(String string) {
@ -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());
}
} }