Пример #1
0
def object(arg, scope):
    obj = Object()

    while arg is not nil:
        assert type(arg) is Pair
        slot = arg.car

        if type(slot) is FormNode:
            func = eval_node(slot.car, scope)
            slot = FormNode(ValueNode('CACHE', func), slot.cdr) # this ensures we only eval the car of the form once
            if func is cons:
                assert type(slot.cdr) is Pair
                obj.set(slot.cdr, scope)
            elif func is get:
                assert type(slot.cdr) is Pair
                assert type(slot.cdr.cdr) is Pair
                key_node = slot.cdr.cdr.car
                obj.set(Pair(key_node, Pair(slot, nil)), scope)
            else:
                raise Exception("I don't know what to do with that yet")
        elif type(slot) is IdentifierNode:
            obj.set(Pair(slot, Pair(slot, nil)), scope)
        else:
            raise Exception("syntax error!")

        arg = arg.cdr

    return obj
Пример #2
0
 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)'))
Пример #3
0
 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)'))
Пример #4
0
 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))
Пример #5
0
 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'''))
Пример #6
0
def dictionary(arg, scope):
    dct = Dictionary()

    while arg is not nil:
        assert type(arg) is Pair
        pair = eval_node(arg.car, scope)
        dct.set(Pair(ValueNode('CACHE', pair.car), Pair(ValueNode('CACHE', pair.cdr), nil)), scope)
        arg = arg.cdr

    return dct
Пример #7
0
 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)]'))
Пример #8
0
    def test_cons_pattern_match_list(self):
        self._test_pattern('(pattern [a b | c])', '[1 2 | 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a b | c])', '[1 2]', {
            'a': 1,
            'b': 2,
            'c': nil
        })
        self._test_pattern_fails('(pattern [a b | c])', '[1]')
        self._test_pattern_fails('(pattern [a b | c])', 'nil')

        self._test_pattern('(pattern [a b c])', '[1 2 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern_fails('(pattern [a b c])', '[1 2 | 3]')
        self._test_pattern_fails('(pattern [a b c])', '[1 2]')
        self._test_pattern_fails('(pattern [a b c])', '[1]')
        self._test_pattern_fails('(pattern [a b c])', 'nil')

        self._test_pattern('(pattern []:[])', 'nil:nil', {})
        self._test_pattern_fails('(pattern []:[])', 'nil')

        self._test_pattern('(pattern []:[]:[]:[])', 'nil:nil:nil:nil', {})
        self._test_pattern_fails('(pattern []:[]:[]:[])', 'nil')

        self._test_pattern('(pattern []:a)', 'nil:1', {'a': 1})
        self._test_pattern_fails('(pattern []:a)', 'nil')

        self._test_pattern('(pattern a:b)', '1:2', {'a': 1, 'b': 2})
        self._test_pattern('(pattern a:b)', '[1 | 2]', {'a': 1, 'b': 2})
        self._test_pattern('(pattern a:b)', '[1 2 3]', {
            'a': 1,
            'b': Pair(2, Pair(3, nil))
        })
        self._test_pattern_fails('(pattern a:b)', 'nil')

        self._test_pattern('(pattern a:b:c)', '1:2:3', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern a:b:c)', '[1 2 3]', {
            'a': 1,
            'b': 2,
            'c': Pair(3, nil)
        })
        self._test_pattern_fails('(pattern a:b:c)', '1:2')
        self._test_pattern_fails('(pattern a:b:c)', 'nil')
Пример #9
0
def eval_node(node, scope):
    if type(node) is Pair:
        return Pair(eval_node(node.car, scope), eval_node(node.cdr, scope))

    # we're re-evaluating something that's already been evaluated. this might be a terrible idea.
    # currently used by the apply and curry builtins to pre-apply arguments before calling a built-in function
    # should just use ValueNodes in all cases
    if not isinstance(node, Node):
        return node

    if type(node) is FormNode:
        fn = eval_node(node.car, scope)
        if type(fn) is FunctionType:
            return fn(node.cdr, scope)
        else:
            return fn.call(node.cdr, scope)
    elif type(node) is IdentifierNode:
        return scope.get(node.identifier)
    elif type(node) is NumericLiteralNode:
        return int(node.value)
    elif type(node) is SymbolLiteralNode:
        return Symbol(node.value)
    elif type(node) is ValueNode:
        return node.value
    else:
        raise Exception("I don't know how to eval %s (%s)" %
                        (str(node), type(node)))
Пример #10
0
    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)
Пример #11
0
    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'''))
Пример #12
0
 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))')
Пример #13
0
 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))
Пример #14
0
 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)'''))
Пример #15
0
    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'''))
Пример #16
0
 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])')
Пример #17
0
    def __init__(self, arg, scope):
        self.functions = []
        while arg is not nil:
            assert type(arg) is Pair
            assert type(arg.car) is FormNode

            func_arg = Pair(arg.car.car, arg.car.cdr)
            function = Function(func_arg, scope)
            self.functions.append(function)
            # print(function)

            arg = arg.cdr
Пример #18
0
 def test_read_square_brackets_with_cdr_converts_to_list_special_form(self):
     self.assertEqual(
         FormNode(ValueNode('_list', specials.list_),
                  Pair(IdentifierNode('a'), IdentifierNode('b'))),
         read_one('[a | b]'))
Пример #19
0
def cons(arg, scope):
    car = arg.car
    cdr = arg.cdr.car
    return Pair(eval_node(car, scope), eval_node(cdr, scope))
Пример #20
0
def cdr(arg, scope):
    assert type(arg) is Pair
    assert eval_node(arg.cdr, scope) is nil
    val = eval_node(arg.car, scope)
    return val.get(Pair(IdentifierNode('cdr'), nil), scope)
Пример #21
0
    def test_read_misc(self):
        self.assertEqual(
            FormNode(IdentifierNode('id'), Pair(NumericLiteralNode('1'), nil)),
            read_one('(id 1)'))
        self.assertEqual(
            FormNode(
                IdentifierNode('id'),
                Pair(NumericLiteralNode('1'), Pair(NumericLiteralNode('2'),
                                                   nil))),
            read_one('(id 1 2)'))
        self.assertEqual(Forms(IdentifierNode('id'), NumericLiteralNode('1')),
                         read_one('(id 1)'))
        self.assertEqual(
            Forms(
                IdentifierNode('id'),
                FormNode(
                    IdentifierNode('add'),
                    Pair(NumericLiteralNode('1'),
                         Pair(NumericLiteralNode('2'), nil)))),
            read_one('(id (add 1 2))'))
        self.assertEqual(
            Forms(
                IdentifierNode('id'),
                FormNode(
                    ValueNode('_list', specials.list_),
                    Pair(NumericLiteralNode('1'),
                         Pair(NumericLiteralNode('2'), nil)))),
            read_one('(id [1 2])'))
        self.assertEqual(
            Forms(
                IdentifierNode('id'),
                FormNode(
                    ValueNode('_list', specials.list_),
                    Pair(NumericLiteralNode('1'), NumericLiteralNode('2')))),
            read_one('(id [1 | 2])'))
        self.assertEqual(
            Forms(Forms(IdentifierNode('id'), IdentifierNode('id')),
                  NumericLiteralNode('1')), read_one('((id id) 1)'))

        self.assertEqual(
            Forms(
                IdentifierNode('print'),
                Forms(IdentifierNode('add'), NumericLiteralNode('5'),
                      NumericLiteralNode('6'))), read_one('(print (add 5 6))'))

        self.assertEqual(
            Forms(
                IdentifierNode('print'),
                FormNode(
                    ValueNode('_list', specials.list_),
                    Pair(NumericLiteralNode('7'), NumericLiteralNode('8')))),
            read_one('(print [7 | 8])'))

        self.assertEqual(
            Forms(
                IdentifierNode('print'),
                Forms(
                    ValueNode('_list', specials.list_),
                    Forms(IdentifierNode('add'), NumericLiteralNode('10'),
                          NumericLiteralNode('20'))),
            ), read_one('(print [(add 10 20)])'))
Пример #22
0
 def test_identifier_pattern_match(self):
     self._test_pattern('(pattern a)', '10', {'a': 10})
     self._test_pattern('(pattern a)', 'nil', {'a': nil})
     self._test_pattern('(pattern a)', '[1 2 3]',
                        {'a': Pair(1, Pair(2, Pair(3, nil)))})
Пример #23
0
def function_shorthand(arg, scope):
    if arg is nil:
        return Function(Pair(ValueNode('_default_pattern', default_arguments_pattern_singleton), nil), scope)
    assert type(arg) is Pair
    return Function(Pair(ValueNode('_default_pattern', default_arguments_pattern_singleton), Pair(FormNode(arg.car, arg.cdr), nil)), scope)
Пример #24
0
def list_(arg, scope):
    if arg is nil:
        return nil
    if type(arg) is not Pair:
        raise Exception('cannot have a cdr without a car')
    return Pair(eval_node(arg.car, scope), eval_node(arg.cdr, scope))
Пример #25
0
    def test_defaulted_patterns(self):
        self._test_pattern('(pattern [a | b])', '1:2', {'a': 1, 'b': 2})
        self._test_pattern('(pattern [a | b])', '[1]', {'a': 1, 'b': nil})
        self._test_pattern('(pattern [a | b])', '1:2:3', {
            'a': 1,
            'b': Pair(2, 3)
        })
        self._test_pattern_fails('(pattern [a | b])', 'nil')

        # This is a somewhat useless pattern -- the default can never be triggered,
        # since you can never have a cons cell without a cdr (although nil can be
        # thought of as a cons cell without a car).
        self._test_pattern('(pattern [a | b = 20])', '1:2', {'a': 1, 'b': 2})
        self._test_pattern_fails('(pattern [a | b = 20])', '1')
        self._test_pattern_fails('(pattern [a | b = 20])', 'nil')

        self._test_pattern('(pattern [a = 10 | b])', '1:2', {'a': 1, 'b': 2})
        # THIS IS COUNTER-INTUITIVE.
        # At least, it's not what I expected when I first wrote these tests.
        # To explain why it works, though, think of it as nothing:nil -- nothing cons nil is
        # just nil -- you didn't cons anything onto it, so it didn't change. And you can
        # match nothing:nil -- nothing goes to (pattern a = 10), which can accept nothing
        # and simply returns its default value. Then nil goes to b. And it works.
        self._test_pattern('(pattern [a = 10 | b])', 'nil', {
            'a': 10,
            'b': nil
        })
        self._test_pattern_fails('(pattern [a = 10 | b])', '1')

        self._test_pattern('(pattern [a = 10 | b = 20])', '1:2', {
            'a': 1,
            'b': 2
        })
        # This is equivalent to nothing:nil, as explained above.
        self._test_pattern('(pattern [a = 10 | b = 20])', 'nil', {
            'a': 10,
            'b': nil
        })
        self._test_pattern_fails('(pattern [a = 10 | b = 20])', '1')

        self._test_pattern('(pattern [a = 10 | b = 20]:c)', '[1 | 2]:3', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a = 10 | b = 20]:c)', 'nil', {
            'a': 10,
            'b': 20,
            'c': nil
        })

        self._test_pattern('(pattern [a b]:c)', '[1 2]:nil', {
            'a': 1,
            'b': 2,
            'c': nil
        })
        self._test_pattern_fails('(pattern [a = 10 b = 20]:c)', 'nil')
        # This is a little weird when you think of it as a list, but if you think of it as a ConsPattern
        # whose cdr_pattern is a ConsPattern then this does make sense.
        self._test_pattern('(pattern [a = 10 b = 20 | c = 30]:d)', 'nil', {
            'a': 10,
            'b': 20,
            'c': 30,
            'd': nil
        })
        self._test_pattern('(pattern [a b]:c)', '[1 2]:nil', {
            'a': 1,
            'b': 2,
            'c': nil
        })

        self._test_pattern('(pattern [[a | b] | c])', '[1|2]:3', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern_fails('(pattern [[a | b] | c])', '[1|2]')
        self._test_pattern_fails('(pattern [[a | b] | c])', '[1]')
        self._test_pattern_fails('(pattern [[a | b] | c])', 'nil')

        self._test_pattern('(pattern [[a | b] c])', '[1:2 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern_fails('(pattern [[a | b] c])', '[1|2]')
        self._test_pattern_fails('(pattern [[a | b] c])', '[1 3]')
        self._test_pattern_fails('(pattern [[a | b] c])', '[1]')
        self._test_pattern_fails('(pattern [[a | b] c])', 'nil')

        self._test_pattern('(pattern a::even? = 10)', '7', {'a': 10})
        self._test_pattern('(pattern a::even? = 10)', '8', {'a': 8})

        self._test_pattern('(pattern [a b = 20])', '[1 2]', {'a': 1, 'b': 2})
        self._test_pattern('(pattern [a b = 20])', '[1]', {'a': 1, 'b': 20})
        self._test_pattern('(pattern [a b = 20])', '1:2:nil', {'a': 1, 'b': 2})
        self._test_pattern_fails('(pattern [a b = 20])', '1:2:3')
        self._test_pattern_fails('(pattern [a b = 20])', 'nil')

        self._test_pattern('(pattern [a b c = 30])', '[1 2 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a b c = 30])', '[1 2]', {
            'a': 1,
            'b': 2,
            'c': 30
        })
        self._test_pattern_fails('(pattern [a b c = 30])', '[1]')
        self._test_pattern_fails('(pattern [a b c = 30])', 'nil')

        self._test_pattern('(pattern [a b = 20 c = 30])', '[1 2 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a b = 20 c = 30])', '[1 2]', {
            'a': 1,
            'b': 2,
            'c': 30
        })
        self._test_pattern('(pattern [a b = 20 c = 30])', '[1]', {
            'a': 1,
            'b': 20,
            'c': 30
        })
        self._test_pattern_fails('(pattern [a b = 20 c = 30])', 'nil')

        self._test_pattern('(pattern [a = 10 b = 20 c = 30])', '[1 2 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a = 10 b = 20 c = 30])', '[1 2]', {
            'a': 1,
            'b': 2,
            'c': 30
        })
        self._test_pattern('(pattern [a = 10 b = 20 c = 30])', '[1]', {
            'a': 1,
            'b': 20,
            'c': 30
        })
        self._test_pattern('(pattern [a = 10 b = 20 c = 30])', 'nil', {
            'a': 10,
            'b': 20,
            'c': 30
        })
        self._test_pattern_fails('(pattern [a = 10 b = 20 c = 30])', '1')

        self._test_pattern('(pattern [a b = 20 []:[]])', '[1 2 nil:nil]', {
            'a': 1,
            'b': 2
        })
        self._test_pattern_fails('(pattern [a b = 20 []:[]])', '[1]')
        self._test_pattern_fails('(pattern [a b = 20 []:[]])', '[1 2 nil]')

        self._test_pattern('(pattern [a b = 20 | []:[]])', '[1 2 | nil:nil]', {
            'a': 1,
            'b': 2
        })
        self._test_pattern('(pattern [a b = 20 | []:[]])', '[1 2 nil]', {
            'a': 1,
            'b': 2
        })
        self._test_pattern_fails('(pattern [a b = 20 | []:[]])', '[1]')
        self._test_pattern_fails('(pattern [a b = 20 | []:[]])', '[1 nil]')

        self._test_pattern('(pattern [a b = 20 c])', '[1 2 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a b = 20 c])', '[1 2 nil]', {
            'a': 1,
            'b': 2,
            'c': nil
        })
        self._test_pattern_fails('(pattern [a b = 20 c])', '[1 2]')
        self._test_pattern_fails('(pattern [a b = 20 c])', '[1]')
        self._test_pattern_fails('(pattern [a b = 20 c])', 'nil')

        self._test_pattern('(pattern [a b = 20 | c = 30])', '[1 2 | 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a b = 20 | c = 30])', '[1 2]', {
            'a': 1,
            'b': 2,
            'c': nil
        })
        self._test_pattern('(pattern [a b = 20 | c = 30])', '[1]', {
            'a': 1,
            'b': 20,
            'c': nil
        })  # counter-intuitive!
        self._test_pattern_fails('(pattern [a b = 20 | c = 30])', 'nil')

        self._test_pattern('(pattern [a b = 20 | c])', '[1 2 | 3]', {
            'a': 1,
            'b': 2,
            'c': 3
        })
        self._test_pattern('(pattern [a b = 20 | c])', '[1 2]', {
            'a': 1,
            'b': 2,
            'c': nil
        })
        self._test_pattern('(pattern [a b = 20 | c])', '[1]', {
            'a': 1,
            'b': 20,
            'c': nil
        })  # counter-intuitive!
        self._test_pattern_fails('(pattern [a b = 20 | c])', 'nil')

        self._test_pattern('(pattern [a b = 20 3])', '[1 2 3]', {
            'a': 1,
            'b': 2
        })
        self._test_pattern_fails('(pattern [a b = 20 3])', '[1 2]')
        self._test_pattern_fails('(pattern [a b = 20 3])', '[1 3]')
        self._test_pattern_fails('(pattern [a b = 20 3])', '[1]')
        self._test_pattern_fails('(pattern [a b = 20 3])', 'nil')

        self._test_pattern('(pattern [a b = 20 | 3])', '[1 2 | 3]', {
            'a': 1,
            'b': 2
        })
        self._test_pattern_fails('(pattern [a b = 20 | 3])', '[1 2]')
        self._test_pattern_fails('(pattern [a b = 20 | 3])', '[1 3]')
        self._test_pattern_fails('(pattern [a b = 20 | 3])', '[1 | 2]')
        self._test_pattern_fails('(pattern [a b = 20 | 3])', '[1 | 3]')
        self._test_pattern_fails('(pattern [a b = 20 | 3])', '[1]')
        self._test_pattern_fails('(pattern [a b = 20 | 3])', 'nil')

        self._test_pattern('(pattern [a b = 20 []])', '[1 2 nil]', {
            'a': 1,
            'b': 2
        })
        self._test_pattern('(pattern [a b = 20 []])', '[1 nil nil]', {
            'a': 1,
            'b': nil
        })
        self._test_pattern_fails('(pattern [a b = 20 []])', '[1 nil]')
        self._test_pattern_fails('(pattern [a b = 20 []])', '[1]')
        self._test_pattern_fails('(pattern [a b = 20 []])', 'nil')

        self._test_pattern('(pattern [a b = 20 | []])', '[1 2]', {
            'a': 1,
            'b': 2
        })
        self._test_pattern('(pattern [a b = 20 | []])', '[1]', {
            'a': 1,
            'b': 20
        })
        self._test_pattern_fails('(pattern [a b = 20 | []])', '[1 2 nil]')