Convert definition tests to kotlin
This commit is contained in:
parent
a871690e70
commit
2e314f9ff0
|
@ -46,8 +46,8 @@ class LoadTest : SymbolAndFunctionCleaner() {
|
||||||
RuntimeEnvironment.errorOutput = PrintStream(errorOutputStream)
|
RuntimeEnvironment.errorOutput = PrintStream(errorOutputStream)
|
||||||
RuntimeEnvironment.errorManager = ErrorManager()
|
RuntimeEnvironment.errorManager = ErrorManager()
|
||||||
RuntimeEnvironment.path = ""
|
RuntimeEnvironment.path = ""
|
||||||
RuntimeEnvironment.warningOutputDecorator = { s -> s }
|
RuntimeEnvironment.warningOutputDecorator = { it }
|
||||||
RuntimeEnvironment.errorOutputDecorator = { s -> s }
|
RuntimeEnvironment.errorOutputDecorator = { it }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun additionalTearDown() {
|
override fun additionalTearDown() {
|
||||||
|
|
|
@ -1,188 +0,0 @@
|
||||||
package function.builtin.special;
|
|
||||||
|
|
||||||
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 org.junit.Test;
|
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
||||||
import static testutil.TestUtilities.evaluateString;
|
|
||||||
import static testutil.TestUtilities.parseString;
|
|
||||||
|
|
||||||
public class DefineSpecialTest extends SymbolAndFunctionCleaner {
|
|
||||||
|
|
||||||
private ByteArrayOutputStream outputStream;
|
|
||||||
private RuntimeEnvironment environment;
|
|
||||||
|
|
||||||
public DefineSpecialTest() {
|
|
||||||
this.environment = RuntimeEnvironment.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 defineSpecial() {
|
|
||||||
String input = "(define-special f () t)";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
||||||
assertSExpressionsMatch(parseString("t"), evaluateString("(f)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialWithEmptyBody() {
|
|
||||||
String input = "(define-special f ())";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
||||||
assertSExpressionsMatch(parseString("()"), evaluateString("(f)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialDoesNotEvaluateArguments() {
|
|
||||||
evaluateString("(define-special f (x) (car x))");
|
|
||||||
assertSExpressionsMatch(parseString("quote"), evaluateString("(f '(1 2 3))"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialAdd() {
|
|
||||||
evaluateString("(define-special f (x) (+ (eval x) 23))");
|
|
||||||
assertSExpressionsMatch(parseString("27"), evaluateString("(f (+ 2 2))"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialSetVariable() {
|
|
||||||
evaluateString("(define-special f (x) (set x 23))");
|
|
||||||
evaluateString("(f y)");
|
|
||||||
assertSExpressionsMatch(parseString("23"), evaluateString("y"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialVariableCapture() {
|
|
||||||
evaluateString("(setq x 0)");
|
|
||||||
evaluateString("(define-special f (x) (set x 23))");
|
|
||||||
evaluateString("(f x)");
|
|
||||||
assertSExpressionsMatch(parseString("0"), evaluateString("x"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialAvoidVariableCaptureConvention() {
|
|
||||||
evaluateString("(setq x 0)");
|
|
||||||
evaluateString("(define-special f (-x-) (set -x- 23))");
|
|
||||||
evaluateString("(f x)");
|
|
||||||
assertSExpressionsMatch(parseString("23"), evaluateString("x"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void redefineSpecial_DisplaysWarning() {
|
|
||||||
String input = "(define-special myFunction () nil)";
|
|
||||||
evaluateString(input);
|
|
||||||
evaluateString(input);
|
|
||||||
|
|
||||||
assertSomethingPrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void redefineSpecial_ActuallyRedefinesSpecialFunction() {
|
|
||||||
evaluateString("(define-special mySpecialFunction () nil)");
|
|
||||||
evaluateString("(define-special mySpecialFunction () T)");
|
|
||||||
|
|
||||||
assertSomethingPrinted();
|
|
||||||
assertSExpressionsMatch(parseString("t"), evaluateString("(mySpecialFunction)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = DottedArgumentListException.class)
|
|
||||||
public void defineSpecialWithDottedLambdaList() {
|
|
||||||
evaluateString("(funcall 'define-special 'x (cons 'a 'b) ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void defineSpecialWithNonSymbolName() {
|
|
||||||
evaluateString("(define-special 1 () ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void defineSpecialWithBadLambdaList() {
|
|
||||||
evaluateString("(define-special x a ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void defineSpecialWithTooFewArguments() {
|
|
||||||
evaluateString("(define-special x)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void defineSpecialAndCallWithTooFewArguments() {
|
|
||||||
evaluateString("(define-special x (a b))");
|
|
||||||
evaluateString("(x a)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooManyArgumentsException.class)
|
|
||||||
public void defineSpecialAndCallWithTooManyArguments() {
|
|
||||||
evaluateString("(define-special x (a b))");
|
|
||||||
evaluateString("(x a b c)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialWithKeywordRestParameter() {
|
|
||||||
evaluateString("(define-special f (&rest x) (car x))");
|
|
||||||
assertSExpressionsMatch(parseString("1"), evaluateString("(f 1 2 3 4 5)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialWithNormalAndKeywordRestParameter() {
|
|
||||||
evaluateString("(define-special 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 defineSpecialWithParametersFollowingKeywordRest() {
|
|
||||||
evaluateString("(define-special f (a &rest b c) (cons a b))");
|
|
||||||
evaluateString("(f 1 2 3)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defineSpecialWithKeywordRest_CallWithNoArguments() {
|
|
||||||
evaluateString("(define-special f (&rest a) (car a))");
|
|
||||||
assertSExpressionsMatch(parseString("nil"), evaluateString("(f)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void defineSpecialWithNormalAndKeywordRest_CallWithNoArguments() {
|
|
||||||
evaluateString("(define-special f (a &rest b) a)");
|
|
||||||
evaluateString("(f)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void resultOfSpecialFunctionIsNotEvaluated() {
|
|
||||||
evaluateString("(setq x 'grains)");
|
|
||||||
evaluateString("(define-special f (x) x)");
|
|
||||||
evaluateString("(f (setq x 'sprouts))");
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("grains"), evaluateString("x"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
package function.builtin.special
|
||||||
|
|
||||||
|
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 org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import testutil.SymbolAndFunctionCleaner
|
||||||
|
import testutil.TestUtilities.assertSExpressionsMatch
|
||||||
|
import testutil.TestUtilities.evaluateString
|
||||||
|
import testutil.TestUtilities.parseString
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.PrintStream
|
||||||
|
|
||||||
|
class DefineSpecialTest : SymbolAndFunctionCleaner() {
|
||||||
|
|
||||||
|
private var outputStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
private fun assertSomethingPrinted() {
|
||||||
|
assertThat(outputStream.toByteArray()).isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalSetUp() {
|
||||||
|
outputStream.reset()
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
RuntimeEnvironment.output = PrintStream(outputStream)
|
||||||
|
RuntimeEnvironment.errorManager = ErrorManager()
|
||||||
|
RuntimeEnvironment.warningOutputDecorator = { it }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalTearDown() {
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecial() {
|
||||||
|
val input = "(define-special f () t)"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("f"), evaluateString(input))
|
||||||
|
assertSExpressionsMatch(parseString("t"), evaluateString("(f)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithEmptyBody() {
|
||||||
|
val input = "(define-special f ())"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("f"), evaluateString(input))
|
||||||
|
assertSExpressionsMatch(parseString("()"), evaluateString("(f)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialDoesNotEvaluateArguments() {
|
||||||
|
evaluateString("(define-special f (x) (car x))")
|
||||||
|
assertSExpressionsMatch(parseString("quote"), evaluateString("(f '(1 2 3))"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialAdd() {
|
||||||
|
evaluateString("(define-special f (x) (+ (eval x) 23))")
|
||||||
|
assertSExpressionsMatch(parseString("27"), evaluateString("(f (+ 2 2))"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialSetVariable() {
|
||||||
|
evaluateString("(define-special f (x) (set x 23))")
|
||||||
|
evaluateString("(f y)")
|
||||||
|
assertSExpressionsMatch(parseString("23"), evaluateString("y"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialVariableCapture() {
|
||||||
|
evaluateString("(setq x 0)")
|
||||||
|
evaluateString("(define-special f (x) (set x 23))")
|
||||||
|
evaluateString("(f x)")
|
||||||
|
assertSExpressionsMatch(parseString("0"), evaluateString("x"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialAvoidVariableCaptureConvention() {
|
||||||
|
evaluateString("(setq x 0)")
|
||||||
|
evaluateString("(define-special f (-x-) (set -x- 23))")
|
||||||
|
evaluateString("(f x)")
|
||||||
|
assertSExpressionsMatch(parseString("23"), evaluateString("x"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun redefineSpecial_DisplaysWarning() {
|
||||||
|
val input = "(define-special myFunction () nil)"
|
||||||
|
evaluateString(input)
|
||||||
|
evaluateString(input)
|
||||||
|
|
||||||
|
assertSomethingPrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun redefineSpecial_ActuallyRedefinesSpecialFunction() {
|
||||||
|
evaluateString("(define-special mySpecialFunction () nil)")
|
||||||
|
evaluateString("(define-special mySpecialFunction () T)")
|
||||||
|
|
||||||
|
assertSomethingPrinted()
|
||||||
|
assertSExpressionsMatch(parseString("t"), evaluateString("(mySpecialFunction)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithDottedLambdaList() {
|
||||||
|
assertThrows(DottedArgumentListException::class.java) {
|
||||||
|
evaluateString("(funcall 'define-special 'x (cons 'a 'b) ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithNonSymbolName() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(define-special 1 () ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithBadLambdaList() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(define-special x a ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithTooFewArguments() {
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(define-special x)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialAndCallWithTooFewArguments() {
|
||||||
|
evaluateString("(define-special x (a b))")
|
||||||
|
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(x a)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialAndCallWithTooManyArguments() {
|
||||||
|
evaluateString("(define-special x (a b))")
|
||||||
|
|
||||||
|
assertThrows(TooManyArgumentsException::class.java) {
|
||||||
|
evaluateString("(x a b c)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithKeywordRestParameter() {
|
||||||
|
evaluateString("(define-special f (&rest x) (car x))")
|
||||||
|
assertSExpressionsMatch(parseString("1"), evaluateString("(f 1 2 3 4 5)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithNormalAndKeywordRestParameter() {
|
||||||
|
evaluateString("(define-special f (a &rest b) (cons a b))")
|
||||||
|
assertSExpressionsMatch(parseString("(1 2 3 4 5)"), evaluateString("(f 1 2 3 4 5)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithParametersFollowingKeywordRest() {
|
||||||
|
assertThrows(IllegalKeywordRestPositionException::class.java) {
|
||||||
|
evaluateString("(define-special f (a &rest b c) (cons a b))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithKeywordRest_CallWithNoArguments() {
|
||||||
|
evaluateString("(define-special f (&rest a) (car a))")
|
||||||
|
assertSExpressionsMatch(parseString("nil"), evaluateString("(f)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defineSpecialWithNormalAndKeywordRest_CallWithNoArguments() {
|
||||||
|
evaluateString("(define-special f (a &rest b) a)")
|
||||||
|
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(f)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resultOfSpecialFunctionIsNotEvaluated() {
|
||||||
|
evaluateString("(setq x 'grains)")
|
||||||
|
evaluateString("(define-special f (x) x)")
|
||||||
|
evaluateString("(f (setq x 'sprouts))")
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("grains"), evaluateString("x"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,189 +0,0 @@
|
||||||
package function.builtin.special;
|
|
||||||
|
|
||||||
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 org.junit.Test;
|
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
||||||
import static testutil.TestUtilities.evaluateString;
|
|
||||||
import static testutil.TestUtilities.parseString;
|
|
||||||
|
|
||||||
public class DefmacroTest extends SymbolAndFunctionCleaner {
|
|
||||||
|
|
||||||
private ByteArrayOutputStream outputStream;
|
|
||||||
private RuntimeEnvironment environment;
|
|
||||||
|
|
||||||
public DefmacroTest() {
|
|
||||||
this.environment = RuntimeEnvironment.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void macroIsEvaluatedCorrectly() {
|
|
||||||
evaluateString("(defmacro m (x) `'(+ 2 ,x))");
|
|
||||||
assertSExpressionsMatch(parseString("(+ 2 25)"), evaluateString("(m 25)"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
package function.builtin.special
|
||||||
|
|
||||||
|
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 org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import testutil.SymbolAndFunctionCleaner
|
||||||
|
import testutil.TestUtilities.assertSExpressionsMatch
|
||||||
|
import testutil.TestUtilities.evaluateString
|
||||||
|
import testutil.TestUtilities.parseString
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.PrintStream
|
||||||
|
|
||||||
|
class DefmacroTest : SymbolAndFunctionCleaner() {
|
||||||
|
|
||||||
|
private var outputStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
private fun assertSomethingPrinted() {
|
||||||
|
assertThat(outputStream.toByteArray()).isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalSetUp() {
|
||||||
|
outputStream.reset()
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
RuntimeEnvironment.output = PrintStream(outputStream)
|
||||||
|
RuntimeEnvironment.errorManager = ErrorManager()
|
||||||
|
RuntimeEnvironment.warningOutputDecorator = { it }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalTearDown() {
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacro() {
|
||||||
|
val input = "(defmacro m () t)"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("m"), evaluateString(input))
|
||||||
|
assertSExpressionsMatch(parseString("t"), evaluateString("(m)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithEmptyBody() {
|
||||||
|
val input = "(defmacro m ())"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("m"), evaluateString(input))
|
||||||
|
assertSExpressionsMatch(parseString("()"), evaluateString("(m)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroDoesNotEvaluateArguments() {
|
||||||
|
evaluateString("(setq x 'grains)")
|
||||||
|
evaluateString("(defmacro m (x))")
|
||||||
|
evaluateString("(m (setq x 'sprouts))")
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("grains"), evaluateString("x"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroAdd() {
|
||||||
|
evaluateString("(defmacro m (x) (+ (eval x) 23))")
|
||||||
|
assertSExpressionsMatch(parseString("27"), evaluateString("(m (+ 2 2))"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroSetVariable() {
|
||||||
|
evaluateString("(defmacro m (x) (set x 23))")
|
||||||
|
evaluateString("(m y)")
|
||||||
|
assertSExpressionsMatch(parseString("23"), evaluateString("y"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroVariableCapture() {
|
||||||
|
evaluateString("(setq x 0)")
|
||||||
|
evaluateString("(defmacro m (x) (set x 23))")
|
||||||
|
evaluateString("(m x)")
|
||||||
|
assertSExpressionsMatch(parseString("0"), evaluateString("x"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun redefineMacro_DisplaysWarning() {
|
||||||
|
val input = "(defmacro myMacro () nil)"
|
||||||
|
evaluateString(input)
|
||||||
|
evaluateString(input)
|
||||||
|
|
||||||
|
assertSomethingPrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun redefineMacro_ActuallyRedefinesSpecialFunction() {
|
||||||
|
evaluateString("(defmacro myMacro () nil)")
|
||||||
|
evaluateString("(defmacro myMacro () T)")
|
||||||
|
|
||||||
|
assertSomethingPrinted()
|
||||||
|
assertSExpressionsMatch(parseString("t"), evaluateString("(myMacro)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithDottedLambdaList() {
|
||||||
|
assertThrows(DottedArgumentListException::class.java) {
|
||||||
|
evaluateString("(funcall 'defmacro 'm (cons 'a 'b) ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithNonSymbolName() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(defmacro 1 () ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithBadLambdaList() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(defmacro m a ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithTooFewArguments() {
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(defmacro m)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroAndCallWithTooFewArguments() {
|
||||||
|
evaluateString("(defmacro m (a b))")
|
||||||
|
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(m a)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroAndCallWithTooManyArguments() {
|
||||||
|
evaluateString("(defmacro m (a b))")
|
||||||
|
|
||||||
|
assertThrows(TooManyArgumentsException::class.java) {
|
||||||
|
evaluateString("(m a b c)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithKeywordRestParameter() {
|
||||||
|
evaluateString("(defmacro m (&rest x) (car x))")
|
||||||
|
assertSExpressionsMatch(parseString("1"), evaluateString("(m 1 2 3 4 5)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun 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
|
||||||
|
fun defmacroWithParametersFollowingKeywordRest() {
|
||||||
|
assertThrows(IllegalKeywordRestPositionException::class.java) {
|
||||||
|
evaluateString("(defmacro m (a &rest b c) (cons a b))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithKeywordRest_CallWithNoArguments() {
|
||||||
|
evaluateString("(defmacro m (&rest a) (car a))")
|
||||||
|
assertSExpressionsMatch(parseString("nil"), evaluateString("(m)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defmacroWithNormalAndKeywordRest_CallWithNoArguments() {
|
||||||
|
evaluateString("(defmacro m (a &rest b) a)")
|
||||||
|
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(m)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun macroIsEvaluatedAfterExpansion() {
|
||||||
|
evaluateString("(setq x 'grains)")
|
||||||
|
evaluateString("(defmacro m (x) x)")
|
||||||
|
evaluateString("(m (setq x 'sprouts))")
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("sprouts"), evaluateString("x"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun macroIsEvaluatedCorrectly() {
|
||||||
|
evaluateString("(defmacro m (x) `'(+ 2 ,x))")
|
||||||
|
assertSExpressionsMatch(parseString("(+ 2 25)"), evaluateString("(m 25)"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,187 +0,0 @@
|
||||||
package function.builtin.special;
|
|
||||||
|
|
||||||
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 org.junit.Test;
|
|
||||||
import testutil.SymbolAndFunctionCleaner;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static testutil.TestUtilities.assertSExpressionsMatch;
|
|
||||||
import static testutil.TestUtilities.evaluateString;
|
|
||||||
import static testutil.TestUtilities.parseString;
|
|
||||||
|
|
||||||
public class DefunTest extends SymbolAndFunctionCleaner {
|
|
||||||
|
|
||||||
private ByteArrayOutputStream outputStream;
|
|
||||||
private RuntimeEnvironment environment;
|
|
||||||
|
|
||||||
public DefunTest() {
|
|
||||||
this.environment = RuntimeEnvironment.INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 defun() {
|
|
||||||
String input = "(defun f () t)";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
||||||
assertSExpressionsMatch(parseString("t"), evaluateString("(f)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defunWithEmptyBody() {
|
|
||||||
String input = "(defun f ())";
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("f"), evaluateString(input));
|
|
||||||
assertSExpressionsMatch(parseString("()"), evaluateString("(f)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void defunEvaluatesArguments() {
|
|
||||||
evaluateString("(defun f (x) (car x))");
|
|
||||||
assertSExpressionsMatch(parseString("1"), evaluateString("(f '(1 2 3))"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@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() {
|
|
||||||
evaluateString("(defun counter-class () (let ((counter 0)) (lambda () (setq counter (+ 1 counter)))))");
|
|
||||||
evaluateString("(setq my-counter (counter-class))");
|
|
||||||
|
|
||||||
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)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void redefineFunction_DisplaysWarning() {
|
|
||||||
String input = "(defun myFunction () nil)";
|
|
||||||
evaluateString(input);
|
|
||||||
evaluateString(input);
|
|
||||||
|
|
||||||
assertSomethingPrinted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void redefineFunction_ActuallyRedefinesFunction() {
|
|
||||||
evaluateString("(defun myFunction () nil)");
|
|
||||||
evaluateString("(defun myFunction () T)");
|
|
||||||
|
|
||||||
assertSomethingPrinted();
|
|
||||||
assertSExpressionsMatch(parseString("t"), evaluateString("(myFunction)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = DottedArgumentListException.class)
|
|
||||||
public void defunWithDottedLambdaList() {
|
|
||||||
evaluateString("(funcall 'defun 'f (cons 'a 'b) ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void defunWithNonSymbolName() {
|
|
||||||
evaluateString("(defun 1 () ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void defunWithBadLambdaList() {
|
|
||||||
evaluateString("(defun f a ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = BadArgumentTypeException.class)
|
|
||||||
public void defunWithNonSymbolInLambdaList() {
|
|
||||||
evaluateString("(defun f (1) ())");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void defunWithTooFewArguments() {
|
|
||||||
evaluateString("(defun f)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooFewArgumentsException.class)
|
|
||||||
public void defunFunctionAndCallWithTooFewArguments() {
|
|
||||||
evaluateString("(defun f (a b))");
|
|
||||||
evaluateString("(f 'a)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = TooManyArgumentsException.class)
|
|
||||||
public void defunFunctionAndCallWithTooManyArguments() {
|
|
||||||
evaluateString("(defun f (a b))");
|
|
||||||
evaluateString("(f '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)");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void resultOfFunctionIsNotEvaluated() {
|
|
||||||
evaluateString("(setq x 'grains)");
|
|
||||||
evaluateString("(define-special f (x) 'x)");
|
|
||||||
evaluateString("(f (setq x 'sprouts))");
|
|
||||||
|
|
||||||
assertSExpressionsMatch(parseString("grains"), evaluateString("x"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
package function.builtin.special
|
||||||
|
|
||||||
|
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 org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import testutil.SymbolAndFunctionCleaner
|
||||||
|
import testutil.TestUtilities.assertSExpressionsMatch
|
||||||
|
import testutil.TestUtilities.evaluateString
|
||||||
|
import testutil.TestUtilities.parseString
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.PrintStream
|
||||||
|
|
||||||
|
class DefunTest : SymbolAndFunctionCleaner() {
|
||||||
|
|
||||||
|
private var outputStream = ByteArrayOutputStream()
|
||||||
|
|
||||||
|
private fun assertSomethingPrinted() {
|
||||||
|
assertThat(outputStream.toByteArray()).isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalSetUp() {
|
||||||
|
outputStream.reset()
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
RuntimeEnvironment.output = PrintStream(outputStream)
|
||||||
|
RuntimeEnvironment.errorManager = ErrorManager()
|
||||||
|
RuntimeEnvironment.warningOutputDecorator = { it }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun additionalTearDown() {
|
||||||
|
RuntimeEnvironment.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defun() {
|
||||||
|
val input = "(defun f () t)"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("f"), evaluateString(input))
|
||||||
|
assertSExpressionsMatch(parseString("t"), evaluateString("(f)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithEmptyBody() {
|
||||||
|
val input = "(defun f ())"
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("f"), evaluateString(input))
|
||||||
|
assertSExpressionsMatch(parseString("()"), evaluateString("(f)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunEvaluatesArguments() {
|
||||||
|
evaluateString("(defun f (x) (car x))")
|
||||||
|
assertSExpressionsMatch(parseString("1"), evaluateString("(f '(1 2 3))"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunRecursiveFunction() {
|
||||||
|
evaluateString("(defun fact (x) (if (< x 2) 1 (* x (fact (- x 1)))))")
|
||||||
|
assertSExpressionsMatch(parseString("120"), evaluateString("(fact 5)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun 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
|
||||||
|
fun defunSimpleClass() {
|
||||||
|
evaluateString("(defun counter-class () (let ((counter 0)) (lambda () (setq counter (+ 1 counter)))))")
|
||||||
|
evaluateString("(setq my-counter (counter-class))")
|
||||||
|
|
||||||
|
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)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun redefineFunction_DisplaysWarning() {
|
||||||
|
val input = "(defun myFunction () nil)"
|
||||||
|
evaluateString(input)
|
||||||
|
evaluateString(input)
|
||||||
|
|
||||||
|
assertSomethingPrinted()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun redefineFunction_ActuallyRedefinesFunction() {
|
||||||
|
evaluateString("(defun myFunction () nil)")
|
||||||
|
evaluateString("(defun myFunction () T)")
|
||||||
|
|
||||||
|
assertSomethingPrinted()
|
||||||
|
assertSExpressionsMatch(parseString("t"), evaluateString("(myFunction)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithDottedLambdaList() {
|
||||||
|
assertThrows(DottedArgumentListException::class.java) {
|
||||||
|
evaluateString("(funcall 'defun 'f (cons 'a 'b) ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithNonSymbolName() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(defun 1 () ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithBadLambdaList() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(defun f a ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithNonSymbolInLambdaList() {
|
||||||
|
assertThrows(BadArgumentTypeException::class.java) {
|
||||||
|
evaluateString("(defun f (1) ())")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithTooFewArguments() {
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(defun f)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunFunctionAndCallWithTooFewArguments() {
|
||||||
|
evaluateString("(defun f (a b))")
|
||||||
|
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(f 'a)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunFunctionAndCallWithTooManyArguments() {
|
||||||
|
evaluateString("(defun f (a b))")
|
||||||
|
|
||||||
|
assertThrows(TooManyArgumentsException::class.java) {
|
||||||
|
evaluateString("(f 'a 'b 'c)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithKeywordRestParameter() {
|
||||||
|
evaluateString("(defun f (&rest x) (car x))")
|
||||||
|
assertSExpressionsMatch(parseString("1"), evaluateString("(f 1 2 3 4 5)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun 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
|
||||||
|
fun defunWithParametersFollowingKeywordRest() {
|
||||||
|
assertThrows(IllegalKeywordRestPositionException::class.java) {
|
||||||
|
evaluateString("(defun f (a &rest b c) (cons a b))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithKeywordRest_CallWithNoArguments() {
|
||||||
|
evaluateString("(defun f (&rest a) (car a))")
|
||||||
|
assertSExpressionsMatch(parseString("nil"), evaluateString("(f)"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun defunWithNormalAndKeywordRest_CallWithNoArguments() {
|
||||||
|
evaluateString("(defun f (a &rest b) a)")
|
||||||
|
|
||||||
|
assertThrows(TooFewArgumentsException::class.java) {
|
||||||
|
evaluateString("(f)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun resultOfFunctionIsNotEvaluated() {
|
||||||
|
evaluateString("(setq x 'grains)")
|
||||||
|
evaluateString("(define-special f (x) 'x)")
|
||||||
|
evaluateString("(f (setq x 'sprouts))")
|
||||||
|
|
||||||
|
assertSExpressionsMatch(parseString("grains"), evaluateString("x"))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue