package function.builtin.special; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static sexpression.LispNumber.ONE; import static sexpression.Nil.NIL; import static sexpression.Symbol.T; import static testutil.TestUtilities.assertSExpressionsMatch; import static testutil.TestUtilities.evaluateString; import static testutil.TestUtilities.parseString; import org.junit.Test; import function.ArgumentValidator.BadArgumentTypeException; import function.ArgumentValidator.DottedArgumentListException; import function.ArgumentValidator.TooFewArgumentsException; import function.ArgumentValidator.TooManyArgumentsException; import function.builtin.EVAL.UndefinedFunctionException; import sexpression.Cons; import sexpression.LispNumber; import sexpression.Symbol; import testutil.SymbolAndFunctionCleaner; public class LAMBDATest extends SymbolAndFunctionCleaner { @Test public void lambda() { String input = "(lambda (x) x)"; assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input)); } @Test public void lambdaSymbol() { String input = "(λ (x) x)"; assertSExpressionsMatch(parseString("(LAMBDA (X) X)"), evaluateString(input)); } @Test public void lambdaWithNoBody() { String input = "(lambda ())"; assertSExpressionsMatch(parseString("(LAMBDA ())"), evaluateString(input)); } @Test public void lambdaExpressionIsLambdaExpression() { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, new Cons(NIL, NIL))); assertTrue(LAMBDA.isLambdaExpression(lambdaExpression)); } @Test public void somethingElseIsNotLambdaExpression() { assertFalse(LAMBDA.isLambdaExpression(T)); } @Test public void createLambdaExpression() { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, new Cons(NIL, NIL))); assertSExpressionsMatch(parseString("(:LAMBDA () ())"), LAMBDA.createFunction(lambdaExpression).getLambdaExpression()); } @Test(expected = DottedArgumentListException.class) public void lambdaWithDottedArgumentList() { String input = "(apply 'lambda (cons '(x) 1))"; evaluateString(input); } @Test(expected = DottedArgumentListException.class) public void lambdaWithDottedLambdaList() { String input = "(funcall 'lambda (cons 'a 'b) ())"; evaluateString(input); } @Test(expected = DottedArgumentListException.class) public void createFunctionWithDottedArgumentList() { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), new Cons(NIL, ONE)); LAMBDA.createFunction(lambdaExpression); } @Test(expected = BadArgumentTypeException.class) public void createFunctionWithNonList() { Cons lambdaExpression = new Cons(new Symbol("LAMBDA"), ONE); LAMBDA.createFunction(lambdaExpression); } @Test(expected = BadArgumentTypeException.class) public void lambdaWithNonSymbolParameter() { evaluateString("(lambda (1) ())"); } @Test(expected = TooFewArgumentsException.class) public void lambdaWithTooFewArguments() { evaluateString("(lambda)"); } @Test public void anonymousLambdaCall() { String input = "((lambda (x) x) 203)"; assertSExpressionsMatch(new LispNumber("203"), evaluateString(input)); } @Test public void anonymousLambdaCallWithMultipleArguments() { String input = "((lambda (x y) (+ x y)) 203 2)"; assertSExpressionsMatch(new LispNumber("205"), evaluateString(input)); } @Test public void anonymousLambdaCallWithSymbol() { String input = "((λ (x) (+ x 1)) 3)"; assertSExpressionsMatch(new LispNumber("4"), evaluateString(input)); } @Test(expected = TooFewArgumentsException.class) public void anonymousLambdaCallWithTooFewArguments() { evaluateString("((lambda (x) x))"); } @Test(expected = TooManyArgumentsException.class) public void anonymousLambdaCallWithTooManyArguments() { evaluateString("((lambda (x y) x) 1 2 3)"); } @Test(expected = UndefinedFunctionException.class) public void badAnonymousFunctionCall() { evaluateString("((bad-lambda (x y) x) 1 2 3)"); } @Test public void lexicalClosure() { evaluateString("(setq increment-count (let ((counter 0)) (lambda () (setq counter (+ 1 counter)))))"); assertSExpressionsMatch(parseString("1"), evaluateString("(funcall increment-count)")); assertSExpressionsMatch(parseString("2"), evaluateString("(funcall increment-count)")); assertSExpressionsMatch(parseString("3"), evaluateString("(funcall increment-count)")); assertSExpressionsMatch(parseString("4"), evaluateString("(funcall increment-count)")); } }