lis.py: added 'define' procedure short form
This commit is contained in:
parent
d88b17c75f
commit
0cbb0c557c
@ -12,16 +12,17 @@ from collections import ChainMap
|
||||
from collections.abc import MutableMapping
|
||||
from typing import Any, TypeAlias
|
||||
|
||||
Atom: TypeAlias = float | int | str
|
||||
Symbol: TypeAlias = str
|
||||
Atom: TypeAlias = float | int | Symbol
|
||||
Expression: TypeAlias = Atom | list
|
||||
|
||||
Environment: TypeAlias = MutableMapping[str, object]
|
||||
Environment: TypeAlias = MutableMapping[Symbol, object]
|
||||
|
||||
|
||||
class Procedure:
|
||||
"A user-defined Scheme procedure."
|
||||
|
||||
def __init__(self, parms: list[str], body: Expression, env: Environment):
|
||||
def __init__(self, parms: list[Symbol], body: Expression, env: Environment):
|
||||
self.parms, self.body, self.env = parms, body, env
|
||||
|
||||
def __call__(self, *args: Expression) -> Any:
|
||||
@ -68,14 +69,12 @@ def standard_env() -> Environment:
|
||||
'number?': lambda x: isinstance(x, (int, float)),
|
||||
'procedure?': callable,
|
||||
'round': round,
|
||||
'symbol?': lambda x: isinstance(x, str),
|
||||
'symbol?': lambda x: isinstance(x, Symbol),
|
||||
}
|
||||
)
|
||||
return env
|
||||
|
||||
|
||||
global_env: Environment = standard_env()
|
||||
|
||||
################ Parsing: parse, tokenize, and read_from_tokens
|
||||
|
||||
|
||||
@ -114,7 +113,7 @@ def parse_atom(token: str) -> Atom:
|
||||
try:
|
||||
return float(token)
|
||||
except ValueError:
|
||||
return str(token)
|
||||
return Symbol(token)
|
||||
|
||||
|
||||
################ Interaction: A REPL
|
||||
@ -122,8 +121,9 @@ def parse_atom(token: str) -> Atom:
|
||||
|
||||
def repl(prompt: str = 'lis.py> ') -> None:
|
||||
"A prompt-read-evaluate-print loop."
|
||||
global_env: Environment = standard_env()
|
||||
while True:
|
||||
val = evaluate(parse(input(prompt)))
|
||||
val = evaluate(parse(input(prompt)), global_env)
|
||||
if val is not None:
|
||||
print(lispstr(val))
|
||||
|
||||
@ -142,7 +142,7 @@ def lispstr(exp: object) -> str:
|
||||
def evaluate(x: Expression, env: Environment) -> Any:
|
||||
"Evaluate an expression in an environment."
|
||||
match x:
|
||||
case str(var): # variable reference
|
||||
case Symbol(var): # variable reference
|
||||
return env[var]
|
||||
case literal if not isinstance(x, list): # constant literal
|
||||
return literal
|
||||
@ -151,9 +151,11 @@ def evaluate(x: Expression, env: Environment) -> Any:
|
||||
case ['if', test, conseq, alt]: # (if test conseq alt)
|
||||
exp = conseq if evaluate(test, env) else alt
|
||||
return evaluate(exp, env)
|
||||
case ['define', var, exp]: # (define var exp)
|
||||
case ['define', Symbol(var), exp]: # (define var exp)
|
||||
env[var] = evaluate(exp, env)
|
||||
case ['lambda', parms, body]: # (lambda (var...) body)
|
||||
case ['define', [name, *parms], body]: # (define (fun parm...) body)
|
||||
env[name] = Procedure(parms, body, env)
|
||||
case ['lambda', parms, body]: # (lambda (parm...) body)
|
||||
return Procedure(parms, body, env)
|
||||
case [op, *args]: # (proc arg...)
|
||||
proc = evaluate(op, env)
|
||||
|
18
18-context-mngr/lispy/py3.10/lis_test.py
Executable file → Normal file
18
18-context-mngr/lispy/py3.10/lis_test.py
Executable file → Normal file
@ -6,7 +6,7 @@ from lis import parse, evaluate, Expression, Environment, standard_env
|
||||
|
||||
# Norvig's tests are not isolated: they assume the
|
||||
# same environment from first to last test.
|
||||
ENV_FOR_FIRST_TEST = standard_env()
|
||||
global_env_for_first_test = standard_env()
|
||||
|
||||
@mark.parametrize( 'source, expected', [
|
||||
("(quote (testing 1 (2.0) -3.14e159))", ['testing', 1, [2.0], -3.14e159]),
|
||||
@ -48,7 +48,7 @@ ENV_FOR_FIRST_TEST = standard_env()
|
||||
("(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), ENV_FOR_FIRST_TEST)
|
||||
got = evaluate(parse(source), global_env_for_first_test)
|
||||
assert got == expected
|
||||
|
||||
|
||||
@ -149,3 +149,17 @@ def test_invocation_user_procedure(std_env: Environment) -> None:
|
||||
"""
|
||||
got = evaluate(parse(source), std_env)
|
||||
assert got == 22
|
||||
|
||||
|
||||
###################################### for py3.10/lis.py only
|
||||
|
||||
def test_define_function(std_env: Environment) -> None:
|
||||
source = '(define (max 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
|
||||
|
@ -12,20 +12,21 @@ from collections import ChainMap
|
||||
from collections.abc import MutableMapping
|
||||
from typing import Union, Any
|
||||
|
||||
Atom = Union[float, int, str]
|
||||
Symbol = str
|
||||
Atom = Union[float, int, Symbol]
|
||||
Expression = Union[Atom, list]
|
||||
|
||||
Environment = MutableMapping[str, object]
|
||||
Environment = MutableMapping[Symbol, object]
|
||||
|
||||
|
||||
class Procedure:
|
||||
"A user-defined Scheme procedure."
|
||||
|
||||
def __init__(self, parms: list[str], body: Expression, env: Environment):
|
||||
def __init__(self, parms: list[Symbol], body: Expression, env: Environment):
|
||||
self.parms, self.body, self.env = parms, body, env
|
||||
|
||||
def __call__(self, *args: Expression) -> Any:
|
||||
env: Environment = ChainMap(dict(zip(self.parms, args)), self.env)
|
||||
local_env = dict(zip(self.parms, args))
|
||||
env: Environment = ChainMap(local_env, self.env)
|
||||
return evaluate(self.body, env)
|
||||
|
||||
|
||||
@ -67,14 +68,12 @@ def standard_env() -> Environment:
|
||||
'number?': lambda x: isinstance(x, (int, float)),
|
||||
'procedure?': callable,
|
||||
'round': round,
|
||||
'symbol?': lambda x: isinstance(x, str),
|
||||
'symbol?': lambda x: isinstance(x, Symbol),
|
||||
}
|
||||
)
|
||||
return env
|
||||
|
||||
|
||||
global_env: Environment = standard_env()
|
||||
|
||||
################ Parsing: parse, tokenize, and read_from_tokens
|
||||
|
||||
|
||||
@ -113,7 +112,7 @@ def parse_atom(token: str) -> Atom:
|
||||
try:
|
||||
return float(token)
|
||||
except ValueError:
|
||||
return str(token)
|
||||
return Symbol(token)
|
||||
|
||||
|
||||
################ Interaction: A REPL
|
||||
@ -121,8 +120,9 @@ def parse_atom(token: str) -> Atom:
|
||||
|
||||
def repl(prompt: str = 'lis.py> ') -> None:
|
||||
"A prompt-read-evaluate-print loop."
|
||||
global_env: Environment = standard_env()
|
||||
while True:
|
||||
val = evaluate(parse(input(prompt)))
|
||||
val = evaluate(parse(input(prompt)), global_env)
|
||||
if val is not None:
|
||||
print(lispstr(val))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user