Skip to content

Commit 1754a51

Browse files
committed
updated lis.py examples
1 parent f0f1608 commit 1754a51

File tree

9 files changed

+639
-7
lines changed

9 files changed

+639
-7
lines changed

02-array-seq/lispy/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2010-2017 Peter Norvig
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

02-array-seq/lispy/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Norvig's originals and updates
2+
3+
This directory contains:
4+
5+
* `original/`:
6+
Norvig's [`lis.py`](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lis.py),
7+
[`lispy.py`](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lispy.py), and the `lispytest.py` custom test script for testing both;
8+
* `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10.
9+
10+
The `py3.10/` directory also has `lis_test.py` to run with
11+
[pytest](https://docs.pytest.org), including all the
12+
[`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5)
13+
from `original/lispytest.py`,
14+
and additional separate tests for each expression and special form handled by `evaluate`.
15+
16+
17+
## Provenance, Copyright and License
18+
19+
`lis.py` is
20+
[published](https://github.com/norvig/pytudes/blob/c33cd6835a506a57d9fe73e3a8317d49babb13e8/py/lis.py)
21+
in the [norvig/pytudes](https://github.com/norvig/pytudes) repository on Github.
22+
The copyright holder is Peter Norvig and the code is licensed under the
23+
[MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE).
24+
25+
26+
## Changes to Norvig's code
27+
28+
I made small changes to the programs in `original/`:
29+
30+
* In `lis.py`:
31+
* The `Procedure` class accepts a list of expressions as the `body`, and `__call__` evaluates all those expressions in order, returning the value of the last. This is consistent with Scheme's `lambda` syntax and provided a useful example for pattern matching.
32+
* In the `elif` block for `'lambda'`, I added the `*` in front of the `*body` variable in the tuple unpacking to capture the expressions as a list, before calling the `Procedure` constructor.
33+
34+
* In `lispy.py` I made [changes and a pull request](https://github.com/norvig/pytudes/pull/106) to make it run on Python 3.
35+
36+
_Luciano Ramalho<br/>June 29, 2021_

02-array-seq/lispy/original/lis.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
################ Lispy: Scheme Interpreter in Python 3.3+
2+
3+
## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html
4+
5+
################ Imports and Types
6+
7+
import math
8+
import operator as op
9+
from collections import ChainMap as Environment
10+
11+
Symbol = str # A Lisp Symbol is implemented as a Python str
12+
List = list # A Lisp List is implemented as a Python list
13+
Number = (int, float) # A Lisp Number is implemented as a Python int or float
14+
15+
class Procedure(object):
16+
"A user-defined Scheme procedure."
17+
def __init__(self, parms, body, env):
18+
self.parms, self.body, self.env = parms, body, env
19+
def __call__(self, *args):
20+
env = Environment(dict(zip(self.parms, args)), self.env)
21+
for exp in self.body:
22+
result = eval(exp, env)
23+
return result
24+
25+
26+
################ Global Environment
27+
28+
def standard_env():
29+
"An environment with some Scheme standard procedures."
30+
env = {}
31+
env.update(vars(math)) # sin, cos, sqrt, pi, ...
32+
env.update({
33+
'+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv,
34+
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
35+
'abs': abs,
36+
'append': op.add,
37+
'apply': lambda proc, args: proc(*args),
38+
'begin': lambda *x: x[-1],
39+
'car': lambda x: x[0],
40+
'cdr': lambda x: x[1:],
41+
'cons': lambda x,y: [x] + y,
42+
'eq?': op.is_,
43+
'equal?': op.eq,
44+
'length': len,
45+
'list': lambda *x: list(x),
46+
'list?': lambda x: isinstance(x,list),
47+
'map': lambda *args: list(map(*args)),
48+
'max': max,
49+
'min': min,
50+
'not': op.not_,
51+
'null?': lambda x: x == [],
52+
'number?': lambda x: isinstance(x, Number),
53+
'procedure?': callable,
54+
'round': round,
55+
'symbol?': lambda x: isinstance(x, Symbol),
56+
})
57+
return env
58+
59+
global_env = standard_env()
60+
61+
################ Parsing: parse, tokenize, and read_from_tokens
62+
63+
def parse(program):
64+
"Read a Scheme expression from a string."
65+
return read_from_tokens(tokenize(program))
66+
67+
def tokenize(s):
68+
"Convert a string into a list of tokens."
69+
return s.replace('(',' ( ').replace(')',' ) ').split()
70+
71+
def read_from_tokens(tokens):
72+
"Read an expression from a sequence of tokens."
73+
if len(tokens) == 0:
74+
raise SyntaxError('unexpected EOF while reading')
75+
token = tokens.pop(0)
76+
if '(' == token:
77+
L = []
78+
while tokens[0] != ')':
79+
L.append(read_from_tokens(tokens))
80+
tokens.pop(0) # pop off ')'
81+
return L
82+
elif ')' == token:
83+
raise SyntaxError('unexpected )')
84+
else:
85+
return atom(token)
86+
87+
def atom(token):
88+
"Numbers become numbers; every other token is a symbol."
89+
try: return int(token)
90+
except ValueError:
91+
try: return float(token)
92+
except ValueError:
93+
return Symbol(token)
94+
95+
################ Interaction: A REPL
96+
97+
def repl(prompt='lis.py> '):
98+
"A prompt-read-eval-print loop."
99+
while True:
100+
val = eval(parse(input(prompt)))
101+
if val is not None:
102+
print(lispstr(val))
103+
104+
def lispstr(exp):
105+
"Convert a Python object back into a Lisp-readable string."
106+
if isinstance(exp, List):
107+
return '(' + ' '.join(map(lispstr, exp)) + ')'
108+
else:
109+
return str(exp)
110+
111+
################ eval
112+
113+
def eval(x, env=global_env):
114+
"Evaluate an expression in an environment."
115+
if isinstance(x, Symbol): # variable reference
116+
return env[x]
117+
elif not isinstance(x, List): # constant literal
118+
return x
119+
elif x[0] == 'quote': # (quote exp)
120+
(_, exp) = x
121+
return exp
122+
elif x[0] == 'if': # (if test conseq alt)
123+
(_, test, conseq, alt) = x
124+
exp = (conseq if eval(test, env) else alt)
125+
return eval(exp, env)
126+
elif x[0] == 'define': # (define var exp)
127+
(_, var, exp) = x
128+
env[var] = eval(exp, env)
129+
elif x[0] == 'lambda': # (lambda (var...) body)
130+
(_, parms, *body) = x
131+
return Procedure(parms, body, env)
132+
else: # (proc arg...)
133+
proc = eval(x[0], env)
134+
args = [eval(exp, env) for exp in x[1:]]
135+
return proc(*args)

0 commit comments

Comments
 (0)