@@ -6,6 +6,31 @@ import Base: typesof, insert!
66
77separate_kwargs (args... ; kwargs... ) = (args, kwargs. data)
88
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+
934function gen_call_with_extracted_types (__module__, fcn, ex0, kws= Expr[])
1035 if isa (ex0, Expr)
1136 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[])
1742 insert! (args, (isnothing (i) ? 2 : i+ 1 ), ex0. args[2 ])
1843 ex0 = Expr (:call , args... )
1944 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
2084 if any (a-> (Meta. isexpr (a, :kw ) || Meta. isexpr (a, :parameters )), ex0. args)
2185 return quote
2286 local arg1 = $ (esc (ex0. args[1 ]))
@@ -34,10 +98,10 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
3498 if isa (lhs, Expr)
3599 if lhs. head === :(.)
36100 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 ... )
38102 elseif lhs. head === :ref
39103 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 ... )
41105 end
42106 end
43107 elseif ex0. head === :vcat || ex0. head === :typed_vcat
@@ -55,22 +119,22 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
55119 Expr (:call , typesof,
56120 (ex0. head === :vcat ? [] : Any[esc (ex0. args[1 ])]). .. ,
57121 Expr (:tuple , lens... ),
58- map (esc, vcat (rows... ))... ))
122+ map (esc, vcat (rows... ))... ), kws ... )
59123 else
60124 return Expr (:call , fcn, f,
61- Expr (:call , typesof, map (esc, ex0. args)... ))
125+ Expr (:call , typesof, map (esc, ex0. args)... ), kws ... )
62126 end
63127 else
64128 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)
65129 if ex0. head === head
66130 return Expr (:call , fcn, f,
67- Expr (:call , typesof, map (esc, ex0. args)... ))
131+ Expr (:call , typesof, map (esc, ex0. args)... ), kws ... )
68132 end
69133 end
70134 end
71135 end
72136 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 ... )
74138 end
75139
76140 ex = Meta. lower (__module__, ex0)
@@ -89,13 +153,14 @@ function gen_call_with_extracted_types(__module__, fcn, ex0, kws=Expr[])
89153 Expr (:call , typesof, map (esc, ex. args[3 : end ])... )))
90154 else
91155 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 ... )
93157 end
94158 end
95159 if ex. head === :thunk || exret. head === :none
96160 exret = Expr (:call , :error , " expression is not a function call, "
97161 * " 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." )
99164 end
100165 return exret
101166end
0 commit comments