@@ -6,6 +6,31 @@ import Base: typesof, insert!
6
6
7
7
separate_kwargs (args... ; kwargs... ) = (args, kwargs. data)
8
8
9
+ """
10
+ Transform a dot expression into one where each argument has been replaced by a
11
+ variable "xj" (with j an integer from 1 to the returned i).
12
+ The list `args` contains the original arguments that have been replaced.
13
+ """
14
+ function recursive_dotcalls! (ex, args, i= 1 )
15
+ if ! (ex isa Expr) || ((ex. head != = :. || ! (ex. args[2 ] isa Expr)) &&
16
+ (ex. head != = :call || string (ex. args[1 ])[1 ] != ' .' ))
17
+ newarg = Symbol (' x' , i)
18
+ if ex. head === :...
19
+ push! (args, only (ex. args))
20
+ return Expr (:... , newarg), i+ 1
21
+ else
22
+ push! (args, ex)
23
+ return newarg, i+ 1
24
+ end
25
+ end
26
+ (start, branches) = ex. head === :. ? (1 , ex. args[2 ]. args) : (2 , ex. args)
27
+ for j in start: length (branches)
28
+ branch, i = recursive_dotcalls! (branches[j], args, i)
29
+ branches[j] = branch
30
+ end
31
+ return ex, i
32
+ end
33
+
9
34
function gen_call_with_extracted_types (__module__, fcn, ex0, kws= Expr[])
10
35
if isa (ex0, Expr)
11
36
if ex0. head === :do && Meta. isexpr (get (ex0. args, 1 , nothing ), :call )
@@ -17,6 +42,45 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
17
42
insert! (args, (isnothing (i) ? 2 : i+ 1 ), ex0. args[2 ])
18
43
ex0 = Expr (:call , args... )
19
44
end
45
+ if ex0. head === :. || (ex0. head === :call && string (ex0. args[1 ])[1 ] == ' .' )
46
+ codemacro = startswith (string (fcn), " code_" )
47
+ if codemacro && ex0. args[2 ] isa Expr
48
+ # Manually wrap a dot call in a function
49
+ args = Any[]
50
+ ex, i = recursive_dotcalls! (copy (ex0), args)
51
+ xargs = [Symbol (' x' , j) for j in 1 : i- 1 ]
52
+ dotfuncname = gensym (" dotfunction" )
53
+ dotfuncdef = Expr (:local , Expr (:(= ), Expr (:call , dotfuncname, xargs... ), ex))
54
+ return quote
55
+ $ (esc (dotfuncdef))
56
+ local args = typesof ($ (map (esc, args)... ))
57
+ $ (fcn)($ (esc (dotfuncname)), args; $ (kws... ))
58
+ end
59
+ elseif ! codemacro
60
+ fully_qualified_symbol = true # of the form A.B.C.D
61
+ ex1 = ex0
62
+ while ex1 isa Expr && ex1. head === :.
63
+ fully_qualified_symbol = (length (ex1. args) == 2 &&
64
+ ex1. args[2 ] isa QuoteNode &&
65
+ ex1. args[2 ]. value isa Symbol)
66
+ fully_qualified_symbol || break
67
+ ex1 = ex1. args[1 ]
68
+ end
69
+ fully_qualified_symbol &= ex1 isa Symbol
70
+ if fully_qualified_symbol
71
+ if string (fcn) == " which"
72
+ return quote $ (fcn)($ (esc (ex0. args[1 ])), $ (ex0. args[2 ])) end
73
+ else
74
+ return Expr (:call , :error , " expression is not a function call or symbol" )
75
+ end
76
+ elseif ex0. args[2 ] isa Expr
77
+ return Expr (:call , :error , " dot expressions are not lowered to "
78
+ * " a single function call, so @$fcn cannot analyze "
79
+ * " them. You may want to use Meta.@lower to identify "
80
+ * " which function call to target." )
81
+ end
82
+ end
83
+ end
20
84
if any (a-> (Meta. isexpr (a, :kw ) || Meta. isexpr (a, :parameters )), ex0. args)
21
85
return quote
22
86
local arg1 = $ (esc (ex0. args[1 ]))
@@ -34,10 +98,10 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
34
98
if isa (lhs, Expr)
35
99
if lhs. head === :(.)
36
100
return Expr (:call , fcn, Base. setproperty!,
37
- Expr (:call , typesof, map (esc, lhs. args)... , esc (rhs)))
101
+ Expr (:call , typesof, map (esc, lhs. args)... , esc (rhs)), kws ... )
38
102
elseif lhs. head === :ref
39
103
return Expr (:call , fcn, Base. setindex!,
40
- Expr (:call , typesof, esc (lhs. args[1 ]), esc (rhs), map (esc, lhs. args[2 : end ])... ))
104
+ Expr (:call , typesof, esc (lhs. args[1 ]), esc (rhs), map (esc, lhs. args[2 : end ])... ), kws ... )
41
105
end
42
106
end
43
107
elseif ex0. head === :vcat || ex0. head === :typed_vcat
@@ -55,22 +119,22 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
55
119
Expr (:call , typesof,
56
120
(ex0. head === :vcat ? [] : Any[esc (ex0. args[1 ])]). .. ,
57
121
Expr (:tuple , lens... ),
58
- map (esc, vcat (rows... ))... ))
122
+ map (esc, vcat (rows... ))... ), kws ... )
59
123
else
60
124
return Expr (:call , fcn, f,
61
- Expr (:call , typesof, map (esc, ex0. args)... ))
125
+ Expr (:call , typesof, map (esc, ex0. args)... ), kws ... )
62
126
end
63
127
else
64
128
for (head, f) in (:ref => Base. getindex, :hcat => Base. hcat, :(.) => Base. getproperty, :vect => Base. vect, Symbol (" '" ) => Base. adjoint, :typed_hcat => Base. typed_hcat, :string => string)
65
129
if ex0. head === head
66
130
return Expr (:call , fcn, f,
67
- Expr (:call , typesof, map (esc, ex0. args)... ))
131
+ Expr (:call , typesof, map (esc, ex0. args)... ), kws ... )
68
132
end
69
133
end
70
134
end
71
135
end
72
136
if isa (ex0, Expr) && ex0. head === :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions*
73
- return Expr (:call , fcn, esc (ex0. args[1 ]), Tuple{#= __source__=# LineNumberNode, #= __module__=# Module, Any[ Core. Typeof (a) for a in ex0. args[3 : end ] ]. .. })
137
+ return Expr (:call , fcn, esc (ex0. args[1 ]), Tuple{#= __source__=# LineNumberNode, #= __module__=# Module, Any[ Core. Typeof (a) for a in ex0. args[3 : end ] ]. .. }, kws ... )
74
138
end
75
139
76
140
ex = Meta. lower (__module__, ex0)
@@ -89,13 +153,14 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
89
153
Expr (:call , typesof, map (esc, ex. args[3 : end ])... )))
90
154
else
91
155
exret = Expr (:call , fcn, esc (ex. args[1 ]),
92
- Expr (:call , typesof, map (esc, ex. args[2 : end ])... ))
156
+ Expr (:call , typesof, map (esc, ex. args[2 : end ])... ), kws ... )
93
157
end
94
158
end
95
159
if ex. head === :thunk || exret. head === :none
96
160
exret = Expr (:call , :error , " expression is not a function call, "
97
161
* " or is too complex for @$fcn to analyze; "
98
- * " break it down to simpler parts if possible" )
162
+ * " break it down to simpler parts if possible. "
163
+ * " In some cases, you may want to use Meta.@lower." )
99
164
end
100
165
return exret
101
166
end
0 commit comments