diff --git a/src/function/builtin/special/DEFUN.java b/src/function/builtin/special/DEFUN.java index 6f1a86a..75c1dc0 100644 --- a/src/function/builtin/special/DEFUN.java +++ b/src/function/builtin/special/DEFUN.java @@ -1,7 +1,9 @@ package function.builtin.special; +import java.text.MessageFormat; import java.util.HashMap; +import environment.Environment; import function.*; import function.builtin.EVAL; import function.builtin.cons.LIST; @@ -12,6 +14,7 @@ public class DEFUN extends LispFunction { private ArgumentValidator argumentValidator; private ArgumentValidator lambdaListIsListValidator; private ArgumentValidator lambdaListValidator; + private Environment environment; public DEFUN() { this.argumentValidator = new ArgumentValidator("DEFUN"); @@ -23,6 +26,8 @@ public class DEFUN extends LispFunction { this.lambdaListValidator = new ArgumentValidator("DEFUN|lambda_list|"); this.lambdaListValidator.setEveryArgumentExpectedType(Symbol.class); + + this.environment = Environment.getInstance(); } public SExpression call(Cons argumentList) { @@ -37,16 +42,26 @@ public class DEFUN extends LispFunction { lambdaListValidator.validate(lambdaList); Cons functionBody = (Cons) remainingArguments.getCdr(); + UserDefinedFunction function = new UserDefinedFunction(functionName.toString(), lambdaList, functionBody); HashMap functionTable = EVAL.getFunctionTable(); - if (functionTable.containsKey(functionName.toString())) - System.out.println("WARNING: redefining function " + functionName.toString()); + if (isAlreadyDefined(functionName, functionTable)) + printWarning(functionName); - functionTable.put(functionName.toString(), new UserDefinedFunction(functionName.toString(), lambdaList, functionBody)); + functionTable.put(functionName.toString(), function); return functionName; } + private boolean isAlreadyDefined(SExpression functionName, HashMap 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() { return false; } diff --git a/test/function/builtin/special/DEFUNTester.java b/test/function/builtin/special/DEFUNTester.java index 4705fd2..ae6ba24 100644 --- a/test/function/builtin/special/DEFUNTester.java +++ b/test/function/builtin/special/DEFUNTester.java @@ -1,13 +1,29 @@ package function.builtin.special; +import static org.junit.Assert.assertEquals; import static testutil.TestUtilities.*; -import org.junit.Test; +import java.io.*; +import org.junit.*; + +import environment.Environment; import function.ArgumentValidator.*; 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 public void testDefun() { String input = "(defun f () nil)"; @@ -16,6 +32,24 @@ public class DEFUNTester { 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) public void testDefunWithDottedLambdaList() { String input = "(funcall 'defun 'x (cons 'a 'b) ())";