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 static final long serialVersionUID = 1L;
private String functionName; private String functionName;
private String argument; private String argument;
private Class<? extends SExpression> expected; private Class<? extends SExpression> expectedType;
public BadArgumentTypeException(String functionName, SExpression argument, public BadArgumentTypeException(String functionName, SExpression argument,
Class<? extends SExpression> expected) { Class<? extends SExpression> expectedType) {
this.functionName = functionName; this.functionName = functionName;
this.argument = argument.toString(); this.argument = argument.toString();
this.expected = expected; this.expectedType = expectedType;
} }
@Override @Override
public String getMessage() { 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, 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 // bind the values of the arguments to the formal parameter names
for (String param : formalParameters) { for (String param : formalParameters) {
SExpression currentArg = argList.getCar(); SExpression currentArg = argList.getCar();
environment.put(param, currentArg); environment.put(param, currentArg);
argList = (Cons) argList.getCdr(); argList = (Cons) argList.getCdr();
} }

View File

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

View File

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

View File

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

View File

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

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