package function.builtin; import static function.builtin.EVAL.lookupSymbol; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static sexpression.Nil.NIL; import static testutil.TestUtilities.assertIsErrorWithMessage; import static testutil.TestUtilities.assertSExpressionsMatch; import static testutil.TestUtilities.evaluateString; import static testutil.TestUtilities.parseString; import org.junit.Test; import function.ArgumentValidator.DottedArgumentListException; import function.ArgumentValidator.TooFewArgumentsException; import function.ArgumentValidator.TooManyArgumentsException; import function.builtin.BackquoteEvaluator.AtSignNotInCommaException; import function.builtin.EVAL.UndefinedFunctionException; import function.builtin.EVAL.UndefinedSymbolException; import function.builtin.EVAL.UnmatchedAtSignException; import function.builtin.EVAL.UnmatchedCommaException; import function.builtin.special.RECUR.RecurNotInTailPositionException; import testutil.SymbolAndFunctionCleaner; public class EVALTest extends SymbolAndFunctionCleaner { @Test public void evalNumber() { String input = "(eval 9)"; assertSExpressionsMatch(parseString("9"), evaluateString(input)); } @Test public void evalNil() { String input = "(eval ())"; assertSExpressionsMatch(parseString("()"), evaluateString(input)); } @Test public void lookupKeywordSymbol() { String symbol = ":symbol"; assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol)); } @Test public void lookupT() { String symbol = "T"; assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol)); } @Test public void lookupNil() { String symbol = "NIL"; assertSExpressionsMatch(parseString(symbol), lookupSymbol(symbol)); } @Test public void lookupUndefinedSymbol() { assertNull(EVAL.lookupSymbol("undefined")); } @Test(expected = UndefinedFunctionException.class) public void evalUndefinedFunction() { String input = "(funcall 'eval '(undefined))"; evaluateString(input); } @Test(expected = UndefinedSymbolException.class) public void evalUndefinedSymbol() { String input = "(eval undefined)"; evaluateString(input); } @Test(expected = DottedArgumentListException.class) public void evalWithDottedLambdaList() { String input = "(funcall 'eval (cons '+ 1))"; evaluateString(input); } @Test(expected = TooManyArgumentsException.class) public void evalWithTooManyArguments() { evaluateString("(eval '1 '2 '3)"); } @Test(expected = TooFewArgumentsException.class) public void evalWithTooFewArguments() { evaluateString("(eval)"); } @Test public void undefinedFunctionException_HasCorrectAttributes() { assertIsErrorWithMessage(new UndefinedFunctionException(NIL)); } @Test public void undefinedSymbolException_HasCorrectAttributes() { assertIsErrorWithMessage(new UndefinedSymbolException(NIL)); } @Test(expected = UnmatchedCommaException.class) public void evalComma() { String input = ",a"; evaluateString(input); } @Test(expected = UnmatchedAtSignException.class) public void evalAtSign() { String input = "@a"; evaluateString(input); } @Test public void evalBackTick() { String input = "`(a b c)"; assertSExpressionsMatch(parseString("(a b c)"), evaluateString(input)); } @Test public void evalBackTickWithCommasAndAtSigns() { String input = "(let ((x '(1 2 3)) (y '(4 5 6)) (z 'apple)) `(start ,x ,@y ,z end))"; assertSExpressionsMatch(parseString("(start (1 2 3) 4 5 6 apple end)"), evaluateString(input)); } @Test public void evalBackTickOnComma() { String input = "`,9"; assertSExpressionsMatch(parseString("9"), evaluateString(input)); } @Test(expected = AtSignNotInCommaException.class) public void evalBackTickOnAtSign() { evaluateString("`@9"); } @Test public void evalNestedBackquotes() { String input = "`,`,`,`,9"; assertSExpressionsMatch(parseString("9"), evaluateString(input)); } @Test public void unmatchedCommaException_HasCorrectAttributes() { assertIsErrorWithMessage(new UnmatchedCommaException()); } @Test public void unmatchedAtSignException_HasCorrectAttributes() { assertIsErrorWithMessage(new UnmatchedAtSignException()); } @Test public void evalQuoteInNestedList() { String input = "(let ((g 27)) `((,g)))"; assertSExpressionsMatch(parseString("((27))"), evaluateString(input)); } @Test public void evalAtSignInNestedList() { String input = "(let ((g '(1 2 3))) `((,@g)))"; assertSExpressionsMatch(parseString("((1 2 3))"), evaluateString(input)); } @Test public void evalNestedBackquotesInList() { String input = "`(,`(1 ,`2 ,@`(3)))"; assertSExpressionsMatch(parseString("((1 2 3))"), evaluateString(input)); } @Test public void scopeRestoredAfterFailure_Let() { evaluateString("(setq n 100)"); try { evaluateString("(let ((n 200)) (begin 1 2 3 y))"); fail("expected exception"); } catch (UndefinedSymbolException e) {} assertSExpressionsMatch(parseString("100"), evaluateString("n")); } @Test public void scopeRestoredAfterFailure_Defun() { evaluateString("(setq n 100)"); try { evaluateString("(defun test (n) (begin 1 2 3 y))"); evaluateString("(test 200)"); fail("expected exception"); } catch (UndefinedSymbolException e) {} assertSExpressionsMatch(parseString("100"), evaluateString("n")); } @Test public void scopeRestoredAfterFailure_Lambda() { evaluateString("(setq n 100)"); try { evaluateString("((lambda (n) (begin 1 2 3 y)) 200)"); fail("expected exception"); } catch (UndefinedSymbolException e) {} assertSExpressionsMatch(parseString("100"), evaluateString("n")); } @Test public void scopeRestoredAfterFailure_Recur() { evaluateString("(setq n 100)"); try { evaluateString("(defun tail-recursive (n) (begin (recur) 2))"); evaluateString("(tail-recursive 200)"); fail("expected exception"); } catch (RecurNotInTailPositionException e) {} assertSExpressionsMatch(parseString("100"), evaluateString("n")); } }