186 lines
5.8 KiB
Java
186 lines
5.8 KiB
Java
package function.builtin.special;
|
|
|
|
import static org.junit.Assert.assertTrue;
|
|
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
import static testutil.TestUtilities.evaluateString;
|
|
import static testutil.TestUtilities.parseString;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.PrintStream;
|
|
|
|
import org.junit.Test;
|
|
|
|
import environment.RuntimeEnvironment;
|
|
import error.ErrorManager;
|
|
import function.ArgumentValidator.BadArgumentTypeException;
|
|
import function.ArgumentValidator.DottedArgumentListException;
|
|
import function.ArgumentValidator.TooFewArgumentsException;
|
|
import function.ArgumentValidator.TooManyArgumentsException;
|
|
import function.UserDefinedFunction.IllegalKeywordRestPositionException;
|
|
import testutil.SymbolAndFunctionCleaner;
|
|
|
|
public class DEFMACROTest extends SymbolAndFunctionCleaner {
|
|
|
|
private ByteArrayOutputStream outputStream;
|
|
private RuntimeEnvironment environment;
|
|
|
|
public DEFMACROTest() {
|
|
this.environment = RuntimeEnvironment.getInstance();
|
|
}
|
|
|
|
private void assertSomethingPrinted() {
|
|
assertTrue(outputStream.toByteArray().length > 0);
|
|
}
|
|
|
|
@Override
|
|
public void additionalSetUp() {
|
|
outputStream = new ByteArrayOutputStream();
|
|
|
|
environment.reset();
|
|
environment.setOutput(new PrintStream(outputStream));
|
|
environment.setErrorManager(new ErrorManager());
|
|
environment.setWarningOutputDecorator(s -> s);
|
|
}
|
|
|
|
@Override
|
|
public void additionalTearDown() {
|
|
environment.reset();
|
|
}
|
|
|
|
@Test
|
|
public void defmacro() {
|
|
String input = "(defmacro m () t)";
|
|
|
|
assertSExpressionsMatch(parseString("m"), evaluateString(input));
|
|
assertSExpressionsMatch(parseString("t"), evaluateString("(m)"));
|
|
}
|
|
|
|
@Test
|
|
public void defmacroWithEmptyBody() {
|
|
String input = "(defmacro m ())";
|
|
|
|
assertSExpressionsMatch(parseString("m"), evaluateString(input));
|
|
assertSExpressionsMatch(parseString("()"), evaluateString("(m)"));
|
|
}
|
|
|
|
@Test
|
|
public void defmacroDoesNotEvaluateArguments() {
|
|
evaluateString("(setq x 'grains)");
|
|
evaluateString("(defmacro m (x))");
|
|
evaluateString("(m (setq x 'sprouts))");
|
|
|
|
assertSExpressionsMatch(parseString("grains"), evaluateString("x"));
|
|
}
|
|
|
|
@Test
|
|
public void defmacroAdd() {
|
|
evaluateString("(defmacro m (x) (+ (eval x) 23))");
|
|
assertSExpressionsMatch(parseString("27"), evaluateString("(m (+ 2 2))"));
|
|
}
|
|
|
|
@Test
|
|
public void defmacroSetVariable() {
|
|
evaluateString("(defmacro m (x) (set x 23))");
|
|
evaluateString("(m y)");
|
|
assertSExpressionsMatch(parseString("23"), evaluateString("y"));
|
|
}
|
|
|
|
@Test
|
|
public void defmacroVariableCapture() {
|
|
evaluateString("(setq x 0)");
|
|
evaluateString("(defmacro m (x) (set x 23))");
|
|
evaluateString("(m x)");
|
|
assertSExpressionsMatch(parseString("0"), evaluateString("x"));
|
|
}
|
|
|
|
@Test
|
|
public void redefineMacro_DisplaysWarning() {
|
|
String input = "(defmacro myMacro () nil)";
|
|
evaluateString(input);
|
|
evaluateString(input);
|
|
|
|
assertSomethingPrinted();
|
|
}
|
|
|
|
@Test
|
|
public void redefineMacro_ActuallyRedefinesSpecialFunction() {
|
|
evaluateString("(defmacro myMacro () nil)");
|
|
evaluateString("(defmacro myMacro () T)");
|
|
|
|
assertSomethingPrinted();
|
|
assertSExpressionsMatch(parseString("t"), evaluateString("(myMacro)"));
|
|
}
|
|
|
|
@Test(expected = DottedArgumentListException.class)
|
|
public void defmacroWithDottedLambdaList() {
|
|
evaluateString("(funcall 'defmacro 'm (cons 'a 'b) ())");
|
|
}
|
|
|
|
@Test(expected = BadArgumentTypeException.class)
|
|
public void defmacroWithNonSymbolName() {
|
|
evaluateString("(defmacro 1 () ())");
|
|
}
|
|
|
|
@Test(expected = BadArgumentTypeException.class)
|
|
public void defmacroWithBadLambdaList() {
|
|
evaluateString("(defmacro m a ())");
|
|
}
|
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
public void defmacroWithTooFewArguments() {
|
|
evaluateString("(defmacro m)");
|
|
}
|
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
public void defmacroAndCallWithTooFewArguments() {
|
|
evaluateString("(defmacro m (a b))");
|
|
evaluateString("(m a)");
|
|
}
|
|
|
|
@Test(expected = TooManyArgumentsException.class)
|
|
public void defmacroAndCallWithTooManyArguments() {
|
|
evaluateString("(defmacro m (a b))");
|
|
evaluateString("(m a b c)");
|
|
}
|
|
|
|
@Test
|
|
public void defmacroWithKeywordRestParameter() {
|
|
evaluateString("(defmacro m (&rest x) (car x))");
|
|
assertSExpressionsMatch(parseString("1"), evaluateString("(m 1 2 3 4 5)"));
|
|
}
|
|
|
|
@Test
|
|
public void defmacroWithNormalAndKeywordRestParameter() {
|
|
evaluateString("(defmacro m (a &rest b) (list 'cons a (list 'quote b)))");
|
|
assertSExpressionsMatch(parseString("(1 2 3 4 5)"), evaluateString("(m 1 2 3 4 5)"));
|
|
}
|
|
|
|
@Test(expected = IllegalKeywordRestPositionException.class)
|
|
public void defmacroWithParametersFollowingKeywordRest() {
|
|
evaluateString("(defmacro m (a &rest b c) (cons a b))");
|
|
evaluateString("(m 1 2 3)");
|
|
}
|
|
|
|
@Test
|
|
public void defmacroWithKeywordRest_CallWithNoArguments() {
|
|
evaluateString("(defmacro m (&rest a) (car a))");
|
|
assertSExpressionsMatch(parseString("nil"), evaluateString("(m)"));
|
|
}
|
|
|
|
@Test(expected = TooFewArgumentsException.class)
|
|
public void defmacroWithNormalAndKeywordRest_CallWithNoArguments() {
|
|
evaluateString("(defmacro m (a &rest b) a)");
|
|
evaluateString("(m)");
|
|
}
|
|
|
|
@Test
|
|
public void macroIsEvaluatedAfterExpansion() {
|
|
evaluateString("(setq x 'grains)");
|
|
evaluateString("(defmacro m (x) x)");
|
|
evaluateString("(m (setq x 'sprouts))");
|
|
|
|
assertSExpressionsMatch(parseString("sprouts"), evaluateString("x"));
|
|
}
|
|
|
|
}
|