Skip to content

Commit 34f0a56

Browse files
committed
pondering about nested autorefs
1 parent b478a00 commit 34f0a56

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

unpythonic/syntax/autoref.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
from macropy.core.walkers import Walker
88

99
from ..dynassign import dyn
10+
from ..lazyutil import force1
1011

1112
# TODO: suppport Attribute, Subscript in autoref
1213
# TODO: support nested autorefs
13-
# Consider:
14+
# We need something like::
15+
#
1416
# with autoref(o):
1517
# x # --> (o.x if hasattr(o, "x") else x)
1618
# x.a # --> (o.x.a if hasattr(o, "x") else x.a)
@@ -23,6 +25,45 @@
2325
# o # --> (p.o if hasattr(p, "o") else o)
2426
# o.x # --> (p.o.x if hasattr(p, "o") else o.x)
2527
# o[s] # --> (p.o[s] if hasattr(p, "o") else o[s])
28+
#
29+
# One possible clean-ish implementation is::
30+
#
31+
# with autoref(o):
32+
# x # --> (lambda _ar271: _ar271[1] if _ar271[0] else x)(_autoref_resolve((o, "x")))
33+
# x.a # --> ((lambda _ar271: _ar271[1] if _ar271[0] else x)(_autoref_resolve((o, "x")))).a
34+
# x[s] # --> ((lambda _ar271: _ar271[1] if _ar271[0] else x)(_autoref_resolve((o, "x"))))[s]
35+
# o # --> o
36+
# with autoref(p):
37+
# # the outer autoref just needs to insert its obj to the arglist
38+
# x # --> (lambda _ar314: _ar314[1] if _ar314[0] else x)(*_autoref_resolve((p, o, "x")))
39+
# x.a # --> ((lambda _ar314: _ar314[1] if _ar314[0] else x)(*_autoref_resolve((p, o, "x"))).a
40+
# x[s] # --> ((lambda _ar314: _ar314[1] if _ar314[0] else x)(*_autoref_resolve((p, o, "x")))[s]
41+
# # these are transformed when the **outer** autoref transforms
42+
# o # --> (lambda _ar314: _ar314[1] if _ar314[0] else o)(*_autoref_resolve((p, "o")))
43+
# o.x # --> ((lambda _ar314: _ar314[1] if _ar314[0] else o)(*_autoref_resolve((p, "o")))).x
44+
# o[s] # --> ((lambda _ar314: _ar314[1] if _ar314[0] else o)(*_autoref_resolve((p, "o"))))[s]
45+
#
46+
# The lambda is needed, because the lexical-variable lookup for ``x`` must occur at the use site.
47+
# We could modify ``_autoref_resolve`` to look in ``locals()``, too (and pass it in), but that
48+
# leads to an unnecessary performance hit.
49+
#
50+
# Recall the blocks expand from inside out. Here ``_ar*`` are gensyms. A single unique name
51+
# for the parameter of the lambdas would be sufficient, but this is easier to arrange.
52+
#
53+
# In ``_autoref_resolve``, we use a single args parameter to avoid dealing with ``*args``
54+
# when analyzing the Call node, thus avoiding much special-case code for the AST differences
55+
# between Python 3.4 and 3.5+.
56+
#
57+
# In reality, we also capture-and-assign the autoref'd expr into a gensym'd variable (instead of referring
58+
# to ``o`` and ``p`` directly), so that arbitrary expressions can be autoref'd without giving them
59+
# a name in user code.
60+
61+
def _autoref_resolve(args):
62+
*objs, s = args
63+
for o in objs:
64+
if hasattr(o, s):
65+
return True, force1(getattr(o, s))
66+
return False, None
2667

2768
def autoref(block_body, args):
2869
assert len(args) == 1, "expected exactly one argument, the object to implicitly reference"

unpythonic/syntax/lazify.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,10 @@ def transform_starred(tree, dstarred=False):
260260
thelambda.body = rec(thelambda.body)
261261
# namelambda() is used by let[] and do[]
262262
# Lazy() is a strict function, takes a lambda, constructs a Lazy object
263+
# _autoref_resolve doesn't need any special handling
263264
elif isdo(tree) or is_decorator(tree.func, "namelambda") or \
264-
any(isx(tree.func, s) for s in _ctorcalls_all) or isx(tree.func, isLazy):
265+
any(isx(tree.func, s) for s in _ctorcalls_all) or isx(tree.func, isLazy) or \
266+
isx(tree.func, "_autoref_resolve"):
265267
# here we know the operator (.func) to be one of specific names;
266268
# don't transform it to avoid confusing lazyrec[] (important if this
267269
# is an inner call in the arglist of an outer, lazy call, since it

0 commit comments

Comments
 (0)