Added unit tests and refactored defun

This commit is contained in:
Mike Cifelli 2017-01-27 12:12:27 -05:00
parent 5fd22b310f
commit 79648cd96f
2 changed files with 53 additions and 4 deletions

View File

@ -1,7 +1,9 @@
package function.builtin.special; package function.builtin.special;
import java.text.MessageFormat;
import java.util.HashMap; import java.util.HashMap;
import environment.Environment;
import function.*; import function.*;
import function.builtin.EVAL; import function.builtin.EVAL;
import function.builtin.cons.LIST; import function.builtin.cons.LIST;
@ -12,6 +14,7 @@ public class DEFUN extends LispFunction {
private ArgumentValidator argumentValidator; private ArgumentValidator argumentValidator;
private ArgumentValidator lambdaListIsListValidator; private ArgumentValidator lambdaListIsListValidator;
private ArgumentValidator lambdaListValidator; private ArgumentValidator lambdaListValidator;
private Environment environment;
public DEFUN() { public DEFUN() {
this.argumentValidator = new ArgumentValidator("DEFUN"); this.argumentValidator = new ArgumentValidator("DEFUN");
@ -23,6 +26,8 @@ public class DEFUN extends LispFunction {
this.lambdaListValidator = new ArgumentValidator("DEFUN|lambda_list|"); this.lambdaListValidator = new ArgumentValidator("DEFUN|lambda_list|");
this.lambdaListValidator.setEveryArgumentExpectedType(Symbol.class); this.lambdaListValidator.setEveryArgumentExpectedType(Symbol.class);
this.environment = Environment.getInstance();
} }
public SExpression call(Cons argumentList) { public SExpression call(Cons argumentList) {
@ -37,16 +42,26 @@ public class DEFUN extends LispFunction {
lambdaListValidator.validate(lambdaList); lambdaListValidator.validate(lambdaList);
Cons functionBody = (Cons) remainingArguments.getCdr(); Cons functionBody = (Cons) remainingArguments.getCdr();
UserDefinedFunction function = new UserDefinedFunction(functionName.toString(), lambdaList, functionBody);
HashMap<String, LispFunction> functionTable = EVAL.getFunctionTable(); HashMap<String, LispFunction> functionTable = EVAL.getFunctionTable();
if (functionTable.containsKey(functionName.toString())) if (isAlreadyDefined(functionName, functionTable))
System.out.println("WARNING: redefining function " + functionName.toString()); printWarning(functionName);
functionTable.put(functionName.toString(), new UserDefinedFunction(functionName.toString(), lambdaList, functionBody)); functionTable.put(functionName.toString(), function);
return functionName; return functionName;
} }
private boolean isAlreadyDefined(SExpression functionName, HashMap<String, LispFunction> functionTable) {
return functionTable.containsKey(functionName.toString());
}
private void printWarning(SExpression functionName) {
String message = MessageFormat.format("WARNING: redefining function {0}", functionName.toString());
environment.getOutput().println(message);
}
public boolean evaluateArguments() { public boolean evaluateArguments() {
return false; return false;
} }

View File

@ -1,13 +1,29 @@
package function.builtin.special; package function.builtin.special;
import static org.junit.Assert.assertEquals;
import static testutil.TestUtilities.*; import static testutil.TestUtilities.*;
import org.junit.Test; import java.io.*;
import org.junit.*;
import environment.Environment;
import function.ArgumentValidator.*; import function.ArgumentValidator.*;
public class DEFUNTester { public class DEFUNTester {
private ByteArrayOutputStream outputStream;
private void assertPrinted(String expected) {
assertEquals(expected, outputStream.toString());
}
@Before
public void setUp() {
this.outputStream = new ByteArrayOutputStream();
Environment.getInstance().setOutput(new PrintStream(outputStream));
}
@Test @Test
public void testDefun() { public void testDefun() {
String input = "(defun f () nil)"; String input = "(defun f () nil)";
@ -16,6 +32,24 @@ public class DEFUNTester {
assertSExpressionsMatch(parseString("()"), evaluateString("(f)")); assertSExpressionsMatch(parseString("()"), evaluateString("(f)"));
} }
@Test
public void redefineFunction_DisplaysWarning() {
String input = "(defun myFunction () nil)";
evaluateString(input);
evaluateString(input);
assertPrinted("WARNING: redefining function MYFUNCTION\n");
}
@Test
public void redefineFunction_ActuallyRedefinesFunction() {
evaluateString("(defun myFunction2 () nil)");
evaluateString("(defun myFunction2 () T)");
assertPrinted("WARNING: redefining function MYFUNCTION2\n");
assertSExpressionsMatch(parseString("t"), evaluateString("(myFunction2)"));
}
@Test(expected = DottedArgumentListException.class) @Test(expected = DottedArgumentListException.class)
public void testDefunWithDottedLambdaList() { public void testDefunWithDottedLambdaList() {
String input = "(funcall 'defun 'x (cons 'a 'b) ())"; String input = "(funcall 'defun 'x (cons 'a 'b) ())";