complete draft: update from Atlas
This commit is contained in:
parent
08a4001b43
commit
135eca25d9
@ -5,7 +5,7 @@ import random
|
||||
from tombola import Tombola
|
||||
|
||||
|
||||
class LotteryBlower(Tombola):
|
||||
class LottoBlower(Tombola):
|
||||
|
||||
def __init__(self, iterable):
|
||||
self._balls = list(iterable) # <1>
|
||||
@ -17,14 +17,14 @@ class LotteryBlower(Tombola):
|
||||
try:
|
||||
position = random.randrange(len(self._balls)) # <2>
|
||||
except ValueError:
|
||||
raise LookupError('pick from empty BingoCage')
|
||||
raise LookupError('pick from empty LottoBlower')
|
||||
return self._balls.pop(position) # <3>
|
||||
|
||||
def loaded(self): # <4>
|
||||
return bool(self._balls)
|
||||
|
||||
def inspect(self): # <5>
|
||||
return tuple(sorted(self._balls))
|
||||
return tuple(self._balls)
|
||||
|
||||
|
||||
# end::LOTTERY_BLOWER[]
|
||||
|
@ -28,7 +28,7 @@ class Tombola(abc.ABC): # <1>
|
||||
except LookupError:
|
||||
break
|
||||
self.load(items) # <7>
|
||||
return tuple(sorted(items))
|
||||
return tuple(items)
|
||||
|
||||
|
||||
# end::TOMBOLA_ABC[]
|
||||
|
@ -1,4 +1,5 @@
|
||||
# tag::TOMBOLA_RUNNER[]
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import doctest
|
||||
|
||||
from tombola import Tombola
|
||||
@ -13,8 +14,7 @@ TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
|
||||
def main(argv):
|
||||
verbose = '-v' in argv
|
||||
real_subclasses = Tombola.__subclasses__() # <2>
|
||||
virtual_subclasses = list(Tombola._abc_registry) # <3>
|
||||
|
||||
virtual_subclasses = [tombolist.TomboList] # <3>
|
||||
for cls in real_subclasses + virtual_subclasses: # <4>
|
||||
test(cls, verbose)
|
||||
|
||||
@ -33,4 +33,3 @@ def test(cls, verbose=False):
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
main(sys.argv)
|
||||
# end::TOMBOLA_RUNNER[]
|
||||
|
@ -11,8 +11,8 @@ Create and load instance from iterable::
|
||||
>>> globe = ConcreteTombola(balls)
|
||||
>>> globe.loaded()
|
||||
True
|
||||
>>> globe.inspect()
|
||||
(0, 1, 2)
|
||||
>>> sorted(globe.inspect())
|
||||
[0, 1, 2]
|
||||
|
||||
|
||||
Pick and collect balls::
|
||||
|
@ -18,6 +18,6 @@ class TomboList(list): # <2>
|
||||
return bool(self) # <6>
|
||||
|
||||
def inspect(self):
|
||||
return tuple(sorted(self))
|
||||
return tuple(self)
|
||||
|
||||
# Tombola.register(TomboList) # <7>
|
||||
|
@ -38,8 +38,7 @@ deploy(trash_can)
|
||||
# tag::DEPLOY_NOT_VALID[]
|
||||
compost_can: TrashCan[Compostable] = TrashCan()
|
||||
deploy(compost_can)
|
||||
# end::DEPLOY_NOT_VALID[]
|
||||
|
||||
## Argument 1 to "deploy" has
|
||||
## mypy: Argument 1 to "deploy" has
|
||||
## incompatible type "TrashCan[Compostable]"
|
||||
## expected "TrashCan[Biodegradable]"
|
||||
# end::DEPLOY_NOT_VALID[]
|
||||
|
@ -38,11 +38,12 @@ orange_juice_dispenser = BeverageDispenser(OrangeJuice())
|
||||
install(orange_juice_dispenser)
|
||||
# end::INSTALL_JUICE_DISPENSERS[]
|
||||
|
||||
################################################ not a juice dispenser
|
||||
################################################ more general dispenser
|
||||
|
||||
# tag::INSTALL_BEVERAGE_DISPENSER[]
|
||||
beverage_dispenser = BeverageDispenser(Beverage())
|
||||
|
||||
## Argument 1 to "install" has
|
||||
install(beverage_dispenser)
|
||||
## mypy: Argument 1 to "install" has
|
||||
## incompatible type "BeverageDispenser[Beverage]"
|
||||
## expected "BeverageDispenser[Juice]"
|
||||
install(beverage_dispenser)
|
||||
# end::INSTALL_BEVERAGE_DISPENSER[]
|
||||
|
@ -37,7 +37,7 @@ install(juice_dispenser)
|
||||
# tag::INSTALL_BEVERAGE_DISPENSER[]
|
||||
beverage_dispenser = BeverageDispenser(Beverage())
|
||||
install(beverage_dispenser)
|
||||
## Argument 1 to "install" has
|
||||
## mypy: Argument 1 to "install" has
|
||||
## incompatible type "BeverageDispenser[Beverage]"
|
||||
## expected "BeverageDispenser[Juice]"
|
||||
# end::INSTALL_BEVERAGE_DISPENSER[]
|
||||
@ -48,7 +48,7 @@ install(beverage_dispenser)
|
||||
# tag::INSTALL_ORANGE_JUICE_DISPENSER[]
|
||||
orange_juice_dispenser = BeverageDispenser(OrangeJuice())
|
||||
install(orange_juice_dispenser)
|
||||
# end::INSTALL_ORANGE_JUICE_DISPENSER[]
|
||||
## Argument 1 to "install" has
|
||||
## mypy: Argument 1 to "install" has
|
||||
## incompatible type "BeverageDispenser[OrangeJuice]"
|
||||
## expected "BeverageDispenser[Juice]"
|
||||
# end::INSTALL_ORANGE_JUICE_DISPENSER[]
|
||||
|
2
15-more-types/cast/empty.py
Normal file
2
15-more-types/cast/empty.py
Normal file
@ -0,0 +1,2 @@
|
||||
# Mypy 0.812 can't spot this inevitable runtime IndexError
|
||||
print([][0])
|
23
15-more-types/cast/find.py
Normal file
23
15-more-types/cast/find.py
Normal file
@ -0,0 +1,23 @@
|
||||
# tag::CAST[]
|
||||
from typing import cast
|
||||
|
||||
def find_first_str(a: list[object]) -> str:
|
||||
index = next(i for i, x in enumerate(a) if isinstance(x, str))
|
||||
# We only get here if there's at least one string in a
|
||||
return cast(str, a[index])
|
||||
# end::CAST[]
|
||||
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
l1 = [10, 20, 'thirty', 40]
|
||||
if TYPE_CHECKING:
|
||||
reveal_type(l1)
|
||||
|
||||
print(find_first_str(l1))
|
||||
|
||||
l2 = [0, ()]
|
||||
try:
|
||||
find_first_str(l2)
|
||||
except StopIteration as e:
|
||||
print(repr(e))
|
37
15-more-types/cast/tcp_echo.py
Normal file
37
15-more-types/cast/tcp_echo.py
Normal file
@ -0,0 +1,37 @@
|
||||
import asyncio
|
||||
|
||||
from asyncio import StreamReader, StreamWriter
|
||||
|
||||
# tag::CAST_IMPORTS[]
|
||||
from asyncio.trsock import TransportSocket
|
||||
from typing import cast
|
||||
# end::CAST_IMPORTS[]
|
||||
|
||||
async def handle_echo(reader: StreamReader, writer: StreamWriter) -> None:
|
||||
data = await reader.read(100)
|
||||
message = data.decode()
|
||||
addr = writer.get_extra_info('peername')
|
||||
|
||||
print(f"Received {message!r} from {addr!r}")
|
||||
|
||||
print(f"Send: {message!r}")
|
||||
writer.write(data)
|
||||
await writer.drain()
|
||||
|
||||
print("Close the connection")
|
||||
writer.close()
|
||||
|
||||
async def main() -> None:
|
||||
server = await asyncio.start_server(
|
||||
handle_echo, '127.0.0.1', 8888)
|
||||
|
||||
# tag::CAST_USE[]
|
||||
socket_list = cast(tuple[TransportSocket, ...], server.sockets)
|
||||
addr = socket_list[0].getsockname()
|
||||
# end::CAST_USE[]
|
||||
print(f'Serving on {addr}')
|
||||
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
asyncio.run(main())
|
29
15-more-types/cast/tcp_echo_no_cast.py
Normal file
29
15-more-types/cast/tcp_echo_no_cast.py
Normal file
@ -0,0 +1,29 @@
|
||||
import asyncio
|
||||
|
||||
from asyncio import StreamReader, StreamWriter
|
||||
|
||||
async def handle_echo(reader: StreamReader, writer: StreamWriter) -> None:
|
||||
data = await reader.read(100)
|
||||
message = data.decode()
|
||||
addr = writer.get_extra_info('peername')
|
||||
|
||||
print(f"Received {message!r} from {addr!r}")
|
||||
|
||||
print(f"Send: {message!r}")
|
||||
writer.write(data)
|
||||
await writer.drain()
|
||||
|
||||
print("Close the connection")
|
||||
writer.close()
|
||||
|
||||
async def main() -> None:
|
||||
server = await asyncio.start_server(
|
||||
handle_echo, '127.0.0.1', 8888)
|
||||
|
||||
addr = server.sockets[0].getsockname()
|
||||
print(f'Serving on {addr}')
|
||||
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
asyncio.run(main())
|
3
15-more-types/clip_annot_demo.py
Normal file
3
15-more-types/clip_annot_demo.py
Normal file
@ -0,0 +1,3 @@
|
||||
from clip_annot_post import clip
|
||||
|
||||
print(clip.__annotations__)
|
@ -1,3 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
"""
|
||||
>>> clip('banana ', 6)
|
||||
'banana'
|
||||
@ -18,9 +20,9 @@
|
||||
"""
|
||||
|
||||
# tag::CLIP_ANNOT[]
|
||||
|
||||
def clip(text: str, max_len: 'int > 0' = 80) -> str: # <1>
|
||||
"""Return text clipped at the last space before or after max_len
|
||||
def clip(text: str, max_len: int = 80) -> str:
|
||||
"""Return new ``str`` clipped at last space before or after ``max_len``.
|
||||
Return full ``text`` if no space found.
|
||||
"""
|
||||
end = None
|
||||
if len(text) > max_len:
|
||||
@ -31,7 +33,7 @@ def clip(text: str, max_len: 'int > 0' = 80) -> str: # <1>
|
||||
space_after = text.rfind(' ', max_len)
|
||||
if space_after >= 0:
|
||||
end = space_after
|
||||
if end is None: # no spaces were found
|
||||
if end is None:
|
||||
end = len(text)
|
||||
return text[:end].rstrip()
|
||||
|
@ -1,10 +0,0 @@
|
||||
>>> from clip_annot import clip
|
||||
>>> from inspect import signature
|
||||
>>> sig = signature(clip)
|
||||
>>> sig.return_annotation
|
||||
<class 'str'>
|
||||
>>> for param in sig.parameters.values():
|
||||
... note = repr(param.annotation).ljust(13)
|
||||
... print(note, ':', param.name, '=', param.default)
|
||||
<class 'str'> : text = <class 'inspect._empty'>
|
||||
'int > 0' : max_len = 80
|
29
15-more-types/lotto/generic_lotto.py
Normal file
29
15-more-types/lotto/generic_lotto.py
Normal file
@ -0,0 +1,29 @@
|
||||
import random
|
||||
|
||||
from collections.abc import Iterable
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
from tombola import Tombola
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class LottoBlower(Tombola, Generic[T]): # <1>
|
||||
|
||||
def __init__(self, items: Iterable[T]) -> None: # <2>
|
||||
self._balls = list[T](items)
|
||||
|
||||
def load(self, items: Iterable[T]) -> None: # <3>
|
||||
self._balls.extend(items)
|
||||
|
||||
def pick(self) -> T: # <4>
|
||||
try:
|
||||
position = random.randrange(len(self._balls))
|
||||
except ValueError:
|
||||
raise LookupError('pick from empty LottoBlower')
|
||||
return self._balls.pop(position)
|
||||
|
||||
def loaded(self) -> bool: # <5>
|
||||
return bool(self._balls)
|
||||
|
||||
def inspect(self) -> tuple[T, ...]: # <6>
|
||||
return tuple(self._balls)
|
28
15-more-types/lotto/generic_lotto_demo.py
Executable file
28
15-more-types/lotto/generic_lotto_demo.py
Executable file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
# tag::LOTTO_USE[]
|
||||
from generic_lotto import LottoBlower
|
||||
|
||||
machine = LottoBlower[int](range(1, 11)) # <1>
|
||||
|
||||
first = machine.pick() # <2>
|
||||
remain = machine.inspect() # <3>
|
||||
# end::LOTTO_USE[]
|
||||
|
||||
expected = set(i for i in range(1, 11) if i != first)
|
||||
|
||||
assert set(remain) == expected
|
||||
|
||||
print('picked:', first)
|
||||
print('remain:', remain)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
reveal_type(first)
|
||||
# Revealed type is 'builtins.int*'
|
||||
if TYPE_CHECKING:
|
||||
reveal_type(remain)
|
||||
# Revealed type is 'builtins.tuple[builtins.int*]'
|
||||
|
||||
|
18
15-more-types/lotto/generic_lotto_errors.py
Executable file
18
15-more-types/lotto/generic_lotto_errors.py
Executable file
@ -0,0 +1,18 @@
|
||||
from generic_lotto import LottoBlower
|
||||
|
||||
machine = LottoBlower[int]([1, .2])
|
||||
## error: List item 1 has incompatible type "float"; # <1>
|
||||
## expected "int"
|
||||
|
||||
machine = LottoBlower[int](range(1, 11))
|
||||
|
||||
machine.load('ABC')
|
||||
## error: Argument 1 to "load" of "LottoBlower" # <2>
|
||||
## has incompatible type "str";
|
||||
## expected "Iterable[int]"
|
||||
## note: Following member(s) of "str" have conflicts:
|
||||
## note: Expected:
|
||||
## note: def __iter__(self) -> Iterator[int]
|
||||
## note: Got:
|
||||
## note: def __iter__(self) -> Iterator[str]
|
||||
|
34
15-more-types/lotto/tombola.py
Normal file
34
15-more-types/lotto/tombola.py
Normal file
@ -0,0 +1,34 @@
|
||||
# tag::TOMBOLA_ABC[]
|
||||
|
||||
import abc
|
||||
|
||||
class Tombola(abc.ABC): # <1>
|
||||
|
||||
@abc.abstractmethod
|
||||
def load(self, iterable): # <2>
|
||||
"""Add items from an iterable."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def pick(self): # <3>
|
||||
"""Remove item at random, returning it.
|
||||
|
||||
This method should raise `LookupError` when the instance is empty.
|
||||
"""
|
||||
|
||||
def loaded(self): # <4>
|
||||
"""Return `True` if there's at least 1 item, `False` otherwise."""
|
||||
return bool(self.inspect()) # <5>
|
||||
|
||||
def inspect(self):
|
||||
"""Return a sorted tuple with the items currently inside."""
|
||||
items = []
|
||||
while True: # <6>
|
||||
try:
|
||||
items.append(self.pick())
|
||||
except LookupError:
|
||||
break
|
||||
self.load(items) # <7>
|
||||
return tuple(items)
|
||||
|
||||
|
||||
# end::TOMBOLA_ABC[]
|
@ -1,13 +1,14 @@
|
||||
from functools import reduce # <1>
|
||||
from operator import add
|
||||
from typing import overload, Iterable, Union, TypeVar
|
||||
import functools
|
||||
import operator
|
||||
from collections.abc import Iterable
|
||||
from typing import overload, Union, TypeVar
|
||||
|
||||
T = TypeVar('T')
|
||||
S = TypeVar('S') # <2>
|
||||
S = TypeVar('S') # <1>
|
||||
|
||||
@overload
|
||||
def sum(it: Iterable[T]) -> Union[T, int]: ... # <3>
|
||||
def sum(it: Iterable[T]) -> Union[T, int]: ... # <2>
|
||||
@overload
|
||||
def sum(it: Iterable[T], /, start: S) -> Union[T, S]: ... # <4>
|
||||
def sum(it, /, start=0): # <5>
|
||||
return reduce(add, it, start)
|
||||
def sum(it: Iterable[T], /, start: S) -> Union[T, S]: ... # <3>
|
||||
def sum(it, /, start=0): # <4>
|
||||
return functools.reduce(operator.add, it, start)
|
||||
|
32
15-more-types/protocol/abs_demo.py
Executable file
32
15-more-types/protocol/abs_demo.py
Executable file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import math
|
||||
from typing import NamedTuple, SupportsAbs
|
||||
|
||||
class Vector2d(NamedTuple):
|
||||
x: float
|
||||
y: float
|
||||
|
||||
def __abs__(self) -> float: # <1>
|
||||
return math.hypot(self.x, self.y)
|
||||
|
||||
def is_unit(v: SupportsAbs[float]) -> bool: # <2>
|
||||
"""'True' if the magnitude of 'v' is close to 1."""
|
||||
return math.isclose(abs(v), 1.0) # <3>
|
||||
|
||||
assert issubclass(Vector2d, SupportsAbs) # <4>
|
||||
|
||||
v0 = Vector2d(0, 1) # <5>
|
||||
sqrt2 = math.sqrt(2)
|
||||
v1 = Vector2d(sqrt2 / 2, sqrt2 / 2)
|
||||
v2 = Vector2d(1, 1)
|
||||
v3 = complex(.5, math.sqrt(3) / 2)
|
||||
v4 = 1 # <6>
|
||||
|
||||
assert is_unit(v0)
|
||||
assert is_unit(v1)
|
||||
assert not is_unit(v2)
|
||||
assert is_unit(v3)
|
||||
assert is_unit(v4)
|
||||
|
||||
print('OK')
|
@ -12,10 +12,10 @@ MISSING = object()
|
||||
EMPTY_MSG = 'max() arg is an empty sequence'
|
||||
|
||||
@overload
|
||||
def max(__arg1: LT, __arg2: LT, *_args: LT, key: None = ...) -> LT:
|
||||
def max(__arg1: LT, __arg2: LT, *args: LT, key: None = ...) -> LT:
|
||||
...
|
||||
@overload
|
||||
def max(__arg1: T, __arg2: T, *_args: T, key: Callable[[T], LT]) -> T:
|
||||
def max(__arg1: T, __arg2: T, *args: T, key: Callable[[T], LT]) -> T:
|
||||
...
|
||||
@overload
|
||||
def max(__iterable: Iterable[LT], *, key: None = ...) -> LT:
|
7
15-more-types/protocol/random/generic_randompick.py
Normal file
7
15-more-types/protocol/random/generic_randompick.py
Normal file
@ -0,0 +1,7 @@
|
||||
from typing import Protocol, runtime_checkable, TypeVar
|
||||
|
||||
T_co = TypeVar('T_co', covariant=True) # <1>
|
||||
|
||||
@runtime_checkable
|
||||
class RandomPicker(Protocol[T_co]): # <2>
|
||||
def pick(self) -> T_co: ... # <3>
|
@ -1,24 +1,26 @@
|
||||
import random
|
||||
from typing import Iterable, TYPE_CHECKING
|
||||
from typing import Iterable, Generic, TypeVar, TYPE_CHECKING
|
||||
|
||||
from randompick_generic import GenericRandomPicker
|
||||
T_co = TypeVar('T_co', covariant=True)
|
||||
|
||||
from generic_randompick import RandomPicker
|
||||
|
||||
|
||||
class LottoPicker:
|
||||
def __init__(self, items: Iterable[int]) -> None:
|
||||
class LottoPicker(Generic[T_co]):
|
||||
def __init__(self, items: Iterable[T_co]) -> None:
|
||||
self._items = list(items)
|
||||
random.shuffle(self._items)
|
||||
|
||||
def pick(self) -> int:
|
||||
def pick(self) -> T_co:
|
||||
return self._items.pop()
|
||||
|
||||
|
||||
def test_issubclass() -> None:
|
||||
assert issubclass(LottoPicker, GenericRandomPicker)
|
||||
assert issubclass(LottoPicker, RandomPicker)
|
||||
|
||||
|
||||
def test_isinstance() -> None:
|
||||
popper: GenericRandomPicker = LottoPicker([1])
|
||||
popper: RandomPicker = LottoPicker[int]([1])
|
||||
if TYPE_CHECKING:
|
||||
reveal_type(popper)
|
||||
# Revealed type is '???'
|
@ -1,7 +0,0 @@
|
||||
from typing import Protocol, runtime_checkable, TypeVar
|
||||
|
||||
T_co = TypeVar('T_co', covariant=True)
|
||||
|
||||
@runtime_checkable
|
||||
class GenericRandomPicker(Protocol[T_co]):
|
||||
def pick(self) -> T_co: ...
|
21
18-context-mngr/lispy/LICENSE
Normal file
21
18-context-mngr/lispy/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2010-2017 Peter Norvig
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
24
18-context-mngr/lispy/README.md
Normal file
24
18-context-mngr/lispy/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# lis.py
|
||||
|
||||
This directory contains 3 versions of
|
||||
[Peter Norvig's `lis.py` interpreter](https://norvig.com/lispy.html)
|
||||
for Scheme.
|
||||
|
||||
* `original/`: Norvig's `lis.py` unchanged, `lispy.py` with
|
||||
[minor changes](https://github.com/norvig/pytudes/pull/106) to run on Python 3,
|
||||
and the `lispytest.py` custom test suite;
|
||||
* `py3.9/`: `lis.py` with type hints and a few minor edits—requires Python 3.9;
|
||||
* `py3.10/`: `lis.py` with type hints, pattern matching, and minor edits—requires Python 3.10.
|
||||
|
||||
The `py3.9/` and `py3.10/` directories also have identical `lis_test.py` to run with
|
||||
[pytest](https://docs.pytest.org).
|
||||
These files include all the
|
||||
[`lis_tests` suite](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/py/lispytest.py#L5)
|
||||
from `original/lispytest.py`,
|
||||
and individual tests for each expression and special form handled by `evaluate`.
|
||||
|
||||
## Provenance, Copyright and License
|
||||
|
||||
`lis.py` is published in the [norvig/pytudes](https://github.com/norvig/pytudes) repository on Github.
|
||||
The copyright holder is Peter Norvig and the code is licensed under the
|
||||
[MIT license](https://github.com/norvig/pytudes/blob/60168bce8cdfacf57c92a5b2979f0b2e95367753/LICENSE).
|
132
18-context-mngr/lispy/original/lis.py
Normal file
132
18-context-mngr/lispy/original/lis.py
Normal file
@ -0,0 +1,132 @@
|
||||
################ Lispy: Scheme Interpreter in Python 3.3+
|
||||
|
||||
## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html
|
||||
|
||||
################ Imports and Types
|
||||
|
||||
import math
|
||||
import operator as op
|
||||
from collections import ChainMap as Environment
|
||||
|
||||
Symbol = str # A Lisp Symbol is implemented as a Python str
|
||||
List = list # A Lisp List is implemented as a Python list
|
||||
Number = (int, float) # A Lisp Number is implemented as a Python int or float
|
||||
|
||||
class Procedure(object):
|
||||
"A user-defined Scheme procedure."
|
||||
def __init__(self, parms, body, env):
|
||||
self.parms, self.body, self.env = parms, body, env
|
||||
def __call__(self, *args):
|
||||
env = Environment(dict(zip(self.parms, args)), self.env)
|
||||
return eval(self.body, env)
|
||||
|
||||
################ Global Environment
|
||||
|
||||
def standard_env():
|
||||
"An environment with some Scheme standard procedures."
|
||||
env = {}
|
||||
env.update(vars(math)) # sin, cos, sqrt, pi, ...
|
||||
env.update({
|
||||
'+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv,
|
||||
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
|
||||
'abs': abs,
|
||||
'append': op.add,
|
||||
'apply': lambda proc, args: proc(*args),
|
||||
'begin': lambda *x: x[-1],
|
||||
'car': lambda x: x[0],
|
||||
'cdr': lambda x: x[1:],
|
||||
'cons': lambda x,y: [x] + y,
|
||||
'eq?': op.is_,
|
||||
'equal?': op.eq,
|
||||
'length': len,
|
||||
'list': lambda *x: list(x),
|
||||
'list?': lambda x: isinstance(x,list),
|
||||
'map': lambda *args: list(map(*args)),
|
||||
'max': max,
|
||||
'min': min,
|
||||
'not': op.not_,
|
||||
'null?': lambda x: x == [],
|
||||
'number?': lambda x: isinstance(x, Number),
|
||||
'procedure?': callable,
|
||||
'round': round,
|
||||
'symbol?': lambda x: isinstance(x, Symbol),
|
||||
})
|
||||
return env
|
||||
|
||||
global_env = standard_env()
|
||||
|
||||
################ Parsing: parse, tokenize, and read_from_tokens
|
||||
|
||||
def parse(program):
|
||||
"Read a Scheme expression from a string."
|
||||
return read_from_tokens(tokenize(program))
|
||||
|
||||
def tokenize(s):
|
||||
"Convert a string into a list of tokens."
|
||||
return s.replace('(',' ( ').replace(')',' ) ').split()
|
||||
|
||||
def read_from_tokens(tokens):
|
||||
"Read an expression from a sequence of tokens."
|
||||
if len(tokens) == 0:
|
||||
raise SyntaxError('unexpected EOF while reading')
|
||||
token = tokens.pop(0)
|
||||
if '(' == token:
|
||||
L = []
|
||||
while tokens[0] != ')':
|
||||
L.append(read_from_tokens(tokens))
|
||||
tokens.pop(0) # pop off ')'
|
||||
return L
|
||||
elif ')' == token:
|
||||
raise SyntaxError('unexpected )')
|
||||
else:
|
||||
return atom(token)
|
||||
|
||||
def atom(token):
|
||||
"Numbers become numbers; every other token is a symbol."
|
||||
try: return int(token)
|
||||
except ValueError:
|
||||
try: return float(token)
|
||||
except ValueError:
|
||||
return Symbol(token)
|
||||
|
||||
################ Interaction: A REPL
|
||||
|
||||
def repl(prompt='lis.py> '):
|
||||
"A prompt-read-eval-print loop."
|
||||
while True:
|
||||
val = eval(parse(input(prompt)))
|
||||
if val is not None:
|
||||
print(lispstr(val))
|
||||
|
||||
def lispstr(exp):
|
||||
"Convert a Python object back into a Lisp-readable string."
|
||||
if isinstance(exp, List):
|
||||
return '(' + ' '.join(map(lispstr, exp)) + ')'
|
||||
else:
|
||||
return str(exp)
|
||||
|
||||
################ eval
|
||||
|
||||
def eval(x, env=global_env):
|
||||
"Evaluate an expression in an environment."
|
||||
if isinstance(x, Symbol): # variable reference
|
||||
return env[x]
|
||||
elif not isinstance(x, List): # constant literal
|
||||
return x
|
||||
elif x[0] == 'quote': # (quote exp)
|
||||
(_, exp) = x
|
||||
return exp
|
||||
elif x[0] == 'if': # (if test conseq alt)
|
||||
(_, test, conseq, alt) = x
|
||||
exp = (conseq if eval(test, env) else alt)
|
||||
return eval(exp, env)
|
||||
elif x[0] == 'define': # (define var exp)
|
||||
(_, var, exp) = x
|
||||
env[var] = eval(exp, env)
|
||||
elif x[0] == 'lambda': # (lambda (var...) body)
|
||||
(_, parms, body) = x
|
||||
return Procedure(parms, body, env)
|
||||
else: # (proc arg...)
|
||||
proc = eval(x[0], env)
|
||||
args = [eval(exp, env) for exp in x[1:]]
|
||||
return proc(*args)
|
316
18-context-mngr/lispy/original/lispy.py
Normal file
316
18-context-mngr/lispy/original/lispy.py
Normal file
@ -0,0 +1,316 @@
|
||||
################ Scheme Interpreter in Python
|
||||
|
||||
## (c) Peter Norvig, 2010; See http://norvig.com/lispy2.html
|
||||
|
||||
################ Symbol, Procedure, classes
|
||||
|
||||
import re, sys, io
|
||||
|
||||
class Symbol(str): pass
|
||||
|
||||
def Sym(s, symbol_table={}):
|
||||
"Find or create unique Symbol entry for str s in symbol table."
|
||||
if s not in symbol_table: symbol_table[s] = Symbol(s)
|
||||
return symbol_table[s]
|
||||
|
||||
_quote, _if, _set, _define, _lambda, _begin, _definemacro, = map(Sym,
|
||||
"quote if set! define lambda begin define-macro".split())
|
||||
|
||||
_quasiquote, _unquote, _unquotesplicing = map(Sym,
|
||||
"quasiquote unquote unquote-splicing".split())
|
||||
|
||||
class Procedure:
|
||||
"A user-defined Scheme procedure."
|
||||
def __init__(self, parms, exp, env):
|
||||
self.parms, self.exp, self.env = parms, exp, env
|
||||
def __call__(self, *args):
|
||||
return eval(self.exp, Env(self.parms, args, self.env))
|
||||
|
||||
################ parse, read, and user interaction
|
||||
|
||||
def parse(inport):
|
||||
"Parse a program: read and expand/error-check it."
|
||||
# Backwards compatibility: given a str, convert it to an InPort
|
||||
if isinstance(inport, str): inport = InPort(io.StringIO(inport))
|
||||
return expand(read(inport), toplevel=True)
|
||||
|
||||
eof_object = Symbol('#<eof-object>') # Note: uninterned; can't be read
|
||||
|
||||
class InPort:
|
||||
"An input port. Retains a line of chars."
|
||||
tokenizer = r"""\s*(,@|[('`,)]|"(?:[\\].|[^\\"])*"|;.*|[^\s('"`,;)]*)(.*)"""
|
||||
def __init__(self, file):
|
||||
self.file = file; self.line = ''
|
||||
def next_token(self):
|
||||
"Return the next token, reading new text into line buffer if needed."
|
||||
while True:
|
||||
if self.line == '': self.line = self.file.readline()
|
||||
if self.line == '': return eof_object
|
||||
token, self.line = re.match(InPort.tokenizer, self.line).groups()
|
||||
if token != '' and not token.startswith(';'):
|
||||
return token
|
||||
|
||||
def readchar(inport):
|
||||
"Read the next character from an input port."
|
||||
if inport.line != '':
|
||||
ch, inport.line = inport.line[0], inport.line[1:]
|
||||
return ch
|
||||
else:
|
||||
return inport.file.read(1) or eof_object
|
||||
|
||||
def read(inport):
|
||||
"Read a Scheme expression from an input port."
|
||||
def read_ahead(token):
|
||||
if '(' == token:
|
||||
L = []
|
||||
while True:
|
||||
token = inport.next_token()
|
||||
if token == ')': return L
|
||||
else: L.append(read_ahead(token))
|
||||
elif ')' == token: raise SyntaxError('unexpected )')
|
||||
elif token in quotes: return [quotes[token], read(inport)]
|
||||
elif token is eof_object: raise SyntaxError('unexpected EOF in list')
|
||||
else: return atom(token)
|
||||
# body of read:
|
||||
token1 = inport.next_token()
|
||||
return eof_object if token1 is eof_object else read_ahead(token1)
|
||||
|
||||
quotes = {"'":_quote, "`":_quasiquote, ",":_unquote, ",@":_unquotesplicing}
|
||||
|
||||
def atom(token):
|
||||
'Numbers become numbers; #t and #f are booleans; "..." string; otherwise Symbol.'
|
||||
if token == '#t': return True
|
||||
elif token == '#f': return False
|
||||
elif token[0] == '"': return token[1:-1]
|
||||
try: return int(token)
|
||||
except ValueError:
|
||||
try: return float(token)
|
||||
except ValueError:
|
||||
try: return complex(token.replace('i', 'j', 1))
|
||||
except ValueError:
|
||||
return Sym(token)
|
||||
|
||||
def to_string(x):
|
||||
"Convert a Python object back into a Lisp-readable string."
|
||||
if x is True: return "#t"
|
||||
elif x is False: return "#f"
|
||||
elif isa(x, Symbol): return x
|
||||
elif isa(x, str): return repr(x)
|
||||
elif isa(x, list): return '('+' '.join(map(to_string, x))+')'
|
||||
elif isa(x, complex): return str(x).replace('j', 'i')
|
||||
else: return str(x)
|
||||
|
||||
def load(filename):
|
||||
"Eval every expression from a file."
|
||||
repl(None, InPort(open(filename)), None)
|
||||
|
||||
def repl(prompt='lispy> ', inport=InPort(sys.stdin), out=sys.stdout):
|
||||
"A prompt-read-eval-print loop."
|
||||
sys.stderr.write("Lispy version 2.0\n")
|
||||
while True:
|
||||
try:
|
||||
if prompt: sys.stderr.write(prompt)
|
||||
x = parse(inport)
|
||||
if x is eof_object: return
|
||||
val = eval(x)
|
||||
if val is not None and out: print(to_string(val), file=out)
|
||||
except Exception as e:
|
||||
print('%s: %s' % (type(e).__name__, e))
|
||||
|
||||
################ Environment class
|
||||
|
||||
class Env(dict):
|
||||
"An environment: a dict of {'var':val} pairs, with an outer Env."
|
||||
def __init__(self, parms=(), args=(), outer=None):
|
||||
# Bind parm list to corresponding args, or single parm to list of args
|
||||
self.outer = outer
|
||||
if isa(parms, Symbol):
|
||||
self.update({parms:list(args)})
|
||||
else:
|
||||
if len(args) != len(parms):
|
||||
raise TypeError('expected %s, given %s, '
|
||||
% (to_string(parms), to_string(args)))
|
||||
self.update(zip(parms,args))
|
||||
def find(self, var):
|
||||
"Find the innermost Env where var appears."
|
||||
if var in self: return self
|
||||
elif self.outer is None: raise LookupError(var)
|
||||
else: return self.outer.find(var)
|
||||
|
||||
def is_pair(x): return x != [] and isa(x, list)
|
||||
def cons(x, y): return [x]+y
|
||||
|
||||
def callcc(proc):
|
||||
"Call proc with current continuation; escape only"
|
||||
ball = RuntimeWarning("Sorry, can't continue this continuation any longer.")
|
||||
def throw(retval): ball.retval = retval; raise ball
|
||||
try:
|
||||
return proc(throw)
|
||||
except RuntimeWarning as w:
|
||||
if w is ball: return ball.retval
|
||||
else: raise w
|
||||
|
||||
def add_globals(self):
|
||||
"Add some Scheme standard procedures."
|
||||
import math, cmath, operator as op
|
||||
self.update(vars(math))
|
||||
self.update(vars(cmath))
|
||||
self.update({
|
||||
'+':op.add, '-':op.sub, '*':op.mul, '/':op.truediv, 'not':op.not_,
|
||||
'>':op.gt, '<':op.lt, '>=':op.ge, '<=':op.le, '=':op.eq,
|
||||
'equal?':op.eq, 'eq?':op.is_, 'length':len, 'cons':cons,
|
||||
'car':lambda x:x[0], 'cdr':lambda x:x[1:], 'append':op.add,
|
||||
'list':lambda *x:list(x), 'list?': lambda x:isa(x,list),
|
||||
'null?':lambda x:x==[], 'symbol?':lambda x: isa(x, Symbol),
|
||||
'boolean?':lambda x: isa(x, bool), 'pair?':is_pair,
|
||||
'port?': lambda x:isa(x,file), 'apply':lambda proc,l: proc(*l),
|
||||
'eval':lambda x: eval(expand(x)), 'load':lambda fn: load(fn), 'call/cc':callcc,
|
||||
'open-input-file':open,'close-input-port':lambda p: p.file.close(),
|
||||
'open-output-file':lambda f:open(f,'w'), 'close-output-port':lambda p: p.close(),
|
||||
'eof-object?':lambda x:x is eof_object, 'read-char':readchar,
|
||||
'read':read, 'write':lambda x,port=sys.stdout:port.write(to_string(x)),
|
||||
'display':lambda x,port=sys.stdout:port.write(x if isa(x,str) else to_string(x))})
|
||||
return self
|
||||
|
||||
isa = isinstance
|
||||
|
||||
global_env = add_globals(Env())
|
||||
|
||||
################ eval (tail recursive)
|
||||
|
||||
def eval(x, env=global_env):
|
||||
"Evaluate an expression in an environment."
|
||||
while True:
|
||||
if isa(x, Symbol): # variable reference
|
||||
return env.find(x)[x]
|
||||
elif not isa(x, list): # constant literal
|
||||
return x
|
||||
elif x[0] is _quote: # (quote exp)
|
||||
(_, exp) = x
|
||||
return exp
|
||||
elif x[0] is _if: # (if test conseq alt)
|
||||
(_, test, conseq, alt) = x
|
||||
x = (conseq if eval(test, env) else alt)
|
||||
elif x[0] is _set: # (set! var exp)
|
||||
(_, var, exp) = x
|
||||
env.find(var)[var] = eval(exp, env)
|
||||
return None
|
||||
elif x[0] is _define: # (define var exp)
|
||||
(_, var, exp) = x
|
||||
env[var] = eval(exp, env)
|
||||
return None
|
||||
elif x[0] is _lambda: # (lambda (var*) exp)
|
||||
(_, vars, exp) = x
|
||||
return Procedure(vars, exp, env)
|
||||
elif x[0] is _begin: # (begin exp+)
|
||||
for exp in x[1:-1]:
|
||||
eval(exp, env)
|
||||
x = x[-1]
|
||||
else: # (proc exp*)
|
||||
exps = [eval(exp, env) for exp in x]
|
||||
proc = exps.pop(0)
|
||||
if isa(proc, Procedure):
|
||||
x = proc.exp
|
||||
env = Env(proc.parms, exps, proc.env)
|
||||
else:
|
||||
return proc(*exps)
|
||||
|
||||
################ expand
|
||||
|
||||
def expand(x, toplevel=False):
|
||||
"Walk tree of x, making optimizations/fixes, and signaling SyntaxError."
|
||||
require(x, x!=[]) # () => Error
|
||||
if not isa(x, list): # constant => unchanged
|
||||
return x
|
||||
elif x[0] is _quote: # (quote exp)
|
||||
require(x, len(x)==2)
|
||||
return x
|
||||
elif x[0] is _if:
|
||||
if len(x)==3: x = x + [None] # (if t c) => (if t c None)
|
||||
require(x, len(x)==4)
|
||||
return list(map(expand, x))
|
||||
elif x[0] is _set:
|
||||
require(x, len(x)==3);
|
||||
var = x[1] # (set! non-var exp) => Error
|
||||
require(x, isa(var, Symbol), "can set! only a symbol")
|
||||
return [_set, var, expand(x[2])]
|
||||
elif x[0] is _define or x[0] is _definemacro:
|
||||
require(x, len(x)>=3)
|
||||
_def, v, body = x[0], x[1], x[2:]
|
||||
if isa(v, list) and v: # (define (f args) body)
|
||||
f, args = v[0], v[1:] # => (define f (lambda (args) body))
|
||||
return expand([_def, f, [_lambda, args]+body])
|
||||
else:
|
||||
require(x, len(x)==3) # (define non-var/list exp) => Error
|
||||
require(x, isa(v, Symbol), "can define only a symbol")
|
||||
exp = expand(x[2])
|
||||
if _def is _definemacro:
|
||||
require(x, toplevel, "define-macro only allowed at top level")
|
||||
proc = eval(exp)
|
||||
require(x, callable(proc), "macro must be a procedure")
|
||||
macro_table[v] = proc # (define-macro v proc)
|
||||
return None # => None; add v:proc to macro_table
|
||||
return [_define, v, exp]
|
||||
elif x[0] is _begin:
|
||||
if len(x)==1: return None # (begin) => None
|
||||
else: return [expand(xi, toplevel) for xi in x]
|
||||
elif x[0] is _lambda: # (lambda (x) e1 e2)
|
||||
require(x, len(x)>=3) # => (lambda (x) (begin e1 e2))
|
||||
vars, body = x[1], x[2:]
|
||||
require(x, (isa(vars, list) and all(isa(v, Symbol) for v in vars))
|
||||
or isa(vars, Symbol), "illegal lambda argument list")
|
||||
exp = body[0] if len(body) == 1 else [_begin] + body
|
||||
return [_lambda, vars, expand(exp)]
|
||||
elif x[0] is _quasiquote: # `x => expand_quasiquote(x)
|
||||
require(x, len(x)==2)
|
||||
return expand_quasiquote(x[1])
|
||||
elif isa(x[0], Symbol) and x[0] in macro_table:
|
||||
return expand(macro_table[x[0]](*x[1:]), toplevel) # (m arg...)
|
||||
else: # => macroexpand if m isa macro
|
||||
return list(map(expand, x)) # (f arg...) => expand each
|
||||
|
||||
def require(x, predicate, msg="wrong length"):
|
||||
"Signal a syntax error if predicate is false."
|
||||
if not predicate: raise SyntaxError(to_string(x)+': '+msg)
|
||||
|
||||
_append, _cons, _let = map(Sym, "append cons let".split())
|
||||
|
||||
def expand_quasiquote(x):
|
||||
"""Expand `x => 'x; `,x => x; `(,@x y) => (append x y) """
|
||||
if not is_pair(x):
|
||||
return [_quote, x]
|
||||
require(x, x[0] is not _unquotesplicing, "can't splice here")
|
||||
if x[0] is _unquote:
|
||||
require(x, len(x)==2)
|
||||
return x[1]
|
||||
elif is_pair(x[0]) and x[0][0] is _unquotesplicing:
|
||||
require(x[0], len(x[0])==2)
|
||||
return [_append, x[0][1], expand_quasiquote(x[1:])]
|
||||
else:
|
||||
return [_cons, expand_quasiquote(x[0]), expand_quasiquote(x[1:])]
|
||||
|
||||
def let(*args):
|
||||
args = list(args)
|
||||
x = cons(_let, args)
|
||||
require(x, len(args)>1)
|
||||
bindings, body = args[0], args[1:]
|
||||
require(x, all(isa(b, list) and len(b)==2 and isa(b[0], Symbol)
|
||||
for b in bindings), "illegal binding list")
|
||||
vars, vals = zip(*bindings)
|
||||
return [[_lambda, list(vars)]+list(map(expand, body))] + list(map(expand, vals))
|
||||
|
||||
macro_table = {_let:let} ## More macros can go here
|
||||
|
||||
eval(parse("""(begin
|
||||
|
||||
(define-macro and (lambda args
|
||||
(if (null? args) #t
|
||||
(if (= (length args) 1) (car args)
|
||||
`(if ,(car args) (and ,@(cdr args)) #f)))))
|
||||
|
||||
;; More macros can also go here
|
||||
|
||||
)"""))
|
||||
|
||||
if __name__ == '__main__':
|
||||
repl()
|
122
18-context-mngr/lispy/original/lispytest.py
Normal file
122
18-context-mngr/lispy/original/lispytest.py
Normal file
@ -0,0 +1,122 @@
|
||||
from __future__ import print_function
|
||||
|
||||
################ Tests for lis.py and lispy.py
|
||||
|
||||
lis_tests = [
|
||||
("(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]),
|
||||
]
|
||||
|
||||
lispy_tests = [
|
||||
("()", SyntaxError), ("(set! x)", SyntaxError),
|
||||
("(define 3 4)", SyntaxError),
|
||||
("(quote 1 2)", SyntaxError), ("(if 1 2 3 4)", SyntaxError),
|
||||
("(lambda 3 3)", SyntaxError), ("(lambda (x))", SyntaxError),
|
||||
("""(if (= 1 2) (define-macro a 'a)
|
||||
(define-macro a 'b))""", SyntaxError),
|
||||
("(define (twice x) (* 2 x))", None), ("(twice 2)", 4),
|
||||
("(twice 2 2)", TypeError),
|
||||
("(define lyst (lambda items items))", None),
|
||||
("(lyst 1 2 3 (+ 2 2))", [1,2,3,4]),
|
||||
("(if 1 2)", 2),
|
||||
("(if (= 3 4) 2)", None),
|
||||
("(begin (define x 1) (set! x (+ x 1)) (+ x 1))", 3),
|
||||
("(define ((account bal) amt) (set! bal (+ bal amt)) bal)", None),
|
||||
("(define a1 (account 100))", None),
|
||||
("(a1 0)", 100), ("(a1 10)", 110), ("(a1 10)", 120),
|
||||
("""(define (newton guess function derivative epsilon)
|
||||
(define guess2 (- guess (/ (function guess) (derivative guess))))
|
||||
(if (< (abs (- guess guess2)) epsilon) guess2
|
||||
(newton guess2 function derivative epsilon)))""", None),
|
||||
("""(define (square-root a)
|
||||
(newton 1 (lambda (x) (- (* x x) a)) (lambda (x) (* 2 x)) 1e-8))""", None),
|
||||
("(> (square-root 200.) 14.14213)", True),
|
||||
("(< (square-root 200.) 14.14215)", True),
|
||||
("(= (square-root 200.) (sqrt 200.))", True),
|
||||
("""(define (sum-squares-range start end)
|
||||
(define (sumsq-acc start end acc)
|
||||
(if (> start end) acc (sumsq-acc (+ start 1) end (+ (* start start) acc))))
|
||||
(sumsq-acc start end 0))""", None),
|
||||
("(sum-squares-range 1 3000)", 9004500500), ## Tests tail recursion
|
||||
("(call/cc (lambda (throw) (+ 5 (* 10 (throw 1))))) ;; throw", 1),
|
||||
("(call/cc (lambda (throw) (+ 5 (* 10 1)))) ;; do not throw", 15),
|
||||
("""(call/cc (lambda (throw)
|
||||
(+ 5 (* 10 (call/cc (lambda (escape) (* 100 (escape 3)))))))) ; 1 level""", 35),
|
||||
("""(call/cc (lambda (throw)
|
||||
(+ 5 (* 10 (call/cc (lambda (escape) (* 100 (throw 3)))))))) ; 2 levels""", 3),
|
||||
("""(call/cc (lambda (throw)
|
||||
(+ 5 (* 10 (call/cc (lambda (escape) (* 100 1))))))) ; 0 levels""", 1005),
|
||||
("(* 1i 1i)", -1), ("(sqrt -1)", 1j),
|
||||
("(let ((a 1) (b 2)) (+ a b))", 3),
|
||||
("(let ((a 1) (b 2 3)) (+ a b))", SyntaxError),
|
||||
("(and 1 2 3)", 3), ("(and (> 2 1) 2 3)", 3), ("(and)", True),
|
||||
("(and (> 2 1) (> 2 3))", False),
|
||||
("(define-macro unless (lambda args `(if (not ,(car args)) (begin ,@(cdr args))))) ; test `", None),
|
||||
("(unless (= 2 (+ 1 1)) (display 2) 3 4)", None),
|
||||
(r'(unless (= 4 (+ 1 1)) (display 2) (display "\n") 3 4)', 4),
|
||||
("(quote x)", 'x'),
|
||||
("(quote (1 2 three))", [1, 2, 'three']),
|
||||
("'x", 'x'),
|
||||
("'(one 2 3)", ['one', 2, 3]),
|
||||
("(define L (list 1 2 3))", None),
|
||||
("`(testing ,@L testing)", ['testing',1,2,3,'testing']),
|
||||
("`(testing ,L testing)", ['testing',[1,2,3],'testing']),
|
||||
("`,@L", SyntaxError),
|
||||
("""'(1 ;test comments '
|
||||
;skip this line
|
||||
2 ; more ; comments ; ) )
|
||||
3) ; final comment""", [1,2,3]),
|
||||
]
|
||||
|
||||
def test(tests, name=''):
|
||||
"For each (exp, expected) test case, see if eval(parse(exp)) == expected."
|
||||
fails = 0
|
||||
for (x, expected) in tests:
|
||||
try:
|
||||
result = eval(parse(x))
|
||||
print(x, '=>', lispstr(result))
|
||||
ok = (result == expected)
|
||||
except Exception as e:
|
||||
print(x, '=raises=>', type(e).__name__, e)
|
||||
ok = isinstance(expected, type) and issubclass(expected, Exception) and isinstance(e, expected)
|
||||
if not ok:
|
||||
fails += 1
|
||||
print('FAIL!!! Expected', expected)
|
||||
print('%s %s: %d out of %d tests fail.' % ('*'*45, name, fails, len(tests)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
from lis import *
|
||||
test(lis_tests, 'lis.py')
|
||||
from lispy import *
|
||||
test(lis_tests+lispy_tests, 'lispy.py')
|
||||
|
160
18-context-mngr/lispy/py3.10/lis.py
Normal file
160
18-context-mngr/lispy/py3.10/lis.py
Normal file
@ -0,0 +1,160 @@
|
||||
################ Lispy: Scheme Interpreter in Python 3.9
|
||||
|
||||
## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html
|
||||
## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021)
|
||||
## by Luciano Ramalho, adding type hints and pattern matching.
|
||||
|
||||
################ Imports and Types
|
||||
|
||||
import math
|
||||
import operator as op
|
||||
from collections import ChainMap
|
||||
from collections.abc import MutableMapping
|
||||
from typing import Any, TypeAlias
|
||||
|
||||
Atom: TypeAlias = float | int | str
|
||||
Expression: TypeAlias = Atom | list
|
||||
|
||||
Environment: TypeAlias = MutableMapping[str, object]
|
||||
|
||||
|
||||
class Procedure:
|
||||
"A user-defined Scheme procedure."
|
||||
|
||||
def __init__(self, parms: list[str], 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)
|
||||
return evaluate(self.body, env)
|
||||
|
||||
|
||||
################ Global Environment
|
||||
|
||||
|
||||
def standard_env() -> Environment:
|
||||
"An environment with some Scheme standard procedures."
|
||||
env: Environment = {}
|
||||
env.update(vars(math)) # sin, cos, sqrt, pi, ...
|
||||
env.update(
|
||||
{
|
||||
'+': op.add,
|
||||
'-': op.sub,
|
||||
'*': op.mul,
|
||||
'/': op.truediv,
|
||||
'>': op.gt,
|
||||
'<': op.lt,
|
||||
'>=': op.ge,
|
||||
'<=': op.le,
|
||||
'=': op.eq,
|
||||
'abs': abs,
|
||||
'append': op.add,
|
||||
'apply': lambda proc, args: proc(*args),
|
||||
'begin': lambda *x: x[-1],
|
||||
'car': lambda x: x[0],
|
||||
'cdr': lambda x: x[1:],
|
||||
'cons': lambda x, y: [x] + y,
|
||||
'eq?': op.is_,
|
||||
'equal?': op.eq,
|
||||
'length': len,
|
||||
'list': lambda *x: list(x),
|
||||
'list?': lambda x: isinstance(x, list),
|
||||
'map': lambda *args: list(map(*args)),
|
||||
'max': max,
|
||||
'min': min,
|
||||
'not': op.not_,
|
||||
'null?': lambda x: x == [],
|
||||
'number?': lambda x: isinstance(x, (int, float)),
|
||||
'procedure?': callable,
|
||||
'round': round,
|
||||
'symbol?': lambda x: isinstance(x, str),
|
||||
}
|
||||
)
|
||||
return env
|
||||
|
||||
|
||||
global_env: Environment = standard_env()
|
||||
|
||||
################ Parsing: parse, tokenize, and read_from_tokens
|
||||
|
||||
|
||||
def parse(program: str) -> Expression:
|
||||
"Read a Scheme expression from a string."
|
||||
return read_from_tokens(tokenize(program))
|
||||
|
||||
|
||||
def tokenize(s: str) -> list[str]:
|
||||
"Convert a string into a list of tokens."
|
||||
return s.replace('(', ' ( ').replace(')', ' ) ').split()
|
||||
|
||||
|
||||
def read_from_tokens(tokens: list[str]) -> Expression:
|
||||
"Read an expression from a sequence of tokens."
|
||||
if len(tokens) == 0:
|
||||
raise SyntaxError('unexpected EOF while reading')
|
||||
token = tokens.pop(0)
|
||||
if '(' == token:
|
||||
L = []
|
||||
while tokens[0] != ')':
|
||||
L.append(read_from_tokens(tokens))
|
||||
tokens.pop(0) # pop off ')'
|
||||
return L
|
||||
elif ')' == token:
|
||||
raise SyntaxError('unexpected )')
|
||||
else:
|
||||
return parse_atom(token)
|
||||
|
||||
|
||||
def parse_atom(token: str) -> Atom:
|
||||
"Numbers become numbers; every other token is a symbol."
|
||||
try:
|
||||
return int(token)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(token)
|
||||
except ValueError:
|
||||
return str(token)
|
||||
|
||||
|
||||
################ Interaction: A REPL
|
||||
|
||||
|
||||
def repl(prompt: str = 'lis.py> ') -> None:
|
||||
"A prompt-read-evaluate-print loop."
|
||||
while True:
|
||||
val = evaluate(parse(input(prompt)))
|
||||
if val is not None:
|
||||
print(lispstr(val))
|
||||
|
||||
|
||||
def lispstr(exp: object) -> str:
|
||||
"Convert a Python object back into a Lisp-readable string."
|
||||
if isinstance(exp, list):
|
||||
return '(' + ' '.join(map(lispstr, exp)) + ')'
|
||||
else:
|
||||
return str(exp)
|
||||
|
||||
|
||||
################ eval
|
||||
|
||||
|
||||
def evaluate(x: Expression, env: Environment = global_env) -> Any:
|
||||
"Evaluate an expression in an environment."
|
||||
match x:
|
||||
case str(): # variable reference
|
||||
return env[x]
|
||||
case literal if not isinstance(x, list): # constant literal
|
||||
return literal
|
||||
case ['quote', exp]: # (quote exp)
|
||||
return exp
|
||||
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)
|
||||
env[var] = evaluate(exp, env)
|
||||
case ['lambda', parms, body]: # (lambda (var...) body)
|
||||
return Procedure(parms, body, env)
|
||||
case [op, *args]: # (proc arg...)
|
||||
proc = evaluate(op, env)
|
||||
values = (evaluate(arg, env) for arg in args)
|
||||
return proc(*values)
|
147
18-context-mngr/lispy/py3.10/lis_test.py
Executable file
147
18-context-mngr/lispy/py3.10/lis_test.py
Executable file
@ -0,0 +1,147 @@
|
||||
from typing import Optional
|
||||
|
||||
from pytest import mark
|
||||
|
||||
from lis import parse, evaluate, Expression, Environment, 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]),
|
||||
])
|
||||
@mark.skip
|
||||
def test_evaluate(source: str, expected: Optional[Expression]) -> None:
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
# tests for each of the cases in evaluate
|
||||
|
||||
def test_evaluate_variable() -> None:
|
||||
env: Environment = dict(x=10)
|
||||
source = 'x'
|
||||
expected = 10
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_literal() -> None:
|
||||
source = '3.3'
|
||||
expected = 3.3
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_quote() -> None:
|
||||
source = '(quote (1.1 is not 1))'
|
||||
expected = [1.1, 'is', 'not', 1]
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_if_true() -> None:
|
||||
source = '(if 1 10 no-such-thing)'
|
||||
expected = 10
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_if_false() -> None:
|
||||
source = '(if 0 no-such-thing 20)'
|
||||
expected = 20
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_define() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(define answer (* 6 7))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got is None
|
||||
assert env['answer'] == 42
|
||||
|
||||
|
||||
def test_lambda() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(lambda (a b) (if (>= a b) a b))'
|
||||
func = evaluate(parse(source), env)
|
||||
assert func.parms == ['a', 'b']
|
||||
assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b']
|
||||
assert func.env is env
|
||||
assert func(1, 2) == 2
|
||||
assert func(3, 2) == 3
|
||||
|
||||
|
||||
def test_begin() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = """
|
||||
(begin
|
||||
(define x (* 2 3))
|
||||
(* x 7)
|
||||
)
|
||||
"""
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == 42
|
||||
|
||||
|
||||
def test_invocation_builtin_car() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(car (quote (11 22 33)))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == 11
|
||||
|
||||
|
||||
def test_invocation_builtin_append() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(append (quote (a b)) (quote (c d)))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == ['a', 'b', 'c', 'd']
|
||||
|
||||
|
||||
def test_invocation_builtin_map() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == [2, 4, 6]
|
||||
|
||||
|
||||
def test_invocation_user_procedure() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = """
|
||||
(begin
|
||||
(define max (lambda (a b) (if (>= a b) a b)))
|
||||
(max 22 11)
|
||||
)
|
||||
"""
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == 22
|
163
18-context-mngr/lispy/py3.9/lis.py
Normal file
163
18-context-mngr/lispy/py3.9/lis.py
Normal file
@ -0,0 +1,163 @@
|
||||
################ Lispy: Scheme Interpreter in Python 3.9
|
||||
|
||||
## (c) Peter Norvig, 2010-18; See http://norvig.com/lispy.html
|
||||
## Minor edits for Fluent Python, Second Edition (O'Reilly, 2021)
|
||||
## by Luciano Ramalho, mostly adding type hints.
|
||||
|
||||
################ Imports and Types
|
||||
|
||||
import math
|
||||
import operator as op
|
||||
from collections import ChainMap
|
||||
from collections.abc import MutableMapping
|
||||
from typing import Union, Any
|
||||
|
||||
Atom = Union[float, int, str]
|
||||
Expression = Union[Atom, list]
|
||||
|
||||
Environment = MutableMapping[str, object]
|
||||
|
||||
|
||||
class Procedure:
|
||||
"A user-defined Scheme procedure."
|
||||
|
||||
def __init__(self, parms: list[str], 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)
|
||||
return evaluate(self.body, env)
|
||||
|
||||
|
||||
################ Global Environment
|
||||
|
||||
|
||||
def standard_env() -> Environment:
|
||||
"An environment with some Scheme standard procedures."
|
||||
env: Environment = {}
|
||||
env.update(vars(math)) # sin, cos, sqrt, pi, ...
|
||||
env.update(
|
||||
{
|
||||
'+': op.add,
|
||||
'-': op.sub,
|
||||
'*': op.mul,
|
||||
'/': op.truediv,
|
||||
'>': op.gt,
|
||||
'<': op.lt,
|
||||
'>=': op.ge,
|
||||
'<=': op.le,
|
||||
'=': op.eq,
|
||||
'abs': abs,
|
||||
'append': op.add,
|
||||
'apply': lambda proc, args: proc(*args),
|
||||
'begin': lambda *x: x[-1],
|
||||
'car': lambda x: x[0],
|
||||
'cdr': lambda x: x[1:],
|
||||
'cons': lambda x, y: [x] + y,
|
||||
'eq?': op.is_,
|
||||
'equal?': op.eq,
|
||||
'length': len,
|
||||
'list': lambda *x: list(x),
|
||||
'list?': lambda x: isinstance(x, list),
|
||||
'map': lambda *args: list(map(*args)),
|
||||
'max': max,
|
||||
'min': min,
|
||||
'not': op.not_,
|
||||
'null?': lambda x: x == [],
|
||||
'number?': lambda x: isinstance(x, (int, float)),
|
||||
'procedure?': callable,
|
||||
'round': round,
|
||||
'symbol?': lambda x: isinstance(x, str),
|
||||
}
|
||||
)
|
||||
return env
|
||||
|
||||
|
||||
global_env: Environment = standard_env()
|
||||
|
||||
################ Parsing: parse, tokenize, and read_from_tokens
|
||||
|
||||
|
||||
def parse(program: str) -> Expression:
|
||||
"Read a Scheme expression from a string."
|
||||
return read_from_tokens(tokenize(program))
|
||||
|
||||
|
||||
def tokenize(s: str) -> list[str]:
|
||||
"Convert a string into a list of tokens."
|
||||
return s.replace('(', ' ( ').replace(')', ' ) ').split()
|
||||
|
||||
|
||||
def read_from_tokens(tokens: list[str]) -> Expression:
|
||||
"Read an expression from a sequence of tokens."
|
||||
if len(tokens) == 0:
|
||||
raise SyntaxError('unexpected EOF while reading')
|
||||
token = tokens.pop(0)
|
||||
if '(' == token:
|
||||
L = []
|
||||
while tokens[0] != ')':
|
||||
L.append(read_from_tokens(tokens))
|
||||
tokens.pop(0) # pop off ')'
|
||||
return L
|
||||
elif ')' == token:
|
||||
raise SyntaxError('unexpected )')
|
||||
else:
|
||||
return parse_atom(token)
|
||||
|
||||
|
||||
def parse_atom(token: str) -> Atom:
|
||||
"Numbers become numbers; every other token is a symbol."
|
||||
try:
|
||||
return int(token)
|
||||
except ValueError:
|
||||
try:
|
||||
return float(token)
|
||||
except ValueError:
|
||||
return str(token)
|
||||
|
||||
|
||||
################ Interaction: A REPL
|
||||
|
||||
|
||||
def repl(prompt: str = 'lis.py> ') -> None:
|
||||
"A prompt-read-evaluate-print loop."
|
||||
while True:
|
||||
val = evaluate(parse(input(prompt)))
|
||||
if val is not None:
|
||||
print(lispstr(val))
|
||||
|
||||
|
||||
def lispstr(exp: object) -> str:
|
||||
"Convert a Python object back into a Lisp-readable string."
|
||||
if isinstance(exp, list):
|
||||
return '(' + ' '.join(map(lispstr, exp)) + ')'
|
||||
else:
|
||||
return str(exp)
|
||||
|
||||
|
||||
################ eval
|
||||
|
||||
|
||||
def evaluate(x: Expression, env: Environment = global_env) -> Any:
|
||||
"Evaluate an expression in an environment."
|
||||
if isinstance(x, str): # variable reference
|
||||
return env[x]
|
||||
elif not isinstance(x, list): # constant literal
|
||||
return x
|
||||
elif x[0] == 'quote': # (quote exp)
|
||||
(_, exp) = x
|
||||
return exp
|
||||
elif x[0] == 'if': # (if test conseq alt)
|
||||
(_, test, conseq, alt) = x
|
||||
exp = conseq if evaluate(test, env) else alt
|
||||
return evaluate(exp, env)
|
||||
elif x[0] == 'define': # (define var exp)
|
||||
(_, var, exp) = x
|
||||
env[var] = evaluate(exp, env)
|
||||
elif x[0] == 'lambda': # (lambda (var...) body)
|
||||
(_, parms, body) = x
|
||||
return Procedure(parms, body, env)
|
||||
else: # (proc arg...)
|
||||
proc = evaluate(x[0], env)
|
||||
args = [evaluate(exp, env) for exp in x[1:]]
|
||||
return proc(*args)
|
147
18-context-mngr/lispy/py3.9/lis_test.py
Executable file
147
18-context-mngr/lispy/py3.9/lis_test.py
Executable file
@ -0,0 +1,147 @@
|
||||
from typing import Optional
|
||||
|
||||
from pytest import mark
|
||||
|
||||
from lis import parse, evaluate, Expression, Environment, 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]),
|
||||
])
|
||||
@mark.skip
|
||||
def test_evaluate(source: str, expected: Optional[Expression]) -> None:
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
# tests for each of the cases in evaluate
|
||||
|
||||
def test_evaluate_variable() -> None:
|
||||
env: Environment = dict(x=10)
|
||||
source = 'x'
|
||||
expected = 10
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_literal() -> None:
|
||||
source = '3.3'
|
||||
expected = 3.3
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_quote() -> None:
|
||||
source = '(quote (1.1 is not 1))'
|
||||
expected = [1.1, 'is', 'not', 1]
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_if_true() -> None:
|
||||
source = '(if 1 10 no-such-thing)'
|
||||
expected = 10
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_evaluate_if_false() -> None:
|
||||
source = '(if 0 no-such-thing 20)'
|
||||
expected = 20
|
||||
got = evaluate(parse(source))
|
||||
assert got == expected
|
||||
|
||||
|
||||
def test_define() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(define answer (* 6 7))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got is None
|
||||
assert env['answer'] == 42
|
||||
|
||||
|
||||
def test_lambda() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(lambda (a b) (if (>= a b) a b))'
|
||||
func = evaluate(parse(source), env)
|
||||
assert func.parms == ['a', 'b']
|
||||
assert func.body == ['if', ['>=', 'a', 'b'], 'a', 'b']
|
||||
assert func.env is env
|
||||
assert func(1, 2) == 2
|
||||
assert func(3, 2) == 3
|
||||
|
||||
|
||||
def test_begin() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = """
|
||||
(begin
|
||||
(define x (* 2 3))
|
||||
(* x 7)
|
||||
)
|
||||
"""
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == 42
|
||||
|
||||
|
||||
def test_invocation_builtin_car() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(car (quote (11 22 33)))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == 11
|
||||
|
||||
|
||||
def test_invocation_builtin_append() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(append (quote (a b)) (quote (c d)))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == ['a', 'b', 'c', 'd']
|
||||
|
||||
|
||||
def test_invocation_builtin_map() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = '(map (lambda (x) (* x 2)) (quote (1 2 3))))'
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == [2, 4, 6]
|
||||
|
||||
|
||||
def test_invocation_user_procedure() -> None:
|
||||
env: Environment = standard_env()
|
||||
source = """
|
||||
(begin
|
||||
(define max (lambda (a b) (if (>= a b) a b)))
|
||||
(max 22 11)
|
||||
)
|
||||
"""
|
||||
got = evaluate(parse(source), env)
|
||||
assert got == 22
|
@ -1,9 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# tag::TCP_MOJIFINDER_TOP[]
|
||||
import sys
|
||||
import asyncio
|
||||
import functools
|
||||
import sys
|
||||
from asyncio.trsock import TransportSocket
|
||||
from typing import cast
|
||||
|
||||
from charindex import InvertedIndex, format_results # <1>
|
||||
|
||||
@ -54,17 +56,19 @@ async def supervisor(index: InvertedIndex, host: str, port: int):
|
||||
server = await asyncio.start_server( # <1>
|
||||
functools.partial(finder, index), # <2>
|
||||
host, port) # <3>
|
||||
addr = server.sockets[0].getsockname() # type: ignore # <4>
|
||||
print(f'Serving on {addr}. Hit CTRL-C to stop.')
|
||||
await server.serve_forever() # <5>
|
||||
|
||||
socket_list = cast(tuple[TransportSocket, ...], server.sockets) # <4>
|
||||
addr = socket_list[0].getsockname()
|
||||
print(f'Serving on {addr}. Hit CTRL-C to stop.') # <5>
|
||||
await server.serve_forever() # <6>
|
||||
|
||||
def main(host: str = '127.0.0.1', port_arg: str = '2323'):
|
||||
port = int(port_arg)
|
||||
print('Building index.')
|
||||
index = InvertedIndex() # <6>
|
||||
index = InvertedIndex() # <7>
|
||||
try:
|
||||
asyncio.run(supervisor(index, host, port)) # <7>
|
||||
except KeyboardInterrupt: # <8>
|
||||
asyncio.run(supervisor(index, host, port)) # <8>
|
||||
except KeyboardInterrupt: # <9>
|
||||
print('\nServer shut down.')
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -2191,7 +2191,7 @@
|
||||
"time_start": "2014-07-23 11:30:00",
|
||||
"time_stop": "2014-07-23 12:10:00",
|
||||
"venue_serial": 1450,
|
||||
"description": "This talk will discuss why LineRate, a high-performance Layer 7 app proxy for developers, chose to embed Node.js as the programming language for the data path. The talk will focus on the challenges of building an embeddable\r\nNode.js and conclude with how the open source Node.js code base could evolve to better support embeddable use cases.",
|
||||
"description": "This talk will discuss why LineRate, a high-performance Layer 7 app proxy for developers, chose to embed Node.js as the programming language for the data path. The talk will focus on the challenges of building an embeddable\r\nNode.js and conclude with how the open source Node.js codebase could evolve to better support embeddable use cases.",
|
||||
"website_url": "http://oscon.com/oscon2014/public/schedule/detail/34327",
|
||||
"speakers": [173056],
|
||||
"categories": [
|
||||
@ -11594,7 +11594,7 @@
|
||||
"position": "Freelance Perl expert and Agile consultant",
|
||||
"affiliation": "All Around The World",
|
||||
"twitter": "OvidPerl",
|
||||
"bio": "<p>I’m a well-known Perl expert, better known online as Ovid. I specialize in large-scale, database driven code bases and wrote the test harness that currently ships with the Perl programming language. I’m constantly trying to create better testing tools for the Perl community.</p>\n<p>I sit on the Board of Directors of the Perl Foundation and run a consulting company with my lovely wife, Le\u00efla, from our offices in La Rochelle, a medieval port town on the west coast of France.</p>\n<p>I speak at conferences all over the world and also do private speaking engagements and training for companies. Currently I’m a specialist for hire, often focusing on complex <span class=\"caps\">ETL</span> problems or making developers more productive by fixing test suites and making them run much faster.</p>"
|
||||
"bio": "<p>I’m a well-known Perl expert, better known online as Ovid. I specialize in large-scale, database driven codebases and wrote the test harness that currently ships with the Perl programming language. I’m constantly trying to create better testing tools for the Perl community.</p>\n<p>I sit on the Board of Directors of the Perl Foundation and run a consulting company with my lovely wife, Le\u00efla, from our offices in La Rochelle, a medieval port town on the west coast of France.</p>\n<p>I speak at conferences all over the world and also do private speaking engagements and training for companies. Currently I’m a specialist for hire, often focusing on complex <span class=\"caps\">ETL</span> problems or making developers more productive by fixing test suites and making them run much faster.</p>"
|
||||
},
|
||||
|
||||
{
|
||||
@ -11666,7 +11666,7 @@
|
||||
"position": "Computer Science Professional",
|
||||
"affiliation": "KEYW Corporation",
|
||||
"twitter": "",
|
||||
"bio": "<p>David Quigley making a return appearance to <span class=\"caps\">OSCON</span> after his \u201cDemystifying SELinux: <span class=\"caps\">WTF</span> is it saying?\u201d talk started his career as a Computer Systems Researcher for the National Information Assurance Research Lab at the <span class=\"caps\">NSA</span> where he worked as a member of the SELinux team. David leads the design and implementation efforts to provide Labeled-<span class=\"caps\">NFS</span> support for SELinux. David has previously contributed to the open source community through maintaining the Unionfs 1.0 code base and through code contributions to various other projects. David has presented at conferences such as the Ottawa Linux Symposium, the StorageSS workshop, LinuxCon and several local Linux User Group meetings where presentation topics have included storage, file systems, and security. David currently works as a Computer Science Professional for the Operations, Analytics and Software Development (<span class=\"caps\">OASD</span>) Division at Keyw Corporation.</p>"
|
||||
"bio": "<p>David Quigley making a return appearance to <span class=\"caps\">OSCON</span> after his \u201cDemystifying SELinux: <span class=\"caps\">WTF</span> is it saying?\u201d talk started his career as a Computer Systems Researcher for the National Information Assurance Research Lab at the <span class=\"caps\">NSA</span> where he worked as a member of the SELinux team. David leads the design and implementation efforts to provide Labeled-<span class=\"caps\">NFS</span> support for SELinux. David has previously contributed to the open source community through maintaining the Unionfs 1.0 codebase and through code contributions to various other projects. David has presented at conferences such as the Ottawa Linux Symposium, the StorageSS workshop, LinuxCon and several local Linux User Group meetings where presentation topics have included storage, file systems, and security. David currently works as a Computer Science Professional for the Operations, Analytics and Software Development (<span class=\"caps\">OASD</span>) Division at Keyw Corporation.</p>"
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -96,7 +96,7 @@ def checked(cls: type) -> type: # <1>
|
||||
for name, constructor in _fields(cls).items(): # <2>
|
||||
setattr(cls, name, Field(name, constructor)) # <3>
|
||||
|
||||
cls._fields = classmethod(_fields) #type: ignore # <4>
|
||||
cls._fields = classmethod(_fields) # type: ignore # <4>
|
||||
|
||||
instance_methods = ( # <5>
|
||||
__init__,
|
||||
|
Loading…
Reference in New Issue
Block a user