2017-01-14 18:01:14 -05:00
|
|
|
package function.builtin.special;
|
2016-12-25 13:29:06 -05:00
|
|
|
|
2017-02-11 10:42:07 -05:00
|
|
|
import static org.junit.Assert.assertTrue;
|
2017-03-03 15:06:49 -05:00
|
|
|
import static table.FunctionTable.resetFunctionTable;
|
2016-12-25 13:29:06 -05:00
|
|
|
import static testutil.TestUtilities.*;
|
|
|
|
|
2017-01-27 12:12:27 -05:00
|
|
|
import java.io.*;
|
2016-12-25 13:29:06 -05:00
|
|
|
|
2017-01-27 12:12:27 -05:00
|
|
|
import org.junit.*;
|
|
|
|
|
2017-02-06 13:39:05 -05:00
|
|
|
import environment.RuntimeEnvironment;
|
2017-02-11 10:42:07 -05:00
|
|
|
import error.ErrorManager;
|
2016-12-25 13:29:06 -05:00
|
|
|
import function.ArgumentValidator.*;
|
2017-03-01 11:11:59 -05:00
|
|
|
import function.UserDefinedFunction.IllegalKeywordRestPositionException;
|
2016-12-25 13:29:06 -05:00
|
|
|
|
|
|
|
public class DEFUNTester {
|
|
|
|
|
2017-01-27 12:12:27 -05:00
|
|
|
private ByteArrayOutputStream outputStream;
|
2017-02-11 10:51:37 -05:00
|
|
|
private RuntimeEnvironment environment;
|
|
|
|
|
|
|
|
public DEFUNTester() {
|
|
|
|
this.environment = RuntimeEnvironment.getInstance();
|
|
|
|
}
|
2017-01-27 12:12:27 -05:00
|
|
|
|
2017-02-11 10:42:07 -05:00
|
|
|
private void assertSomethingPrinted() {
|
|
|
|
assertTrue(outputStream.toByteArray().length > 0);
|
2017-01-27 12:12:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Before
|
|
|
|
public void setUp() {
|
2017-02-11 10:51:37 -05:00
|
|
|
outputStream = new ByteArrayOutputStream();
|
|
|
|
|
2017-03-05 10:20:31 -05:00
|
|
|
environment.reset();
|
2017-02-11 10:51:37 -05:00
|
|
|
environment.setOutput(new PrintStream(outputStream));
|
|
|
|
environment.setErrorManager(new ErrorManager());
|
2017-02-11 13:33:34 -05:00
|
|
|
environment.setWarningOutputDecorator(s -> s);
|
2017-03-03 15:06:49 -05:00
|
|
|
resetFunctionTable();
|
2017-02-06 12:02:19 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@After
|
|
|
|
public void tearDown() {
|
2017-03-05 10:20:31 -05:00
|
|
|
environment.reset();
|
2017-03-03 15:06:49 -05:00
|
|
|
resetFunctionTable();
|
2017-01-27 12:12:27 -05:00
|
|
|
}
|
|
|
|
|
2016-12-25 13:29:06 -05:00
|
|
|
@Test
|
2017-02-27 12:00:24 -05:00
|
|
|
public void defun() {
|
2017-02-04 15:38:47 -05:00
|
|
|
String input = "(defun f () t)";
|
|
|
|
|
|
|
|
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
|
|
assertSExpressionsMatch(parseString("t"), evaluateString("(f)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2017-02-27 12:00:24 -05:00
|
|
|
public void defunWithEmptyBody() {
|
2017-02-04 15:38:47 -05:00
|
|
|
String input = "(defun f ())";
|
2016-12-25 13:29:06 -05:00
|
|
|
|
2017-01-27 10:51:25 -05:00
|
|
|
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
|
|
assertSExpressionsMatch(parseString("()"), evaluateString("(f)"));
|
2016-12-25 13:29:06 -05:00
|
|
|
}
|
|
|
|
|
2017-02-25 19:11:31 -05:00
|
|
|
@Test
|
2017-02-27 12:00:24 -05:00
|
|
|
public void defunEvaluatesArguments() {
|
2017-02-25 19:11:31 -05:00
|
|
|
evaluateString("(defun f (x) (car x))");
|
|
|
|
assertSExpressionsMatch(parseString("1"), evaluateString("(f '(1 2 3))"));
|
|
|
|
}
|
|
|
|
|
2017-02-27 12:00:24 -05:00
|
|
|
@Test
|
|
|
|
public void defunRecursiveFunction() {
|
|
|
|
evaluateString("(defun fact (x) (if (< x 2) 1 (* x (fact (- x 1)))))");
|
|
|
|
assertSExpressionsMatch(parseString("120"), evaluateString("(fact 5)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void defunTailRecursiveFunction() {
|
|
|
|
evaluateString("(defun fact-tail (x acc) (if (< x 2) acc (fact-tail (- x 1) (* x acc))))");
|
|
|
|
assertSExpressionsMatch(parseString("120"), evaluateString("(fact-tail 5 1)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void defunSimpleClass() {
|
2017-03-07 16:41:26 -05:00
|
|
|
evaluateString("(defun counter-class () (let ((counter 0)) (lambda () (setq counter (+ 1 counter)))))");
|
|
|
|
evaluateString("(setq my-counter (counter-class))");
|
2017-02-27 12:00:24 -05:00
|
|
|
|
|
|
|
assertSExpressionsMatch(parseString("1"), evaluateString("(funcall my-counter)"));
|
|
|
|
assertSExpressionsMatch(parseString("2"), evaluateString("(funcall my-counter)"));
|
|
|
|
assertSExpressionsMatch(parseString("3"), evaluateString("(funcall my-counter)"));
|
|
|
|
assertSExpressionsMatch(parseString("4"), evaluateString("(funcall my-counter)"));
|
|
|
|
}
|
|
|
|
|
2017-01-27 12:12:27 -05:00
|
|
|
@Test
|
|
|
|
public void redefineFunction_DisplaysWarning() {
|
|
|
|
String input = "(defun myFunction () nil)";
|
|
|
|
evaluateString(input);
|
|
|
|
evaluateString(input);
|
|
|
|
|
2017-02-11 10:42:07 -05:00
|
|
|
assertSomethingPrinted();
|
2017-01-27 12:12:27 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void redefineFunction_ActuallyRedefinesFunction() {
|
|
|
|
evaluateString("(defun myFunction2 () nil)");
|
|
|
|
evaluateString("(defun myFunction2 () T)");
|
|
|
|
|
2017-02-11 10:42:07 -05:00
|
|
|
assertSomethingPrinted();
|
2017-01-27 12:12:27 -05:00
|
|
|
assertSExpressionsMatch(parseString("t"), evaluateString("(myFunction2)"));
|
|
|
|
}
|
|
|
|
|
2017-01-16 13:38:49 -05:00
|
|
|
@Test(expected = DottedArgumentListException.class)
|
2017-02-27 12:00:24 -05:00
|
|
|
public void defunWithDottedLambdaList() {
|
2017-02-25 19:11:31 -05:00
|
|
|
evaluateString("(funcall 'defun 'x (cons 'a 'b) ())");
|
2017-01-16 13:38:49 -05:00
|
|
|
}
|
|
|
|
|
2016-12-25 13:29:06 -05:00
|
|
|
@Test(expected = BadArgumentTypeException.class)
|
2017-02-27 12:00:24 -05:00
|
|
|
public void defunWithNonSymbolName() {
|
2016-12-25 13:29:06 -05:00
|
|
|
evaluateString("(defun 1 () ())");
|
|
|
|
}
|
|
|
|
|
2017-01-16 13:38:49 -05:00
|
|
|
@Test(expected = BadArgumentTypeException.class)
|
2017-02-27 12:00:24 -05:00
|
|
|
public void defunWithBadLambdaList() {
|
2017-01-16 13:38:49 -05:00
|
|
|
evaluateString("(defun x a ())");
|
|
|
|
}
|
|
|
|
|
2016-12-25 13:29:06 -05:00
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
2017-02-27 12:00:24 -05:00
|
|
|
public void defunWithTooFewArguments() {
|
2017-02-04 15:38:47 -05:00
|
|
|
evaluateString("(defun x)");
|
2016-12-25 13:29:06 -05:00
|
|
|
}
|
|
|
|
|
2017-03-01 11:11:59 -05:00
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
|
|
public void defunFunctionAndCallWithTooFewArguments() {
|
|
|
|
evaluateString("(defun x (a b))");
|
|
|
|
evaluateString("(x 'a)");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test(expected = TooManyArgumentsException.class)
|
|
|
|
public void defunFunctionAndCallWithTooManyArguments() {
|
|
|
|
evaluateString("(defun x (a b))");
|
|
|
|
evaluateString("(x 'a 'b 'c)");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void defunWithKeywordRestParameter() {
|
|
|
|
evaluateString("(defun f (&rest x) (car x))");
|
|
|
|
assertSExpressionsMatch(parseString("1"), evaluateString("(f 1 2 3 4 5)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void defunWithNormalAndKeywordRestParameter() {
|
|
|
|
evaluateString("(defun f (a &rest b) (cons a b))");
|
|
|
|
assertSExpressionsMatch(parseString("(1 2 3 4 5)"), evaluateString("(f 1 2 3 4 5)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test(expected = IllegalKeywordRestPositionException.class)
|
|
|
|
public void defunWithParametersFollowingKeywordRest() {
|
|
|
|
evaluateString("(defun f (a &rest b c) (cons a b))");
|
|
|
|
evaluateString("(f 1 2 3)");
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void defunWithKeywordRest_CallWithNoArguments() {
|
|
|
|
evaluateString("(defun f (&rest a) (car a))");
|
|
|
|
assertSExpressionsMatch(parseString("nil"), evaluateString("(f)"));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
|
|
public void defunWithNormalAndKeywordRest_CallWithNoArguments() {
|
|
|
|
evaluateString("(defun f (a &rest b) a)");
|
|
|
|
evaluateString("(f)");
|
|
|
|
}
|
|
|
|
|
2016-12-25 13:29:06 -05:00
|
|
|
}
|