Пример #1
0
    def test_defining_then_looking_up_function(self):
        """Test calling named function that's been previously defined 
        from the environment"""

        env = Environment()
        evaluate(["define", "my-fn", ["lambda", ["x"], "x"]], env)
        assert_equals(integer(42), evaluate(["my-fn", integer(42)], env))
Пример #2
0
    def test_calling_with_wrong_number_of_arguments(self):
        """Lambda should raise exception when called with wrong number of arguments"""

        env = Environment()
        evaluate(["define", "fn", ["lambda", ["x", "y"], integer(42)]], env)
        with assert_raises_regexp(LispTypeError, "expected 2"):
            evaluate(["fn", integer(1)], env)
Пример #3
0
    def test_set_bang(self):
        """The set! special form updates an already defined variable."""

        env = Environment({"x": integer(1)})
        ast = ["set!", "x", integer(2)]
        evaluate(ast, env)
        assert_equals(integer(2), env["x"])
Пример #4
0
    def test_define_with_wrong_number_of_arguments(self):
        """Defines should have exactly two arguments, or raise an error"""

        with assert_raises_regexp(LispSyntaxError, "Wrong number of arguments"):
            evaluate(["define", "x"], Environment())

        with assert_raises_regexp(LispSyntaxError, "Wrong number of arguments"):
            evaluate(["define", "x", integer(1), integer(2)], Environment())
Пример #5
0
    def test_eval_unquote_outside_of_quasiquote_raises_exception(self):
        """Unquote cannot stand alone, without an *directly enclosing* quasiquote."""

        with assert_raises(LispError):
            # standalone unquote
            evaluate(["unquote", "foo"], Environment())

        with assert_raises(LispError):
            # unquote within another unquote
            ast = ["quasiquote", ["unquote", ["unquote", "foo"]]]
            evaluate(ast, Environment())
Пример #6
0
 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())
Пример #7
0
    def test_set_bang_only_updates_visible_variable(self):
        """Only the innermost (visible) variable binding should be updated"""

        outer = Environment({"x": integer(1)})
        middle = Environment({"x": integer(2)}, outer)
        inner = Environment({}, middle)

        evaluate(["set!", "x", integer(3)], inner)

        assert_false("x" in inner)
        assert_equals(integer(3), middle["x"])
        assert_equals(integer(1), outer["x"])
Пример #8
0
    def test_eval_simple_expression(self):
        """Eval takes one argument, evaluates it (like all functions) and 
        evaluates the evaluated argument"""

        # equivalent to evaluate("foo")
        with assert_raises_regexp(LispNamingError, "Variable 'foo' is undefined"):
            assert_equals("foo", evaluate(["eval", ["quote", "foo"]], Environment()))

        # equivalent to evaluate(["quote, "foo"])
        ast = ["eval", ["quote", ["quote", "foo"]]]
        assert_equals("foo", evaluate(ast, Environment()))

        # equivalent to evaluate(["quote, ["quote, "foo"]])
        ast = ["eval", ["quote", ["quote", ["quote", "foo"]]]]
        assert_equals(["quote", "foo"], evaluate(ast, Environment()))
Пример #9
0
    def test_quote(self):
        """Quoting returns the expression being quoted without evaluating it."""

        ast = ["quote", 
                    ["foo", ["+", integer(1), integer(2)], 
                            ["*", integer(4), integer(10)]]]
        assert_equals(ast[1], evaluate(ast, Environment()))
Пример #10
0
    def test_lambda_with_argument_and_env(self):
        """Test that arguments overshadow variables defined in the environment
        when the function body is evaluated"""

        env = Environment({"x": integer(1)})
        ast = [["lambda", ["x"], "x"], integer(2)]
        assert_equals(integer(2), evaluate(ast, env))
Пример #11
0
    def test_lambda_with_free_var(self):
        """Tests that the lambda have access to variables 
        from the environment in which it was defined"""

        env = Environment({"free-variable": integer(100)})
        ast = [["lambda", [], "free-variable"]]
        assert_equals(integer(100), evaluate(ast, env))
Пример #12
0
    def test_lambda_evaluates_to_lambda_which_is_a_closure(self):
        """The lambda form should evaluate to a lambda object extending closure"""

        ast = ["lambda", [], integer(42)]
        lm = evaluate(ast, Environment())
        assert_is_instance(lm, Lambda) 
        assert_is_instance(lm, Closure) 
Пример #13
0
    def test_lambda_closure_keeps_defining_env(self):
        """The closure should keep a copy of the environment where it was defined"""

        env = Environment({"foo": integer(1), "bar": integer(2)})
        ast = ["lambda", [], integer(42)]
        lm = evaluate(ast, env)
        assert_equals(lm.env, env) 
Пример #14
0
 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()))
Пример #15
0
 def test_lambda_closure_holds_function(self):
     "The function part of the lambda closure is the parameters and the body"
     params = ["x", "y"]
     body = ["+", "x", "y"]
     ast = ["lambda", params, body]
     lm = evaluate(ast, Environment)
     assert_equals(lm.params, params)
     assert_equals(lm.body, body)
Пример #16
0
    def test_defining_macro(self):
        """(macro ...) should return something of type macro"""

        macro = evaluate(["macro", ["foo", "bar"], ["quote", ["bar", "foo"]]], 
            Environment())
        assert_true(is_macro(macro))
        assert_equals(["foo", "bar"], macro.params)
        assert_equals(["quote", ["bar", "foo"]], macro.body)
Пример #17
0
    def test_eq_on_non_atoms(self):
        """Only atoms can equal with eq

        Two lists are not equal (as measured by `eq`) even if they contains
        the same elements."""

        env = Environment()
        ast = ["eq", ["quote", ["foo", "bar"]], ["quote", ["foo", "bar"]]]
        assert_equals(boolean(False), evaluate(ast, env))
Пример #18
0
    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"])
Пример #19
0
    def test_calling_builtin_forces_argument_evaluation(self):
        """Bultins (like the regular lambdas) are call-by-value 
        and the arguments thould therefore be evaluated first"""

        env = Environment({
            'x': integer(2), 
            '+': Builtin(lambda a, b: integer(value_of(a) + value_of(b)))
        })
        ast = ['+', ['cond', 
                        [boolean(True), integer(2)], 
                        [boolean(True), 'whatever']], 
                    'x']
        assert_equals(integer(4), evaluate(ast, env))
Пример #20
0
    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)
Пример #21
0
    def test_atom(self):
        env = Environment()
        assert_equals(boolean(True), 
            evaluate(["atom", boolean(True)], env))

        assert_equals(boolean(True), 
            evaluate(["atom", boolean(False)], env))

        assert_equals(boolean(True), 
            evaluate(["atom", integer(42)], env))

        assert_equals(boolean(True), 
            evaluate(["atom", "foo"], Environment({"foo": "bar"})))

        assert_equals(boolean(False), 
            evaluate(["atom", "foo"], Environment({"foo": ["bar"]})))

        assert_equals(boolean(False), 
            evaluate(["atom", ["quote", ["foo", "bar"]]], env))
Пример #22
0
    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))
Пример #23
0
    def test_let_raises_error_wrong_number_of_elements(self):
        """A variable definition should consist of only two elements,
        the variable and the value"""

        with assert_raises_regexp(LispSyntaxError, "Wrong number of arguments"):
            evaluate(parse("(let ((foo 1 2)) foo)"), Environment())
Пример #24
0
    def test_define_with_nonsymbol_as_variable(self):
        """Malformed defines should throw an error"""

        with assert_raises_regexp(LispSyntaxError, "non-symbol"):
            evaluate(["define", boolean(True), integer(42)], Environment())
Пример #25
0
    def test_define(self):
        """Test simplest possible define"""

        env = Environment()
        evaluate(["define", "x", integer(1000)], env)
        assert_equals(integer(1000), env["x"])
Пример #26
0
 def test_simple_quasiquote_with_unquotes(self):
     env = Environment({"bar": 'inc', "foo": integer(42)})
     ast = ['quasiquote', [['unquote', 'bar'], ['unquote', 'foo']]]
     assert_equals(parse("`(,bar ,foo)"), ast)
     assert_equals(['inc', integer(42)], evaluate(ast, env))
Пример #27
0
    def test_eq_on_two_different_atoms(self):
        """Two different atoms are not equal"""

        ast = ["eq", ["quote", "foo"], ["quote", "bar"]]
        assert_equals(boolean(False), evaluate(ast, Environment()))
Пример #28
0
    def test_eq_on_two_equal_atoms(self):
        """Two of the same atom are equal"""

        ast = ["eq", ["quote", "foo"], ["quote", "foo"]]
        assert_equals(boolean(True), evaluate(ast, Environment()))
Пример #29
0
 def test_eval_quasiquote_with_deeper_unquotes(self):
     env = Environment({"foo": integer(42), "bar": integer(100)})
     ast = ["quasiquote", 
             ["+", ["unquote", "foo"], ["+", integer(1), ["unquote", "bar"]]]]
     assert_equals(["+", integer(42), ["+", integer(1), integer(100)]], 
         evaluate(ast, env))
Пример #30
0
    def test_let_raises_error_on_nonsymbol_as_variable(self):
        """Attempting to define something other than a symbol as a variable should
        result in an exception"""

        with assert_raises_regexp(LispSyntaxError, "non-symbol"):
            evaluate(parse("(let ((#t 1)) 1)"), Environment())