-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathtest_lispylet.py
More file actions
158 lines (134 loc) · 5.47 KB
/
test_lispylet.py
File metadata and controls
158 lines (134 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# -*- coding: utf-8 -*-
from ..syntax import macros, test, test_raises # noqa: F401
from ..test.fixtures import session, testset
from functools import partial
from ..lispylet import let, letrec, dlet, dletrec, blet, bletrec
from ..seq import begin
def runtests():
with testset("basic usage"):
x = let((('a', 1),
('b', 2)),
lambda e: e.a + e.b)
test[x == 3]
x = letrec((('a', 1),
('b', lambda e:
e.a + 2)), # hence, b = 3
lambda e:
e.a + e.b)
test[x == 4]
# mutually recursive functions
t = letrec((('evenp', lambda e:
lambda x:
(x == 0) or e.oddp(x - 1)),
('oddp', lambda e:
lambda x:
(x != 0) and e.evenp(x - 1))),
lambda e:
e.evenp(42))
test[t is True]
f = lambda lst: letrec((("seen", set()),
("see", lambda e:
lambda x:
begin(e.seen.add(x),
x))),
lambda e:
[e.see(x) for x in lst if x not in e.seen])
L = [1, 1, 3, 1, 3, 2, 3, 2, 2, 2, 4, 4, 1, 2, 3]
test[f(L) == [1, 3, 2, 4]]
# Callable values always need a surrounding "lambda e: ...".
with testset("additional examples with callable values"):
test[letrec((('a', 2),
('f', lambda e:
lambda x: # callable, needs the surrounding "lambda e: ...", even though it doesn't use e.
42 * x)),
lambda e:
e.a * e.f(1)) == 84]
square = lambda x: x**2
test[letrec((('a', 2),
('f', lambda e: square)), # callable, needs "lambda e: ..."
lambda e:
e.a * e.f(10)) == 200]
def mul(x, y):
return x * y
test[letrec((('a', 2),
('f', lambda e: mul)), # "mul" is a callable
lambda e:
e.a * e.f(3, 4)) == 24]
double = partial(mul, 2)
test[letrec((('a', 2),
('f', lambda e: double)), # "double" is a callable
lambda e:
e.a * e.f(3)) == 12]
class TimesA:
def __init__(self, a):
self.a = a
def __call__(self, x):
return self.a * x
times5 = TimesA(5)
test[letrec((('a', 2),
('f', lambda e: times5)), # "times5" is a callable
lambda e:
e.a * e.f(3)) == 30]
with testset("let over lambda"):
lc = let((('count', 0),),
lambda e:
lambda: begin(e.set('count', e.count + 1),
e.count))
lc()
lc()
test[lc() == 3]
with testset("let over def"):
@dlet((('x', 17),))
def foo(*, env):
return env.x
test[foo() == 17]
@dletrec((('x', 2),
('y', lambda e: e.x + 3)))
def bar(a, *, env):
return a + env.y
test[bar(10) == 15]
@dlet((('count', 0),))
def counter(*, env):
env.count += 1
return env.count
counter()
counter()
test[counter() == 3]
with testset("let block"):
@blet((('x', 9001),))
def over9000(*, env):
return env.x
test[over9000 == 9001]
@bletrec((('evenp', lambda e:
lambda x:
(x == 0) or e.oddp(x - 1)),
('oddp', lambda e:
lambda x:
(x != 0) and e.evenp(x - 1)),))
def result(*, env):
return env.evenp(42)
test[result is True]
with testset("error cases"):
test_raises[AttributeError,
letrec((('a', lambda e:
e.b + 1), # error, e.b does not exist yet (simple value refers to binding below it)
('b', 42)),
lambda e:
e.a)]
test_raises[AttributeError,
let((('x', 0),),
lambda e:
e.set('y', 3)),
"e.y should not be defined"]
with test_raises[AttributeError, "let environment should be final (should not be able to create new bindings in it inside the let body)"]:
@blet((('x', 1),))
def error1(*, env):
env.y = 2 # error, cannot introduce new bindings into a let environment
test_raises[TypeError, let((), "not a callable")]
test_raises[TypeError, let((), lambda: None)] # body callable must be able to take in environment
test_raises[AttributeError, let((('x', 1),
('x', 2)), lambda e: e.x)] # trying to reassign same name
test_raises[TypeError, letrec((('x', lambda: 1),), lambda e: e.x)] # callable value must be able to take in environment
if __name__ == '__main__': # pragma: no cover
with session(__file__):
runtests()