Skip to content

Commit 14de7ef

Browse files
committed
evaluate with pattern matching
1 parent 4df38f2 commit 14de7ef

File tree

3 files changed

+72
-15
lines changed

3 files changed

+72
-15
lines changed

mylis/mylis_3/evaluator.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
from typing import Any
2-
from .mytypes import Environment, Expression, Symbol
32

3+
from .parser import s_expr
4+
from .mytypes import Environment, Expression, InvalidSyntax, Symbol, UndefinedSymbol
45

5-
def evaluate(x: Expression, env: Environment) -> Any:
6+
7+
def evaluate(exp: Expression, env: Environment) -> Any:
68
"Evaluate an expression in an environment."
7-
if isinstance(x, Symbol): # variable reference
8-
return env[x]
9-
elif not isinstance(x, list): # constant literal
10-
return x
11-
elif x[0] == 'define': # (define var exp)
12-
_, var, exp = x
13-
env[var] = evaluate(exp, env)
14-
else: # (proc arg...)
15-
proc_exp, *args = x
16-
proc = evaluate(proc_exp, env)
17-
arg_values = [evaluate(exp, env) for exp in args]
18-
return proc(*arg_values)
9+
match exp:
10+
case int(x) | float(x): # number literal
11+
return x
12+
case Symbol(var): # variable reference
13+
try:
14+
return env[var]
15+
except KeyError as exc:
16+
raise UndefinedSymbol(var) from exc
17+
case ['define', Symbol(var), value_exp]: # (define var exp)
18+
env[var] = evaluate(value_exp, env)
19+
case [op, *args]: # (op arg1...)
20+
proc = evaluate(op, env)
21+
values = [evaluate(arg, env) for arg in args]
22+
return proc(*values)
23+
case _:
24+
raise InvalidSyntax(s_expr(exp))

mylis/mylis_3/evaluator_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def test_evaluate_call(
4242

4343
# Special forms
4444

45-
def test_define(std_env: Environment) -> None:
45+
def test_define_variable(std_env: Environment) -> None:
4646
source = '(define answer (* 6 7))'
4747
got = evaluate(parse(source), std_env)
4848
assert got is None

mylis/mylis_3/mytypes.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from collections.abc import MutableMapping
2+
from typing import TypeAlias
3+
4+
5+
Symbol: TypeAlias = str
6+
Number: TypeAlias = int | float
7+
Atom: TypeAlias = int | float | Symbol
8+
Expression: TypeAlias = Atom | list # type: ignore
9+
10+
Environment = MutableMapping[Symbol, object]
11+
12+
class InterpreterException(Exception):
13+
"""Generic interpreter exception."""
14+
15+
def __init__(self, value: str = ''):
16+
self.value = value
17+
18+
def __str__(self) -> str:
19+
msg = self.__class__.__doc__ or ''
20+
if self.value:
21+
msg = msg.rstrip('.')
22+
if "'" in self.value:
23+
value = self.value
24+
else:
25+
value = repr(self.value)
26+
msg += f': {value}'
27+
return msg
28+
29+
30+
class ParserException(InterpreterException):
31+
"""Generic exception while parsing."""
32+
33+
34+
class UnexpectedCloseBrace(ParserException):
35+
"""Unexpected close brace."""
36+
37+
38+
class BraceNeverClosed(ParserException):
39+
"""Open brace was never closed."""
40+
41+
42+
class EvaluatorException(InterpreterException):
43+
"""Exception while evaluating."""
44+
45+
46+
class InvalidSyntax(EvaluatorException):
47+
"""Invalid syntax."""
48+
49+
50+
class UndefinedSymbol(EvaluatorException):
51+
"""Undefined symbol."""

0 commit comments

Comments
 (0)