def test_nested_quasiquotes(self): assert_equals(["quasiquote", ["quasiquote", ["quasiquote", "foo"]]], parse("```foo")) assert_equals(["quasiquote", ["quasiquote", ["quasiquote", ["+", integer(1), integer(2)]]]], parse("```(+ 1 2)"))
def test_cond(self): program = """ (cond (#f 1) ((atom '(1 2 3)) 2) ((atom 'foo) 3) (#t 4)) """ assert_equals(integer(3), evaluate(parse(program), Environment()))
def test_simple_let_expression(self): """Let expressions should create a new environment with the new definitions, then evaluate the body with this environment, and not make any changes to the outer environment""" env = Environment({"foo": integer(1)}) ast = parse("(let ((foo 2)) foo)") assert_equals(integer(2), evaluate(ast, env)) assert_equals(integer(1), env["foo"])
def test_defining_lambda_with_error(self): """Tests that the lambda body is not being evaluated when the lambda is evaluated or defined. (It should first be evaluated when the function is later invoced.)""" ast = parse(""" (define fn-with-error (lambda (x y) (function body that would never work))) """) evaluate(ast, Environment())
def test_parse_comments(self): program = """ ;; this first line is a comment (define variable ; here is another comment (if #t 42 ; inline comment! (something else))) """ expected_ast = ['define', 'variable', ['if', boolean(True), integer(42), ['something', 'else']]] assert_equals(expected_ast, parse(program))
def test_begin_form(self): """Testing evaluating expressions in sequence with the begin special form""" env = Environment() result = evaluate(parse(""" (begin (define foo 1) (define bar 2) foo) """), env) assert_equals(integer(1), result) assert_equals(Environment({"foo": integer(1), "bar": integer(2)}), env)
def test_calling_function_recursively(self): """Tests that a named function is included in the environment where it is evaluated""" oposite = """ (define oposite (lambda (p) (cond (p #f) (#t #t)))) """ fn = """ (define fn ;; Meaningless (albeit recursive) function (lambda (x) (cond (x (fn (oposite x))) (#t 1000)))) """ env = Environment() evaluate(parse(oposite), env) evaluate(parse(fn), env) assert_equals(integer(1000), evaluate(["fn", boolean(True)], env)) assert_equals(integer(1000), evaluate(["fn", boolean(False)], env))
def test_parse_quote_tick_on_symbol(self): assert_equals(["quote", "foo"], parse("'foo")) assert_equals(["quote", "+"], parse("'+"))
def test_expand_quotes_with_only_symbols(self): assert_equals(["quote", "foo"], parse("'foo")) assert_equals(["quote", ["quote", ["quote", "foo"]]], parse("'''foo"))
def test_expand_quoted_symbol_dont_touch_nested_quote_on_list(self): source = "(foo ''(bar))" assert_equals(source, unparse(parse(source)))
def test_expand_single_quoted_symbol(self): assert_equals(["foo", ["quote", "bar"]], parse("(foo 'bar)")) assert_equals(["foo", ["quote", boolean(True)]], parse("(foo '#t)")) assert_equals(["foo", ["quote", '+']], parse("(foo '+)"))
def test_parse_list_of_symbols(self): assert_equals(['foo', 'bar', 'baz'], parse('(foo bar baz)'))
def test_expand_single_quoted_list(self): assert_equals(["foo", ["quote", ["+", integer(1), integer(2)]]], parse("(foo '(+ 1 2))")) assert_equals(["foo", ["quote", [boolean(True), boolean(False)]]], parse("(foo '(#t #f))"))
def test_parse_quote_tick_on_list(self): assert_equals(["quote", ["foo", "bar"]], parse("'(foo bar)")) assert_equals(["quote", []], parse("'()"))
def test_parse_with_types(self): program = '(if #f (* 42 x) 100)' ast = ['if', boolean(False), ['*', integer(42), 'x'], integer(100)] assert_equals(ast, parse(program))
def test_parse_exception_extra_paren(self): with assert_raises_regexp(LispSyntaxError, 'Expected EOF'): parse('(foo (bar x y)))')
def test_parse_exception_missing_paren(self): with assert_raises_regexp(LispSyntaxError, 'Unbalanced expression'): parse('(foo (bar x y)')
def test_parse_on_nested_list(self): program = '(foo (bar x y) (baz x))' ast = ['foo', ['bar', 'x', 'y'], ['baz', 'x']] assert_equals(ast, parse(program))
def test_parse_on_simple_list(self): program = '(foo bar)' assert_equals(['foo', 'bar'], parse(program))
def test_parse_quote_tick_on_atom(self): assert_equals(["quote", integer(1)], parse("'1")) assert_equals(["quote", boolean(True)], parse("'#t"))
def test_nested_quotes(self): assert_equals(["quote", ["quote", "foo"]], parse("''foo")) assert_equals(["quote", ["quote", ["quote", "foo"]]], parse("'''foo"))
def test_expand_quasiquoted_symbol(self): assert_equals(["quasiquote", "foo"], parse("`foo")) assert_equals(["quasiquote", "+"], parse("`+")) assert_equals(["quasiquote", boolean(False)], parse("`#f"))
def test_expand_quotes_with_lists(self): assert_equals(["quote", ["foo", "bar"]], parse("'(foo bar)")) assert_equals(["quote", ["quote", ["quote", ["foo", "bar"]]]], parse("'''(foo bar)"))
def test_parse_single_atom(self): assert_equals('foo', parse('foo'))
def test_nested_quotes_on_lists(self): assert_equals(["quote", ["quote", ["foo", "bar"]]], parse("''(foo bar)"))
def test_quasiqute_with_unquote(self): assert_equals(["quasiquote", ["+", ["unquote", "foo"], ["unquote", "bar"], integer(42)]], parse("`(+ ,foo ,bar 42)"))
def test_expand_quasiquoted_list(self): assert_equals(["quasiquote", ["+", integer(1), integer(2)]], parse("`(+ 1 2)"))
def test_expand_quote_combinations(self): assert_equals(["quasiquote", ["quote", ["unquote", "foo"]]], parse("`',foo"))
def test_expand_unquoted_symbol(self): assert_equals(["unquote", "foo"], parse(",foo")) assert_equals(["unquote", "+"], parse(",+")) assert_equals(["unquote", boolean(False)], parse(",#f"))
def test_expand_crazy_quote_combo(self): source = "`(this ,,'`(makes ,no) 'sense)" assert_equals(source, unparse(parse(source)))