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 sexpression.*;
/**
* <code>APPLY</code> 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 <code>argList</code>
*/
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 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);
public APPLY() {
this.argumentValidator = new ArgumentValidator("APPLY");
this.argumentValidator.setExactNumberOfArguments(NUMBER_OF_ARGUMENTS);
}
SExpression car = argList.getCar(); // function name
Cons cdr = (Cons) argList.getCdr();
SExpression cadr = cdr.getCar(); // argument list
public SExpression call(Cons argList) {
argumentValidator.validate(argList);
// make sure the second argument is a list
if (cadr.listp()) {
LispFunction function = EVAL.lookupFunction(car.toString());
Cons cdr = (Cons) argList.getCdr();
SExpression functionName = argList.getCar();
SExpression argumentList = cdr.getCar();
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");
}
}

View File

@ -1,32 +1,23 @@
package function.builtin;
import function.LispFunction;
import function.*;
import sexpression.*;
/**
* <code>ATOM</code> 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);
// 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);
public ATOM() {
this.argumentValidator = new ArgumentValidator("ATOM");
this.argumentValidator.setExactNumberOfArguments(NUMBER_OF_ARGUMENTS);
}
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;
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)

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;
import static org.junit.Assert.*;
import static parser.SExpressionTypeAssertions.*;
import static testutil.SExpressionTypeAssertions.*;
import static testutil.TestUtilities.createIOExceptionThrowingInputStream;
import static testutil.TestUtilities.createInputStreamFromString;

View File

@ -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 {

View File

@ -1,7 +1,13 @@
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) {
@ -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());
}
}