forked from fluentpython/example-code-2e
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlis_test.py
More file actions
180 lines (144 loc) · 5.81 KB
/
lis_test.py
File metadata and controls
180 lines (144 loc) · 5.81 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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
from typing import Optional
from pytest import mark, fixture
from lis import parse, evaluate, Expression, Environment, standard_env
############################################################# tests for parse
@mark.parametrize( 'source, expected', [
('7', 7),
('x', 'x'),
('(sum 1 2 3)', ['sum', 1, 2, 3]),
('(+ (* 2 100) (* 1 10))', ['+', ['*', 2, 100], ['*', 1, 10]]),
('99 100', 99), # parse stops at the first complete expression
('(a)(b)', ['a']),
])
def test_parse(source: str, expected: Expression) -> None:
got = parse(source)
assert got == expected
########################################################## tests for evaluate
# Norvig's tests are not isolated: they assume the
# same environment from first to last test.
global_env_for_first_test = standard_env()
@mark.parametrize( 'source, expected', [
("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]),
("(+ 2 2)", 4),
("(+ (* 2 100) (* 1 10))", 210),
("(if (> 6 5) (+ 1 1) (+ 2 2))", 2),
("(if (< 6 5) (+ 1 1) (+ 2 2))", 4),
("(define x 3)", None),
("x", 3),
("(+ x x)", 6),
("((lambda (x) (+ x x)) 5)", 10),
("(define twice (lambda (x) (* 2 x)))", None),
("(twice 5)", 10),
("(define compose (lambda (f g) (lambda (x) (f (g x)))))", None),
("((compose list twice) 5)", [10]),
("(define repeat (lambda (f) (compose f f)))", None),
("((repeat twice) 5)", 20),
("((repeat (repeat twice)) 5)", 80),
("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))", None),
("(fact 3)", 6),
("(fact 50)", 30414093201713378043612608166064768844377641568960512000000000000),
("(define abs (lambda (n) ((if (> n 0) + -) 0 n)))", None),
("(list (abs -3) (abs 0) (abs 3))", [3, 0, 3]),
("""(define combine (lambda (f)
(lambda (x y)
(if (null? x) (quote ())
(f (list (car x) (car y))
((combine f) (cdr x) (cdr y)))))))""", None),
("(define zip (combine cons))", None),
("(zip (list 1 2 3 4) (list 5 6 7 8))", [[1, 5], [2, 6], [3, 7], [4, 8]]),
("""(define riff-shuffle (lambda (deck)
(begin
(define take (lambda (n seq) (if (<= n 0) (quote ()) (cons (car seq) (take (- n 1) (cdr seq))))))
(define drop (lambda (n seq) (if (<= n 0) seq (drop (- n 1) (cdr seq)))))
(define mid (lambda (seq) (/ (length seq) 2)))
((combine append) (take (mid deck) deck) (drop (mid deck) deck)))))""", None),
("(riff-shuffle (list 1 2 3 4 5 6 7 8))", [1, 5, 2, 6, 3, 7, 4, 8]),
("((repeat riff-shuffle) (list 1 2 3 4 5 6 7 8))", [1, 3, 5, 7, 2, 4, 6, 8]),
("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]),
])
def test_evaluate(source: str, expected: Optional[Expression]) -> None:
got = evaluate(parse(source), global_env_for_first_test)
assert got == expected
@fixture
def std_env() -> Environment:
return standard_env()
# tests for cases in evaluate
def test_evaluate_variable() -> None:
env = Environment({'x': 10})
source = 'x'
expected = 10
got = evaluate(parse(source), env)
assert got == expected
def test_evaluate_literal(std_env: Environment) -> None:
source = '3.3'
expected = 3.3
got = evaluate(parse(source), std_env)
assert got == expected
def test_evaluate_quote(std_env: Environment) -> None:
source = '(quote (1.1 is not 1))'
expected = [1.1, 'is', 'not', 1]
got = evaluate(parse(source), std_env)
assert got == expected
def test_evaluate_if_true(std_env: Environment) -> None:
source = '(if 1 10 no-such-thing)'
expected = 10
got = evaluate(parse(source), std_env)
assert got == expected
def test_evaluate_if_false(std_env: Environment) -> None:
source = '(if 0 no-such-thing 20)'
expected = 20
got = evaluate(parse(source), std_env)
assert got == expected
def test_define(std_env: Environment) -> None:
source = '(define answer (* 6 7))'
got = evaluate(parse(source), std_env)
assert got is None
assert std_env['answer'] == 42
def test_lambda(std_env: Environment) -> None:
source = '(lambda (a b) (if (>= a b) a b))'
func = evaluate(parse(source), std_env)
assert func.parms == ['a', 'b']
assert func.body == [['if', ['>=', 'a', 'b'], 'a', 'b']]
assert func.env is std_env
assert func(1, 2) == 2
assert func(3, 2) == 3
def test_begin(std_env: Environment) -> None:
source = """
(begin
(define x (* 2 3))
(* x 7)
)
"""
got = evaluate(parse(source), std_env)
assert got == 42
def test_invocation_builtin_car(std_env: Environment) -> None:
source = '(car (quote (11 22 33)))'
got = evaluate(parse(source), std_env)
assert got == 11
def test_invocation_builtin_append(std_env: Environment) -> None:
source = '(append (quote (a b)) (quote (c d)))'
got = evaluate(parse(source), std_env)
assert got == ['a', 'b', 'c', 'd']
def test_invocation_builtin_map(std_env: Environment) -> None:
source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))'
got = evaluate(parse(source), std_env)
assert got == [2, 4, 6]
def test_invocation_user_procedure(std_env: Environment) -> None:
source = """
(begin
(define max (lambda (a b) (if (>= a b) a b)))
(max 22 11)
)
"""
got = evaluate(parse(source), std_env)
assert got == 22
def test_define_function(std_env: Environment) -> None:
source = '(define max (lambda (a b) (if (>= a b) a b)))'
got = evaluate(parse(source), std_env)
assert got is None
max_fn = std_env['max']
assert max_fn.parms == ['a', 'b']
assert max_fn.body == [['if', ['>=', 'a', 'b'], 'a', 'b']]
assert max_fn.env is std_env
assert max_fn(1, 2) == 2
assert max_fn(3, 2) == 3