|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 | """Automatic currying. Transforms both function definitions and calls.""" |
3 | 3 |
|
4 | | -from ast import Call, Lambda, FunctionDef, With, withitem |
| 4 | +from ast import Call, Lambda, FunctionDef |
5 | 5 | from .astcompat import AsyncFunctionDef |
6 | 6 |
|
7 | 7 | from macropy.core.quotes import macros, ast_literal |
8 | 8 | from macropy.core.hquotes import macros, hq |
9 | 9 | from macropy.core.walkers import Walker |
10 | 10 |
|
11 | | -from .util import suggest_decorator_index |
12 | | - |
13 | | -from ..dynassign import dyn |
| 11 | +from .util import suggest_decorator_index, isx, make_isxpred, has_curry, \ |
| 12 | + sort_lambda_decorators |
14 | 13 |
|
15 | 14 | # CAUTION: unpythonic.syntax.lambdatools.namedlambda depends on the exact names |
16 | 15 | # "curryf" and "currycall" to detect an auto-curried expression with a final lambda. |
17 | 16 | from ..fun import curry as curryf, _currycall as currycall |
18 | 17 |
|
| 18 | +_iscurry = make_isxpred("curry") |
| 19 | + |
19 | 20 | def curry(block_body): |
20 | 21 | @Walker |
21 | | - def transform_call(tree, *, stop, **kw): # technically a node containing the current subtree |
| 22 | + def transform(tree, *, hascurry, set_ctx, stop, **kw): |
22 | 23 | if type(tree) is Call: |
23 | | - tree.args = [tree.func] + tree.args |
24 | | - tree.func = hq[currycall] |
| 24 | + if has_curry(tree): # detect decorated lambda with manual curry |
| 25 | + set_ctx(hascurry=True) # the lambda inside the curry(...) is the next Lambda node we will descend into. |
| 26 | + if not isx(tree.func, _iscurry): |
| 27 | + tree.args = [tree.func] + tree.args |
| 28 | + tree.func = hq[currycall] |
25 | 29 | elif type(tree) in (FunctionDef, AsyncFunctionDef): |
26 | | - # TODO: detect there's no curry already. |
27 | | - k = suggest_decorator_index("curry", tree.decorator_list) |
28 | | - if k is not None: |
29 | | - tree.decorator_list.insert(k, hq[curryf]) |
30 | | - else: # couldn't determine insert position; just plonk it at the end and hope for the best |
31 | | - tree.decorator_list.append(hq[curryf]) |
| 30 | + if not any(isx(item, _iscurry) for item in tree.decorator_list): # no manual curry already |
| 31 | + k = suggest_decorator_index("curry", tree.decorator_list) |
| 32 | + if k is not None: |
| 33 | + tree.decorator_list.insert(k, hq[curryf]) |
| 34 | + else: # couldn't determine insert position; just plonk it at the end and hope for the best |
| 35 | + tree.decorator_list.append(hq[curryf]) |
32 | 36 | elif type(tree) is Lambda: |
33 | | - # This inserts curry() as the innermost "decorator", and the curry |
34 | | - # macro is meant to run last (after e.g. tco), so we're fine. |
35 | | - # TODO: detect there's no curry already. |
36 | | - tree = hq[curryf(ast_literal[tree])] |
37 | | - # don't recurse on the lambda we just moved, but recurse inside it. |
38 | | - stop() |
39 | | - tree.args[0].body = transform_call.recurse(tree.args[0].body) |
| 37 | + if not hascurry: |
| 38 | + tree = hq[curryf(ast_literal[tree])] |
| 39 | + # don't recurse on the lambda we just moved, but recurse inside it. |
| 40 | + stop() |
| 41 | + tree.args[0].body = transform.recurse(tree.args[0].body, hascurry=False) |
40 | 42 | return tree |
41 | | - return transform_call.recurse(block_body) |
| 43 | + newbody = transform.recurse(block_body, hascurry=False) |
| 44 | + return sort_lambda_decorators(newbody) |
0 commit comments