def test_expand_simple_macro_once(self): """expand-1 should expand macro call expression once""" env = default_env() interpret("""(define swp (macro (foo bar) (list bar foo)))""", env) assert_equals("(42 #t)", interpret("(expand-1 '(swp #t 42))", env))
def test_if_with_variable_lookup(self): """Test evaluation of expressions (variable lookup) within if form""" env = default_env() interpret("(define pred #f)", env) interpret("(define else 42)", env) assert_equals("42", interpret("(if pred then else)", env))
def test_expanding_recursive_macro(self): """Recursive macros just keep on expanding if recursive call is root Based on this clojure example: user=> (defmacro test [x] `(test ~x)) #'user/test user=> (macroexpand-1 '(test true)) (user/test true) user=> (macroexpand-1 (macroexpand-1 (macroexpand-1 '(test true)))) (user/test true) user=> (macroexpand '(test true)) StackOverflowError clojure.lang.ASeq.more (ASeq.java:116) """ env = Environment() interpret("""(define test (macro (x) `(test ,x)))""", env) assert_equals("(test #t)", interpret("(expand-1 '(test #t))", env)) assert_equals("(test #t)", interpret("(expand-1 (expand-1 '(test #t)))", env)) assert_equals("(test #t)", interpret("(expand-1 (expand-1 (expand-1 '(test #t))))", env))
def test_creating_lists(self): """Test different ways to create lists""" xs = "(1 2 #t 4)" assert_equals(xs, interpret("(quote (1 2 #t 4))", self.env)) assert_equals(xs, interpret("(cons 1 (cons 2 (cons #t (cons 4 '()))))", self.env)) assert_equals(xs, interpret("(list 1 2 #t 4)", self.env))
def test_deconstruction_of_lists(self): """Tests picking elements from lists using car and cdr""" interpret("(define lst (list 1 2 3 4 5))", self.env) assert_equals("1", interpret("(car lst)", self.env)) assert_equals("2", interpret("(car (cdr lst))", self.env)) assert_equals("(3 4 5)", interpret("(cdr (cdr lst))", self.env))
def test_arithmetic_functions(self): """Sanity check for the builtin artihmetic functions""" assert_equals("5", interpret('(+ 2 3)', self.env)) assert_equals("3", interpret('(- 5 2)', self.env)) assert_equals("8", interpret('(* 4 2)', self.env)) assert_equals("8", interpret('(/ 16 2)', self.env)) assert_equals("1", interpret('(mod 5 2)', self.env))
def test_expand_1_only_expands_once(self): """when used on recursive macro, expand-1 only expands once""" env = Environment() interpret("""(define add-foo (macro (lst) `(add-foo (cons foo ,lst))))""", env) assert_equals("(add-foo (cons foo nil))", interpret("(expand-1 '(add-foo nil))", env))
def test_factorial(self): """Simple factorial""" env = default_env() interpret(""" (define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1)))))) """, env) assert_equals("120", interpret("(fact 5)", env))
def test_macro_call_expands_macro_then_evaluates_expression(self): """A macro call should evaluate as a call to the expanded macro""" env = Environment() interpret("(define fooify (lambda (x) `(foo foo ,x foo)))", env) interpret("""(define test (macro (foo) `(fooify ,foo)))""", env) assert_equals("(foo foo bar foo)", interpret("(test 'bar)", env))
def IGNORED_test_check_nil_test_fn(self): """Test the `nil?` function `nil?` returns #t on empty lists, and #f otherwise""" env = default_env() assert_equals("#t", interpret("(nil? (list))")) assert_equals("#t", interpret("(nil? 'nil)")) assert_equals("#t", interpret("(nil? (cdr (list 1)))")) assert_equals("#f", interpret("(nil? 'foo)")) assert_equals("#f", interpret("(nil? (list 'foo 'bar))"))
def test_gcd(self): """Greates common dividor""" env = default_env() interpret(""" (define gcd (lambda (a b) (if (= 0 b) a (gcd b (mod a b))))) """, env) assert_equals("6", interpret("(gcd 108 30)", env)) assert_equals("1", interpret("(gcd 17 5)", env))
def test_expand_expands_until_form_is_not_macro_call(self): """Expansion continues until root element of result isn't macro call. Clojure example: user=> (defmacro unless [pred a b] `(if ~pred ~b ~a)) #'user/unless user=> (defmacro test [x] `(unless ~x 'foo 'bar)) #'user/test user=> (macroexpand-1 '(test true)) (user/unless true (quote user/foo) (quote user/bar)) user=> (macroexpand '(test true)) (if true (quote user/bar) (quote user/foo)) """ env = Environment() interpret("""(define unless (macro (pred a b) `(if ,pred ,b ,a)))""", env) interpret("""(define test (macro (x) `(unless ,x 'foo 'bar)))""", env) assert_equals("(unless #t 'foo 'bar)", interpret("(expand-1 '(test #t))", env)) assert_equals("(if #t 'bar 'foo)", interpret("(expand '(test #t))", env))
def test_expand_1_on_non_macro_returns_unchanged(self): """expand-1, when called with a form that is not a macro call, should return that form""" assert_equals("42", interpret("(expand-1 '42)", Environment()))
def test_list_nil(self): """Test the predefined nil value""" assert_equals(interpret("(quote ())"), interpret("nil")) assert_equals("()", interpret("nil"))
def test_comparator_functions(self): """Sanity check for the builtin comparators""" assert_equals("#f", interpret('(= 2 3)', self.env)) assert_equals("#t", interpret('(= #t #t)', self.env)) assert_equals("#f", interpret('(> 1 2)', self.env)) assert_equals("#t", interpret('(> 2 1)', self.env)) assert_equals("#t", interpret('(< 1 2)', self.env)) assert_equals("#f", interpret('(< 2 1)', self.env)) assert_equals("#t", interpret('(>= 2 1)', self.env)) assert_equals("#t", interpret('(>= 2 2)', self.env)) assert_equals("#f", interpret('(>= 2 3)', self.env)) assert_equals("#f", interpret('(<= 2 1)', self.env)) assert_equals("#t", interpret('(<= 2 2)', self.env)) assert_equals("#t", interpret('(<= 2 3)', self.env))
def test_simple_if_statement(self): assert_equals("42", interpret("(if #t 42 1000)"))
def test_if_expands_to_cond_statement(self): """Test the expansion of an if statement into the corresponding cond""" assert_equals("(cond (#t 42) (#t 100))", interpret("(expand-1 '(if #t 42 100))"))