Skip to content

Commit 8f658b0

Browse files
committed
blet, bletrec, bletseq macros
1 parent 8cea037 commit 8f658b0

File tree

4 files changed

+95
-16
lines changed

4 files changed

+95
-16
lines changed

macro_extras/main.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
simple_let, simple_letseq, \
1414
let, letseq, letrec, \
1515
dlet, dletseq, dletrec, \
16+
blet, bletseq, bletrec, \
1617
do, do0, \
1718
forall, insist, deny, forall_simple, \
1819
aif, it, \
@@ -232,7 +233,7 @@ def add3(a, b, c):
232233
# decorator version: let over def
233234
@dlet((x, 0))
234235
def count():
235-
x << x + 1
236+
x << x + 1 # assigment to let environment uses the "assignment expr" syntax
236237
return x
237238
assert count() == 1
238239
assert count() == 2
@@ -249,9 +250,28 @@ def f(x):
249250
@dletseq((x, 1),
250251
(x, x+1),
251252
(x, x+2))
252-
def g(*args, **kwargs):
253+
def g(a):
254+
return a + x
255+
assert g(10) == 14
256+
257+
# block versions (def takes no args, runs immediately, replaced with return value)
258+
@blet((x, 21))
259+
def result():
260+
return 2*x
261+
assert result == 42
262+
263+
@bletseq((x, 1),
264+
(x, x+1),
265+
(x, x+2))
266+
def result():
253267
return x
254-
assert g() == 4
268+
assert result == 4
269+
270+
@bletrec((evenp, lambda x: (x == 0) or oddp(x - 1)),
271+
(oddp, lambda x: (x != 0) and evenp(x - 1)))
272+
def result():
273+
return evenp(42)
274+
assert result is True
255275

256276
# TCO combo
257277
with tco:

unpythonic/let.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,8 +283,8 @@ def decorated(*args, **kwargs):
283283
return decorated
284284
return deco
285285

286-
def _blet(mode, **bindings):
287-
dlet_deco = _dlet(mode, **bindings)
286+
def _blet(mode, _envname="env", **bindings):
287+
dlet_deco = _dlet(mode, _envname, **bindings)
288288
def deco(body):
289289
return call(dlet_deco(body))
290290
return deco

unpythonic/lispylet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ def decorated(*args, **kwargs):
262262
return decorated
263263
return deco
264264

265-
def _blet(bindings, mode="let"):
266-
dlet_deco = _dlet(bindings, mode)
265+
def _blet(bindings, mode="let", _envname="env"):
266+
dlet_deco = _dlet(bindings, mode, _envname)
267267
def deco(body):
268268
return call(dlet_deco(body))
269269
return deco

unpythonic/syntax.py

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
from unpythonic.it import flatmap, uniqify, rev
3434
from unpythonic.fun import curry as curryf, _currycall as currycall, identity
3535
from unpythonic.dynscope import dyn
36-
from unpythonic.lispylet import letrec as letrecf, let as letf, _dlet as dletf
36+
from unpythonic.lispylet import letrec as letrecf, let as letf, \
37+
_dlet as dletf, _blet as bletf
3738
from unpythonic.seq import do as dof, begin as beginf
3839
from unpythonic.fup import fupdate
3940
from unpythonic.misc import namelambda
@@ -584,7 +585,7 @@ def count():
584585
**CAUTION**: assignment to the let environment is ``name << value``;
585586
the regular syntax ``name = value`` creates a local variable.
586587
"""
587-
return _dletimpl(tree, args, "let", gen_sym)
588+
return _dletimpl(tree, args, "let", "decorate", gen_sym)
588589

589590
@macros.decorator
590591
def dletrec(tree, args, *, gen_sym, **kw):
@@ -601,11 +602,38 @@ def f(x):
601602
602603
Same cautions apply as to ``dlet``.
603604
"""
604-
return _dletimpl(tree, args, "letrec", gen_sym)
605+
return _dletimpl(tree, args, "letrec", "decorate", gen_sym)
606+
607+
@macros.decorator
608+
def blet(tree, args, *, gen_sym, **kw):
609+
"""[syntax, decorator] def --> let block.
610+
611+
Example::
612+
613+
@blet((x, 21))
614+
def result():
615+
return 2*x
616+
assert result == 42
617+
"""
618+
return _dletimpl(tree, args, "let", "call", gen_sym)
619+
@macros.decorator
620+
def bletrec(tree, args, *, gen_sym, **kw):
621+
"""[syntax, decorator] def --> letrec block.
622+
623+
Example::
624+
625+
@bletrec((evenp, lambda x: (x == 0) or oddp(x - 1)),
626+
(oddp, lambda x: (x != 0) and evenp(x - 1)))
627+
def result():
628+
return evenp(42)
629+
assert result is True
630+
"""
631+
return _dletimpl(tree, args, "letrec", "call", gen_sym)
605632

606633
# Very similar to _letimpl, but perhaps more readable to keep these separate.
607-
def _dletimpl(tree, args, mode, gen_sym):
634+
def _dletimpl(tree, args, mode, kind, gen_sym):
608635
assert mode in ("let", "letrec")
636+
assert kind in ("decorate", "call")
609637

610638
if not args:
611639
return tree
@@ -621,8 +649,9 @@ def _dletimpl(tree, args, mode, gen_sym):
621649
values = [t1(b) for b in values]
622650
tree = t2(tree)
623651

652+
letter = dletf if kind == "decorate" else bletf
624653
binding_pairs = [q[(u[k], ast_literal[v])] for k, v in zip(names, values)]
625-
tree.decorator_list = tree.decorator_list + [hq[dletf((ast_literal[binding_pairs],), mode=u[mode], _envname=u[e])]]
654+
tree.decorator_list = tree.decorator_list + [hq[letter((ast_literal[binding_pairs],), mode=u[mode], _envname=u[e])]]
626655
tree.args.kwonlyargs = tree.args.kwonlyargs + [arg(arg=e)]
627656
tree.args.kw_defaults = tree.args.kw_defaults + [None]
628657
return tree
@@ -632,7 +661,34 @@ def dletseq(tree, args, gen_sym, **kw):
632661
"""[syntax, decorator] Decorator version of letseq, for 'letseq over def'.
633662
634663
Expands to nested function definitions, each with one ``dlet`` decorator.
664+
665+
Example::
666+
667+
@dletseq((x, 1),
668+
(x, x+1),
669+
(x, x+2))
670+
def g(a):
671+
return a + x
672+
assert g(10) == 14
635673
"""
674+
return _dletseqimpl(tree, args, "decorate", gen_sym)
675+
676+
@macros.decorator
677+
def bletseq(tree, args, gen_sym, **kw):
678+
"""[syntax, decorator] def --> letseq block.
679+
680+
Example::
681+
682+
@bletseq((x, 1),
683+
(x, x+1),
684+
(x, x+2))
685+
def result():
686+
return x
687+
assert result == 4
688+
"""
689+
return _dletseqimpl(tree, args, "call", gen_sym)
690+
691+
def _dletseqimpl(tree, args, kind, gen_sym):
636692
# What we want:
637693
#
638694
# @dletseq((x, 1),
@@ -655,19 +711,21 @@ def dletseq(tree, args, gen_sym, **kw):
655711
# return g2()
656712
# assert g() == 4
657713
#
714+
assert kind in ("decorate", "call")
658715
if not args:
659716
return tree
660717

661718
userargs = tree.args # original arguments to the def
662719
fname = tree.name
663720
noargs = arguments(args=[], kwonlyargs=[], vararg=None, kwarg=None,
664721
defaults=[], kw_defaults=[])
665-
iname = gen_sym("inner")
722+
iname = gen_sym("{}_inner".format(fname))
666723
tree.args = noargs
667724
tree.name = iname
668725

669726
*rest, last = args
670-
innerdef = dlet.transform(tree, last)
727+
dletter = dlet if kind == "decorate" else blet
728+
innerdef = dletter.transform(tree, last)
671729

672730
# optimization: in the final step, no need to generate a wrapper function
673731
if not rest:
@@ -679,12 +737,13 @@ def dletseq(tree, args, gen_sym, **kw):
679737
innerdef.args.kw_defaults += tmpargs.kw_defaults
680738
return innerdef
681739

740+
ret = Return(value=q[name[iname]()]) if kind == "decorate" else Return(value=q[name[iname]])
682741
outer = FunctionDef(name=fname, args=userargs,
683-
body=[innerdef, Return(value=q[name[iname]()])],
742+
body=[innerdef, ret],
684743
decorator_list=[],
685744
returns=None) # no return type annotation
686745
outer = copy_location(outer, tree)
687-
return dletseq.transform(outer, *rest)
746+
return _dletseqimpl(outer, rest, kind, gen_sym)
688747

689748
# -----------------------------------------------------------------------------
690749

0 commit comments

Comments
 (0)