Started refactoring and writing unit tests for DEFUN

This commit is contained in:
Mike Cifelli 2016-12-25 13:29:06 -05:00
parent 37bc303fe8
commit 68510ec1a8
8 changed files with 127 additions and 75 deletions

View File

@ -173,22 +173,25 @@ public class ArgumentValidator {
private static final long serialVersionUID = 1L;
private String functionName;
private String argument;
private Class<? extends SExpression> expected;
private Class<? extends SExpression> expectedType;
public BadArgumentTypeException(String functionName, SExpression argument,
Class<? extends SExpression> expected) {
Class<? extends SExpression> expectedType) {
this.functionName = functionName;
this.argument = argument.toString();
this.expected = expected;
this.expectedType = expectedType;
}
@Override
public String getMessage() {
DisplayName displayName = expected.getAnnotation(DisplayName.class);
String expectedType = (displayName == null) ? "unknown" : displayName.value();
return MessageFormat.format("{0}: {1} is not the expected type of ''{2}''", functionName, argument,
expectedType);
getExpectedTypeName());
}
private String getExpectedTypeName() {
DisplayName displayName = expectedType.getAnnotation(DisplayName.class);
return (displayName == null) ? "unknown" : displayName.value();
}
}

View File

@ -38,9 +38,7 @@ public class UserDefinedFunction extends LispFunction {
// bind the values of the arguments to the formal parameter names
for (String param : formalParameters) {
SExpression currentArg = argList.getCar();
environment.put(param, currentArg);
argList = (Cons) argList.getCdr();
}

View File

@ -1,38 +1,25 @@
package function.builtin;
import function.LispFunction;
import function.*;
import sexpression.*;
/**
* <code>CONS</code> represents the CONS function in Lisp.
*/
public class CONS extends LispFunction {
// The number of arguments that CONS takes.
private static final int NUM_ARGS = 2;
private ArgumentValidator argumentValidator;
public Cons call(Cons argList) {
// retrieve the number of arguments passed to CONS
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("CONS"), argList);
String errMsg = "too " + ((argListLength > NUM_ARGS) ? "many" : "few") + " arguments given to CONS: "
+ originalSExpr;
throw new RuntimeException(errMsg);
public CONS() {
this.argumentValidator = new ArgumentValidator("CONS");
this.argumentValidator.setExactNumberOfArguments(2);
}
// the car of the CONS cell we are going to create
SExpression argOne = argList.getCar();
public Cons call(Cons argumentList) {
argumentValidator.validate(argumentList);
Cons cdr = (Cons) argList.getCdr();
Cons cdr = (Cons) argumentList.getCdr();
SExpression firstArgument = argumentList.getCar();
SExpression secondArgument = cdr.getCar();
// the cdr of the CONS cell we are going to create
SExpression argTwo = cdr.getCar();
return new Cons(argOne, argTwo);
return new Cons(firstArgument, secondArgument);
}
}

View File

@ -5,34 +5,21 @@ import java.util.HashMap;
import function.*;
import sexpression.*;
/**
* <code>DEFUN</code> represents the DEFUN form in Lisp.
*/
public class DEFUN extends LispFunction {
// The minimum number of arguments that DEFUN takes.
private static final int MIN_ARGS = 3;
private ArgumentValidator argumentValidator;
public SExpression call(Cons argList) {
// retrieve the number of arguments passed to DEFUN
int argListLength = LENGTH.getLength(argList);
// make sure we have received the proper number of arguments
if (argListLength < MIN_ARGS) {
Cons originalSExpr = new Cons(new Symbol("DEFUN"), argList);
String errMsg = "too few arguments given to DEFUN: " + originalSExpr;
throw new RuntimeException(errMsg);
public DEFUN() {
this.argumentValidator = new ArgumentValidator("DEFUN");
this.argumentValidator.setMinimumNumberOfArguments(3);
this.argumentValidator.setFirstArgumentExpectedType(Symbol.class);
}
SExpression name = argList.getCar(); // name of the function
public SExpression call(Cons argumentList) {
argumentValidator.validate(argumentList);
// make sure the function name is a symbol
if (!name.symbolp()) {
throw new RuntimeException("DEFUN: " + name + " is not a symbol");
}
Cons cdr = (Cons) argList.getCdr();
Cons cdr = (Cons) argumentList.getCdr();
SExpression name = argumentList.getCar(); // name of the function
SExpression cadr = cdr.getCar();
// make sure the list of arguments (lambda list) is a proper list
@ -60,11 +47,6 @@ public class DEFUN extends LispFunction {
return name;
}
/**
* Determine if the arguments passed to this Lisp function should be evaluated.
*
* @return <code>false</code>
*/
public boolean evaluateArguments() {
return false;
}

View File

@ -36,16 +36,12 @@ public class CARTester {
@Test(expected = TooManyArgumentsException.class)
public void testCarWithTooManyArguments() {
String input = "(car '(1 2) '(1 2) \"oh\")";
assertSExpressionsMatch(evaluateString(input), parseString("1"));
evaluateString("(car '(1 2) '(1 2) \"oh\")");
}
@Test(expected = TooFewArgumentsException.class)
public void testCarWithTooFewArguments() {
String input = "(car)";
assertSExpressionsMatch(evaluateString(input), parseString("1"));
evaluateString("(car)");
}
}

View File

@ -36,16 +36,12 @@ public class CDRTester {
@Test(expected = TooManyArgumentsException.class)
public void testCdrWithTooManyArguments() {
String input = "(cdr '(1 2) '(1 2) \"oh\")";
assertSExpressionsMatch(evaluateString(input), parseString("1"));
evaluateString("(cdr '(1 2) '(1 2) \"oh\")");
}
@Test(expected = TooFewArgumentsException.class)
public void testCdrWithTooFewArguments() {
String input = "(cdr)";
assertSExpressionsMatch(evaluateString(input), parseString("1"));
evaluateString("(cdr)");
}
}

View File

@ -0,0 +1,61 @@
package function.builtin;
import static testutil.TestUtilities.*;
import org.junit.Test;
import function.ArgumentValidator.*;
import sexpression.*;
public class CONSTester {
@Test
public void testConsWithNilValues() {
String input = "(cons () nil)";
assertSExpressionsMatch(evaluateString(input), parseString("(())"));
}
@Test
public void testConsWithTwoSymbols() {
String input = "(cons 'a 'b)";
assertSExpressionsMatch(evaluateString(input), new Cons(new Symbol("A"), new Symbol("B")));
}
@Test
public void testConsWithListAsCdr() {
String input = "(cons 1 '(2 3))";
assertSExpressionsMatch(evaluateString(input), parseString("(1 2 3)"));
}
@Test
public void testConsWithTwoLists() {
String input = "(cons '(1 2) '(3 4))";
assertSExpressionsMatch(evaluateString(input), parseString("((1 2) 3 4)"));
}
@Test
public void testConsWithList() {
String input = "(cons nil '(2 3))";
assertSExpressionsMatch(evaluateString(input), parseString("(nil 2 3)"));
}
@Test(expected = TooManyArgumentsException.class)
public void testConsWithTooManyArguments() {
String input = "(cons 1 2 3)";
evaluateString(input);
}
@Test(expected = TooFewArgumentsException.class)
public void testConsWithTooFewArguments() {
String input = "(cons 1)";
evaluateString(input);
}
}

View File

@ -0,0 +1,29 @@
package function.builtin;
import static testutil.TestUtilities.*;
import org.junit.Test;
import function.ArgumentValidator.*;
public class DEFUNTester {
@Test
public void testDefun() {
String input = "(defun f () nil)";
assertSExpressionsMatch(evaluateString(input), parseString("f"));
assertSExpressionsMatch(evaluateString("(f)"), parseString("()"));
}
@Test(expected = BadArgumentTypeException.class)
public void testDefunWithNonSymbolName() {
evaluateString("(defun 1 () ())");
}
@Test(expected = TooFewArgumentsException.class)
public void testApplyWithTooFewArguments() {
evaluateString("(defun 1 ())");
}
}