def test_eval_nested_functions(self): compose = isheval('(fn x (fn y ((car (cdr x)) ((car x) (car y)))))') self.assertEqual(Function, type(compose)) scope = Scope({'compose': compose}, root) self.assertEqual(compose, isheval('compose', scope)) self.assertEqual(10, isheval('((compose id id) 10)', scope)) self.assertEqual(Pair(10, nil), isheval('((compose id id) [10])', scope))
def test_predicated_patterns(self): self.assertTrue( isheval('(pattern-with-predicate (pattern x) even?)') == isheval( '(pattern (pattern-with-predicate (pattern x) even?))') == isheval('(pattern x)::even?') == isheval( '(pattern-with-predicate x even?)') == isheval( '(pattern x::even?)')) self._test_pattern('(pattern x::even?)', '10', {'x': 10}) self._test_pattern_fails('(pattern x::even?)', '11')
def _test_pattern_fails(self, pattern_code, target_code, initial_scope=None): if initial_scope is None: initial_scope = {} scope = Scope(initial_scope, root) self.assertFalse( isheval(pattern_code).match(isheval(target_code), scope))
def test_eval_list_special_form(self): self.assertEqual(Pair(7, Pair(8, Pair(9, nil))), isheval('(list 7 8 9)')) self.assertEqual(Pair(10, 20), isheval('(list 10 | 20)')) self.assertEqual(Pair(1, Pair(2, 3)), isheval('(list 1 2 | 3)')) self.assertEqual(Pair(10, 20), isheval('(list (add 5 5) | (add 10 10))')) self.assertRaises(Exception, isheval, '(list | 1)') self.assertRaises(Exception, isheval, '(list | [4 5 6])')
def test_calling_pattern(self): self.assertEqual(5, isheval('((pattern a) 5) a')) self.assertEqual(10, isheval('(a = 10) a')) self.assertRaises(Exception, isheval, '((pattern 5) 10)') self.assertEqual( 15, isheval(''' (foo = 10) (change-foo = (fn - (foo = 15))) (change-foo) foo '''))
def _test_pattern(self, pattern_code, target_code, expected_scope, initial_scope=None): if initial_scope is None: initial_scope = {} scope = Scope(initial_scope, root) pattern = isheval(pattern_code) self.assertTrue(pattern.match(isheval(target_code), scope)) self.assertEqual(scope.identifiers(), expected_scope.keys()) for key in expected_scope: self.assertEqual(expected_scope[key], scope.get(key))
def test_function_patterns(self): unary_compose = isheval(''' (fn [fn1 fn2] (fn [arg] (fn1 (fn2 arg)) ) )''') self.assertEqual(Function, type(unary_compose)) scope = Scope({'unary_compose': unary_compose}, root) cadr = isheval('(unary_compose car cdr)', scope) scope.set('cadr', cadr) self.assertEqual(2, isheval('(cadr [1 2 3])', scope)) self.assertEqual(3, isheval('((unary_compose cadr cdr) [1 2 3])', scope))
def test_curry_only_evaluates_arguments_once(self): count = 0 def increment_count(args, scope): nonlocal count count += 1 return 5 scope = Scope({'inc': increment_count}, root) self.assertEqual(0, count) curried = isheval('(curry list (inc))', scope) self.assertEqual(1, count) scope.set('curried', curried) self.assertEqual(Pair(5, Pair(10, Pair(20, nil))), isheval('(curried 10 20)', scope)) self.assertEqual(1, count)
def test_objects_get_shorthand(self): self.assertEqual( 10, isheval(''' (obj = {foo: {bar: 10}}) (get {obj.foo.bar} bar) '''))
def test_binding_methods(self): self.assertEqual( 15, isheval(''' (meth = (md [a] (add a @bar))) (foo = {bar: 10}) ((bind meth foo) 5)'''))
def test_objects_identifier_shorthand(self): self.assertEqual( 10, isheval(''' (foo = 10) (get {foo} foo) '''))
def test_eval_builtins(self): self.assertEqual(1, isheval('(car [1])')) self.assertEqual(1, isheval('(car (id [1]))')) self.assertEqual(nil, isheval('(cdr [1])')) self.assertEqual(Pair(1, 2), isheval('(call cons 1 2)')) self.assertEqual(Pair(1, 2), isheval('(apply cons (list 1 2))')) self.assertEqual(Pair(1, 2), isheval('(apply cons [1 2])')) self.assertEqual(1, isheval('(apply car [[1 2]])')) self.assertEqual(Pair(1, Pair(2, nil)), isheval('((curry list 1) 2)'))
def test_multi_functions(self): self.assertEqual( Pair(11, 30), isheval(''' (def multi-test (mfn ([a] (add a 1)) ([a b] (add a b)) )) (multi-test 10):(multi-test 10 20)'''))
def test_get_in_object_is_evaluated_once(self): self.assertEqual( 1, isheval(''' (a = 0) (obj = {baz: 10}) (side-effect-get = (fn - (a = (add a 1)) get)) { ((side-effect-get) obj baz) } a'''))
def test_dictionaries_can_still_have_attributes(self): self.assertEqual( Pair(10, Pair(20, 30)), isheval(''' (dict = (dictionary)) (set dict 5 10) (set dict foo 20) (set dict #foo 30) dict.5 : dict.foo : dict.#foo'''))
def test_eval_builtin_id(self): self.assertEqual(2, isheval('(id 2)')) self.assertEqual(5, isheval('(id (add 2 3))')) self.assertEqual(Pair(1, nil), isheval('(id (list 1))')) self.assertEqual(Pair(1, nil), isheval('(id [1])')) self.assertEqual(Pair(1, 2), isheval('(id [1 | 2])')) self.assertEqual(1, isheval('(id 1)')) self.assertEqual(20, isheval('((id id) 20)'))
def test_eval_functions_in_scope(self): my_id = isheval('(fn x x)') scope = Scope({'my_id': my_id}, root) self.assertEqual(my_id, isheval('my_id', scope)) self.assertEqual(my_id, isheval('(my_id | (my_id | my_id))', scope)) self.assertEqual(3, isheval('(my_id | 3)', scope)) self.assertEqual(Pair(my_id, nil), isheval('(my_id my_id)', scope)) self.assertEqual(my_id, isheval('(my_id | my_id)', scope)) self.assertEqual(Pair(20, nil), isheval('(my_id 20)', scope))
def test_eval_square_brackets(self): self.assertEqual(Pair(1, nil), isheval('[1]')) self.assertEqual(Pair(1, Pair(2, Pair(3, nil))), isheval('[1 2 3]')) self.assertEqual(Pair(1, 2), isheval('[1 | 2]')) self.assertEqual(Pair(1, Pair(2, 3)), isheval('[1 2 | 3]')) self.assertEqual(Pair(10, nil), isheval('[(add 5 5)]')) self.assertEqual(Pair(10, 20), isheval('[(add 5 5) | (add 10 10)]'))
def test_eval_functions(self): self.assertEqual(20, isheval('((fn x 20))')) self.assertEqual(5, isheval('((fn x x) | 5)')) self.assertEqual(5, isheval('((fn x (car x)) | [5])')) self.assertEqual(5, isheval('((fn x (car x)) 5)')) self.assertEqual(Pair(5, nil), isheval('((fn x x) (add 2 3))')) self.assertEqual(30, isheval('((fn x (add 10 (car x))) 20)')) self.assertRaises(Exception, isheval, '((add 1 2))')
def test_function_patterns_simple(self): compose = isheval('(fn [fn1 fn2] (fn [y] (fn2 (fn1 y))))') scope = Scope({'compose': compose}, root) self.assertEqual(10, isheval('((compose id id) 10)', scope)) my_car = isheval('(fn [x:xs] x)') scope = Scope({'my-car': my_car}, root) self.assertEqual(3, isheval('(my-car [3 4 5])', scope)) my_id = isheval('(fn x x)') scope = Scope({'my-id': my_id}, root) self.assertEqual(20, isheval('(my-id | 20)', scope)) first_arg = isheval('(fn x:xs x)') scope = Scope({'first-arg': first_arg}, root) self.assertEqual(6, isheval('(first-arg 6 7 8)', scope))
def test_promises(self): self.assertEqual( Pair(0, Pair(0, Pair(1, 1))), isheval(''' (x = 0) (side-effect = (fn - (x = (add x 1)) 20)) (a = x) (def y (delay (side-effect))) (b = x) (force y) (c = x) (force y) (d = x) a:b:c:d'''))
def test_pairs_force_cdrs(self): self.assertEqual( Pair(0, Pair(0, Pair(1, 1))), isheval(''' (x = 0) (side-effect = (fn - (x = (add x 1)) 20)) (a = x) (pair = 10:(delay (side-effect))) pair.cdr-slot (b = x) pair.cdr (c = x) (force pair.cdr-slot) (d = x) a:b:c:d'''))
def test_get_binds_methods(self): self.assertEqual( 15, isheval(''' (obj = { baz: 5, meth: (md [a] (add a @baz)) }) (obj.meth 10)'''))
def test_dictionaries_dont_treat_identifiers_specially(self): self.assertEqual( 10, isheval(''' (foo = 5) #{foo: 10}.5'''))
def test_objects_get_syntax(self): self.assertEqual(10, isheval('{foo: 10}.foo'))
def test_dictionaries_special_syntax(self): self.assertEqual(10, isheval('(get #{5: 10} 5)'))
def test_dictionaries_set(self): self.assertEqual( 10, isheval('(dict = (dictionary)) (set dict 5 10) (get dict 5)'))
def test_dictionaries_basic(self): self.assertEqual(10, isheval('(get (dictionary 5:10) 5)'))
def test_dictionaries_get_syntax(self): self.assertEqual(10, isheval('#{5: 10}.5'))
def test_objects_special_syntax(self): self.assertEqual(10, isheval('(get {foo: 10} foo)'))