Skip to content

Commit 0cbb0c5

Browse files
committed
lis.py: added 'define' procedure short form
1 parent d88b17c commit 0cbb0c5

File tree

3 files changed

+39
-23
lines changed

3 files changed

+39
-23
lines changed

18-context-mngr/lispy/py3.10/lis.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@
1212
from collections.abc import MutableMapping
1313
from typing import Any, TypeAlias
1414

15-
Atom: TypeAlias = float | int | str
15+
Symbol: TypeAlias = str
16+
Atom: TypeAlias = float | int | Symbol
1617
Expression: TypeAlias = Atom | list
1718

18-
Environment: TypeAlias = MutableMapping[str, object]
19+
Environment: TypeAlias = MutableMapping[Symbol, object]
1920

2021

2122
class Procedure:
2223
"A user-defined Scheme procedure."
2324

24-
def __init__(self, parms: list[str], body: Expression, env: Environment):
25+
def __init__(self, parms: list[Symbol], body: Expression, env: Environment):
2526
self.parms, self.body, self.env = parms, body, env
2627

2728
def __call__(self, *args: Expression) -> Any:
@@ -68,14 +69,12 @@ def standard_env() -> Environment:
6869
'number?': lambda x: isinstance(x, (int, float)),
6970
'procedure?': callable,
7071
'round': round,
71-
'symbol?': lambda x: isinstance(x, str),
72+
'symbol?': lambda x: isinstance(x, Symbol),
7273
}
7374
)
7475
return env
7576

7677

77-
global_env: Environment = standard_env()
78-
7978
################ Parsing: parse, tokenize, and read_from_tokens
8079

8180

@@ -114,16 +113,17 @@ def parse_atom(token: str) -> Atom:
114113
try:
115114
return float(token)
116115
except ValueError:
117-
return str(token)
116+
return Symbol(token)
118117

119118

120119
################ Interaction: A REPL
121120

122121

123122
def repl(prompt: str = 'lis.py> ') -> None:
124123
"A prompt-read-evaluate-print loop."
124+
global_env: Environment = standard_env()
125125
while True:
126-
val = evaluate(parse(input(prompt)))
126+
val = evaluate(parse(input(prompt)), global_env)
127127
if val is not None:
128128
print(lispstr(val))
129129

@@ -142,7 +142,7 @@ def lispstr(exp: object) -> str:
142142
def evaluate(x: Expression, env: Environment) -> Any:
143143
"Evaluate an expression in an environment."
144144
match x:
145-
case str(var): # variable reference
145+
case Symbol(var): # variable reference
146146
return env[var]
147147
case literal if not isinstance(x, list): # constant literal
148148
return literal
@@ -151,9 +151,11 @@ def evaluate(x: Expression, env: Environment) -> Any:
151151
case ['if', test, conseq, alt]: # (if test conseq alt)
152152
exp = conseq if evaluate(test, env) else alt
153153
return evaluate(exp, env)
154-
case ['define', var, exp]: # (define var exp)
154+
case ['define', Symbol(var), exp]: # (define var exp)
155155
env[var] = evaluate(exp, env)
156-
case ['lambda', parms, body]: # (lambda (var...) body)
156+
case ['define', [name, *parms], body]: # (define (fun parm...) body)
157+
env[name] = Procedure(parms, body, env)
158+
case ['lambda', parms, body]: # (lambda (parm...) body)
157159
return Procedure(parms, body, env)
158160
case [op, *args]: # (proc arg...)
159161
proc = evaluate(op, env)

18-context-mngr/lispy/py3.10/lis_test.py

100755100644
Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
# Norvig's tests are not isolated: they assume the
88
# same environment from first to last test.
9-
ENV_FOR_FIRST_TEST = standard_env()
9+
global_env_for_first_test = standard_env()
1010

1111
@mark.parametrize( 'source, expected', [
1212
("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]),
@@ -48,7 +48,7 @@
4848
("(riff-shuffle (riff-shuffle (riff-shuffle (list 1 2 3 4 5 6 7 8))))", [1,2,3,4,5,6,7,8]),
4949
])
5050
def test_evaluate(source: str, expected: Optional[Expression]) -> None:
51-
got = evaluate(parse(source), ENV_FOR_FIRST_TEST)
51+
got = evaluate(parse(source), global_env_for_first_test)
5252
assert got == expected
5353

5454

@@ -149,3 +149,17 @@ def test_invocation_user_procedure(std_env: Environment) -> None:
149149
"""
150150
got = evaluate(parse(source), std_env)
151151
assert got == 22
152+
153+
154+
###################################### for py3.10/lis.py only
155+
156+
def test_define_function(std_env: Environment) -> None:
157+
source = '(define (max a b) (if (>= a b) a b))'
158+
got = evaluate(parse(source), std_env)
159+
assert got is None
160+
max_fn = std_env['max']
161+
assert max_fn.parms == ['a', 'b']
162+
assert max_fn.body == ['if', ['>=', 'a', 'b'], 'a', 'b']
163+
assert max_fn.env is std_env
164+
assert max_fn(1, 2) == 2
165+
assert max_fn(3, 2) == 3

18-context-mngr/lispy/py3.9/lis.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@
1212
from collections.abc import MutableMapping
1313
from typing import Union, Any
1414

15-
Atom = Union[float, int, str]
15+
Symbol = str
16+
Atom = Union[float, int, Symbol]
1617
Expression = Union[Atom, list]
1718

18-
Environment = MutableMapping[str, object]
19+
Environment = MutableMapping[Symbol, object]
1920

2021

2122
class Procedure:
2223
"A user-defined Scheme procedure."
23-
24-
def __init__(self, parms: list[str], body: Expression, env: Environment):
24+
def __init__(self, parms: list[Symbol], body: Expression, env: Environment):
2525
self.parms, self.body, self.env = parms, body, env
2626

2727
def __call__(self, *args: Expression) -> Any:
28-
env: Environment = ChainMap(dict(zip(self.parms, args)), self.env)
28+
local_env = dict(zip(self.parms, args))
29+
env: Environment = ChainMap(local_env, self.env)
2930
return evaluate(self.body, env)
3031

3132

@@ -67,14 +68,12 @@ def standard_env() -> Environment:
6768
'number?': lambda x: isinstance(x, (int, float)),
6869
'procedure?': callable,
6970
'round': round,
70-
'symbol?': lambda x: isinstance(x, str),
71+
'symbol?': lambda x: isinstance(x, Symbol),
7172
}
7273
)
7374
return env
7475

7576

76-
global_env: Environment = standard_env()
77-
7877
################ Parsing: parse, tokenize, and read_from_tokens
7978

8079

@@ -113,16 +112,17 @@ def parse_atom(token: str) -> Atom:
113112
try:
114113
return float(token)
115114
except ValueError:
116-
return str(token)
115+
return Symbol(token)
117116

118117

119118
################ Interaction: A REPL
120119

121120

122121
def repl(prompt: str = 'lis.py> ') -> None:
123122
"A prompt-read-evaluate-print loop."
123+
global_env: Environment = standard_env()
124124
while True:
125-
val = evaluate(parse(input(prompt)))
125+
val = evaluate(parse(input(prompt)), global_env)
126126
if val is not None:
127127
print(lispstr(val))
128128

0 commit comments

Comments
 (0)