|
7 | 7 | from macropy.core.walkers import Walker |
8 | 8 |
|
9 | 9 | from ..dynassign import dyn |
| 10 | +from ..lazyutil import force1 |
10 | 11 |
|
11 | 12 | # TODO: suppport Attribute, Subscript in autoref |
12 | 13 | # TODO: support nested autorefs |
13 | | -# Consider: |
| 14 | +# We need something like:: |
| 15 | +# |
14 | 16 | # with autoref(o): |
15 | 17 | # x # --> (o.x if hasattr(o, "x") else x) |
16 | 18 | # x.a # --> (o.x.a if hasattr(o, "x") else x.a) |
|
23 | 25 | # o # --> (p.o if hasattr(p, "o") else o) |
24 | 26 | # o.x # --> (p.o.x if hasattr(p, "o") else o.x) |
25 | 27 | # 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 |
26 | 67 |
|
27 | 68 | def autoref(block_body, args): |
28 | 69 | assert len(args) == 1, "expected exactly one argument, the object to implicitly reference" |
|
0 commit comments