Skip to content

Commit 0af2541

Browse files
committed
port the lispython dialect example from pydialect
1 parent 19f5529 commit 0af2541

File tree

3 files changed

+181
-0
lines changed

3 files changed

+181
-0
lines changed

unpythonic/dialects/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# -*- coding: utf-8 -*-
2+
3+
# re-exports
4+
from .lispython import Lispython # noqa: F401

unpythonic/dialects/lispython.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# -*- coding: utf-8 -*-
2+
"""Lispython: the love child of Python and Scheme.
3+
4+
Powered by `mcpyrate` and `unpythonic`.
5+
"""
6+
7+
__all__ = ["Lispython"]
8+
9+
__version__ = '2.0.0'
10+
11+
from mcpyrate.quotes import macros, q # noqa: F401
12+
13+
from mcpyrate.dialects import Dialect
14+
from mcpyrate.splicing import splice_dialect
15+
16+
class Lispython(Dialect):
17+
"""**Schemers rejoice!**
18+
19+
Multiple musings mix in a lambda,
20+
Lament no longer the lack of let.
21+
Languish no longer labelless, lambda,
22+
Linked lists cons and fold.
23+
Tail-call into recursion divine,
24+
The final value always provide.
25+
"""
26+
27+
def transform_ast(self, tree): # tree is an ast.Module
28+
with q as template:
29+
from unpythonic.syntax import (macros, tco, autoreturn, # noqa: F401, F811
30+
multilambda, quicklambda, namedlambda, f,
31+
let, letseq, letrec,
32+
dlet, dletseq, dletrec,
33+
blet, bletseq, bletrec,
34+
local, delete, do, do0,
35+
let_syntax, abbrev,
36+
cond)
37+
# auxiliary syntax elements for the macros
38+
from unpythonic.syntax import where, block, expr # noqa: F401, F811
39+
from unpythonic import cons, car, cdr, ll, llist, nil, prod, dyn # noqa: F401, F811
40+
with namedlambda, autoreturn, quicklambda, multilambda, tco:
41+
__paste_here__ # noqa: F821, just a splicing marker.
42+
tree.body = splice_dialect(tree.body, template, "__paste_here__")
43+
return tree
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# -*- coding: utf-8 -*-
2+
"""Test the Lispython dialect."""
3+
4+
# See the `mcpyrate` dialects user manual:
5+
# https://github.com/Technologicat/mcpyrate/blob/master/doc/dialects.md
6+
7+
from ...dialects import dialects, Lispython # noqa: F401
8+
9+
# Can use macros, too.
10+
from ...syntax import macros, continuations, call_cc # noqa: F401
11+
12+
# `unpythonic` is effectively `lispython`'s stdlib; not everything gets imported by default.
13+
from ...fold import foldl
14+
15+
# Of course, all of Python's stdlib is available too.
16+
#
17+
# So is **any** Python library; the ability to use arbitrary Python libraries in
18+
# a customized Python-based language is pretty much the whole point of dialects.
19+
#
20+
from operator import mul
21+
22+
# TODO: use the test framework
23+
24+
def main():
25+
assert prod((2, 3, 4)) == 24 # noqa: F821, bye missing battery, hello new dialect builtin
26+
assert foldl(mul, 1, (2, 3, 4)) == 24
27+
28+
# cons, car, cdr, ll, llist are builtins (for more linked list utils, import them from unpythonic)
29+
c = cons(1, 2) # noqa: F821
30+
assert tuple(c) == (1, 2)
31+
assert car(c) == 1 # noqa: F821
32+
assert cdr(c) == 2 # noqa: F821
33+
assert ll(1, 2, 3) == llist((1, 2, 3)) # noqa: F821
34+
35+
# all unpythonic.syntax let[], letseq[], letrec[] constructs are builtins
36+
# (including the decorator versions, let_syntax and abbrev)
37+
x = let[(a, 21) in 2 * a] # noqa: F821
38+
assert x == 42
39+
40+
x = letseq[((a, 1), # noqa: F821
41+
(a, 2 * a), # noqa: F821
42+
(a, 2 * a)) in # noqa: F821
43+
a] # noqa: F821
44+
assert x == 4
45+
46+
# rackety cond
47+
a = lambda x: cond[x < 0, "nope", # noqa: F821
48+
x % 2 == 0, "even",
49+
"odd"]
50+
assert a(-1) == "nope"
51+
assert a(2) == "even"
52+
assert a(3) == "odd"
53+
54+
# auto-TCO (both in defs and lambdas), implicit return in tail position
55+
def fact(n):
56+
def f(k, acc):
57+
if k == 1:
58+
return acc # "return" still available for early return
59+
f(k - 1, k * acc)
60+
f(n, acc=1)
61+
assert fact(4) == 24
62+
fact(5000) # no crash (and correct result, since Python uses bignums transparently)
63+
64+
t = letrec[((evenp, lambda x: (x == 0) or oddp(x - 1)), # noqa: F821
65+
(oddp, lambda x:(x != 0) and evenp(x - 1))) in # noqa: F821
66+
evenp(10000)] # no crash # noqa: F821
67+
assert t is True
68+
69+
# lambdas are named automatically
70+
square = lambda x: x**2
71+
assert square(3) == 9
72+
assert square.__name__ == "square"
73+
74+
# the underscore (NOTE: due to this, "f" is a reserved name in lispython)
75+
cube = f[_**3] # noqa: F821
76+
assert cube(3) == 27
77+
assert cube.__name__ == "cube"
78+
79+
# lambdas can have multiple expressions and local variables
80+
#
81+
# If you need to return a literal list from a lambda, use an extra set of
82+
# brackets; the outermost brackets always enable multiple-expression mode.
83+
#
84+
test = lambda x: [local[y << 2 * x], # noqa: F821, local[name << value] makes a local variable
85+
y + 1] # noqa: F821
86+
assert test(10) == 21
87+
88+
a = lambda x: [local[t << x % 2], # noqa: F821
89+
cond[t == 0, "even", # noqa: F821
90+
t == 1, "odd",
91+
None]] # cond[] requires an else branch
92+
assert a(2) == "even"
93+
assert a(3) == "odd"
94+
95+
# actually the multiple-expression environment is an unpythonic.syntax.do[],
96+
# which can be used in any expression position.
97+
x = do[local[z << 2], # noqa: F821
98+
3 * z] # noqa: F821
99+
assert x == 6
100+
101+
# do0[] is the same, but returns the value of the first expression instead of the last one.
102+
x = do0[local[z << 3], # noqa: F821
103+
print("hi from do0, z is {}".format(z))] # noqa: F821
104+
assert x == 3
105+
106+
# MacroPy #21; namedlambda must be in its own with block in the
107+
# dialect implementation or this particular combination will fail
108+
# (uncaught jump, __name__ not set).
109+
t = letrec[((evenp, lambda x: (x == 0) or oddp(x - 1)), # noqa: F821
110+
(oddp, lambda x:(x != 0) and evenp(x - 1))) in # noqa: F821
111+
[local[x << evenp(100)], # noqa: F821, multi-expression let body is a do[] environment
112+
(x, evenp.__name__, oddp.__name__)]] # noqa: F821
113+
assert t == (True, "evenp", "oddp")
114+
115+
with continuations: # should be skipped by the implicit tco inserted by the dialect
116+
k = None # kontinuation
117+
def setk(*args, cc):
118+
nonlocal k
119+
k = cc # current continuation, i.e. where to go after setk() finishes
120+
args # tuple means multiple-return-values
121+
def doit():
122+
lst = ['the call returned']
123+
*more, = call_cc[setk('A')]
124+
lst + list(more)
125+
assert doit() == ['the call returned', 'A']
126+
# We can now send stuff into k, as long as it conforms to the
127+
# signature of the assignment targets of the "call_cc".
128+
assert k('again') == ['the call returned', 'again']
129+
assert k('thrice', '!') == ['the call returned', 'thrice', '!']
130+
131+
print("All tests PASSED")
132+
133+
if __name__ == '__main__':
134+
main()

0 commit comments

Comments
 (0)