package function.builtin.special; import static org.junit.Assert.assertTrue; import static table.FunctionTable.resetFunctionTable; import static testutil.TestUtilities.*; import java.io.*; import org.junit.*; import environment.RuntimeEnvironment; import error.ErrorManager; import function.ArgumentValidator.*; import function.UserDefinedFunction.IllegalKeywordRestPositionException; public class DEFINE_MACROTester { private ByteArrayOutputStream outputStream; private RuntimeEnvironment environment; public DEFINE_MACROTester() { this.environment = RuntimeEnvironment.getInstance(); } private void assertSomethingPrinted() { assertTrue(outputStream.toByteArray().length > 0); } @Before public void setUp() { outputStream = new ByteArrayOutputStream(); environment.reset(); environment.setOutput(new PrintStream(outputStream)); environment.setErrorManager(new ErrorManager()); environment.setWarningOutputDecorator(s -> s); resetFunctionTable(); } @After public void tearDown() { environment.reset(); resetFunctionTable(); } @Test public void defineMacro() { String input = "(define-macro f () t)"; assertSExpressionsMatch(parseString("f"), evaluateString(input)); assertSExpressionsMatch(parseString("t"), evaluateString("(f)")); } @Test public void defineMacroWithEmptyBody() { String input = "(define-macro f ())"; assertSExpressionsMatch(parseString("f"), evaluateString(input)); assertSExpressionsMatch(parseString("()"), evaluateString("(f)")); } @Test public void defineMacroDoesNotEvaluatesArguments() { evaluateString("(define-macro f (x) (car x))"); assertSExpressionsMatch(parseString("quote"), evaluateString("(f '(1 2 3))")); } @Test public void defineMacroAdd() { evaluateString("(define-macro f (x) (+ (eval x) 23))"); assertSExpressionsMatch(parseString("27"), evaluateString("(f (+ 2 2))")); } @Test public void defineMacroSetVariable() { evaluateString("(define-macro f (x) (set x 23))"); evaluateString("(f y)"); assertSExpressionsMatch(parseString("23"), evaluateString("y")); } @Test public void defineMacroVariableCapture() { evaluateString("(setf x 0)"); evaluateString("(define-macro f (x) (set x 23))"); evaluateString("(f x)"); assertSExpressionsMatch(parseString("0"), evaluateString("x")); } @Test public void defineMacroAvoidVariableCaptureConvention() { evaluateString("(setf x 0)"); evaluateString("(define-macro f (-x-) (set -x- 23))"); evaluateString("(f x)"); assertSExpressionsMatch(parseString("23"), evaluateString("x")); } @Test public void redefineMacro_DisplaysWarning() { String input = "(define-macro myFunction () nil)"; evaluateString(input); evaluateString(input); assertSomethingPrinted(); } @Test public void redefineMacro_ActuallyRedefinesMacro() { evaluateString("(define-macro myMacro () nil)"); evaluateString("(define-macro myMacro () T)"); assertSomethingPrinted(); assertSExpressionsMatch(parseString("t"), evaluateString("(myMacro)")); } @Test(expected = DottedArgumentListException.class) public void defineMacroWithDottedLambdaList() { evaluateString("(funcall 'define-macro 'x (cons 'a 'b) ())"); } @Test(expected = BadArgumentTypeException.class) public void defineMacroWithNonSymbolName() { evaluateString("(define-macro 1 () ())"); } @Test(expected = BadArgumentTypeException.class) public void defineMacroWithBadLambdaList() { evaluateString("(define-macro x a ())"); } @Test(expected = TooFewArgumentsException.class) public void defineMacroWithTooFewArguments() { evaluateString("(define-macro x)"); } @Test(expected = TooFewArgumentsException.class) public void defineMacroAndCallWithTooFewArguments() { evaluateString("(define-macro x (a b))"); evaluateString("(x a)"); } @Test(expected = TooManyArgumentsException.class) public void defineMacroAndCallWithTooManyArguments() { evaluateString("(define-macro x (a b))"); evaluateString("(x a b c)"); } @Test public void defineMacroWithKeywordRestParameter() { evaluateString("(define-macro f (&rest x) (car x))"); assertSExpressionsMatch(parseString("1"), evaluateString("(f 1 2 3 4 5)")); } @Test public void defineMacroWithNormalAndKeywordRestParameter() { evaluateString("(define-macro 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 defineMacroWithParametersFollowingKeywordRest() { evaluateString("(define-macro f (a &rest b c) (cons a b))"); evaluateString("(f 1 2 3)"); } @Test public void defineMacroWithKeywordRest_CallWithNoArguments() { evaluateString("(define-macro f (&rest a) (car a))"); assertSExpressionsMatch(parseString("nil"), evaluateString("(f)")); } @Test(expected = TooFewArgumentsException.class) public void defineMacroWithNormalAndKeywordRest_CallWithNoArguments() { evaluateString("(define-macro f (a &rest b) a)"); evaluateString("(f)"); } }