|
1 |
| -# Julia 0.6 macros to aid in vectorization: @view, @views, @__dot__ (@.), |
2 |
| -# backported from Julia 0.6. |
3 |
| - |
4 |
| -# prior to julia#20247, the replace_ref_end! macro had hygiene bugs |
5 |
| -if VERSION < v"0.6.0-dev.2406" |
6 |
| - function trailingsize(A, n) |
7 |
| - s = 1 |
8 |
| - for i=n:ndims(A) |
9 |
| - s *= size(A,i) |
10 |
| - end |
11 |
| - return s |
12 |
| - end |
13 |
| - replace_ref_end!(ex) = replace_ref_end_!(ex, nothing)[1] |
14 |
| - # replace_ref_end_!(ex,withex) returns (new ex, whether withex was used) |
15 |
| - function replace_ref_end_!(ex, withex) |
16 |
| - used_withex = false |
17 |
| - if isa(ex,Symbol) && ex == :end |
18 |
| - withex === nothing && error("Invalid use of end") |
19 |
| - return withex, true |
20 |
| - elseif isa(ex,Expr) |
21 |
| - if ex.head == :ref |
22 |
| - ex.args[1], used_withex = replace_ref_end_!(ex.args[1],withex) |
23 |
| - S = isa(ex.args[1],Symbol) ? ex.args[1]::Symbol : gensym(:S) # temp var to cache ex.args[1] if needed |
24 |
| - used_S = false # whether we actually need S |
25 |
| - # new :ref, so redefine withex |
26 |
| - nargs = length(ex.args)-1 |
27 |
| - if nargs == 0 |
28 |
| - return ex, used_withex |
29 |
| - elseif nargs == 1 |
30 |
| - # replace with endof(S) |
31 |
| - ex.args[2], used_S = replace_ref_end_!(ex.args[2],:($endof($S))) |
32 |
| - else |
33 |
| - n = 1 |
34 |
| - J = endof(ex.args) |
35 |
| - for j = 2:J-1 |
36 |
| - exj, used = replace_ref_end_!(ex.args[j],:($size($S,$n))) |
37 |
| - used_S |= used |
38 |
| - ex.args[j] = exj |
39 |
| - if isa(exj,Expr) && exj.head == :... |
40 |
| - # splatted object |
41 |
| - exjs = exj.args[1] |
42 |
| - n = :($n + length($exjs)) |
43 |
| - elseif isa(n, Expr) |
44 |
| - # previous expression splatted |
45 |
| - n = :($n + 1) |
46 |
| - else |
47 |
| - # an integer |
48 |
| - n += 1 |
49 |
| - end |
50 |
| - end |
51 |
| - ex.args[J], used = replace_ref_end_!(ex.args[J],:($trailingsize($S,$n))) |
52 |
| - used_S |= used |
53 |
| - end |
54 |
| - if used_S && S !== ex.args[1] |
55 |
| - S0 = ex.args[1] |
56 |
| - ex.args[1] = S |
57 |
| - ex = Expr(:let, ex, :($S = $S0)) |
58 |
| - end |
59 |
| - else |
60 |
| - # recursive search |
61 |
| - for i = eachindex(ex.args) |
62 |
| - ex.args[i], used = replace_ref_end_!(ex.args[i],withex) |
63 |
| - used_withex |= used |
64 |
| - end |
65 |
| - end |
66 |
| - end |
67 |
| - ex, used_withex |
68 |
| - end |
69 |
| -end |
70 |
| - |
71 |
| -if !isdefined(Base, Symbol("@view")) |
72 |
| - macro view(ex) |
73 |
| - if Meta.isexpr(ex, :ref) |
74 |
| - ex = replace_ref_end!(ex) |
75 |
| - if Meta.isexpr(ex, :ref) |
76 |
| - ex = Expr(:call, view, ex.args...) |
77 |
| - else # ex replaced by let ...; foo[...]; end |
78 |
| - assert(Meta.isexpr(ex, :let) && Meta.isexpr(ex.args[1], :ref)) |
79 |
| - ex.args[1] = Expr(:call, view, ex.args[1].args...) |
80 |
| - end |
81 |
| - Expr(:&&, true, esc(ex)) |
82 |
| - else |
83 |
| - throw(ArgumentError("Invalid use of @view macro: argument must be a reference expression A[...].")) |
84 |
| - end |
85 |
| - end |
86 |
| - export @view |
87 |
| -end |
88 |
| - |
89 |
| -if !isdefined(Base, Symbol("@views")) |
90 |
| - maybeview(A, args...) = getindex(A, args...) |
91 |
| - maybeview(A::AbstractArray, args...) = view(A, args...) |
92 |
| - maybeview(A::AbstractArray, args::Number...) = getindex(A, args...) |
93 |
| - maybeview(A) = getindex(A) |
94 |
| - maybeview(A::AbstractArray) = getindex(A) |
95 |
| - |
96 |
| - _views(x) = x |
97 |
| - function _views(ex::Expr) |
98 |
| - if ex.head in (:(=), :(.=)) |
99 |
| - # don't use view for ref on the lhs of an assignment, |
100 |
| - # but still use views for the args of the ref: |
101 |
| - lhs = ex.args[1] |
102 |
| - Expr(ex.head, Meta.isexpr(lhs, :ref) ? |
103 |
| - Expr(:ref, map(_views, lhs.args)...) : _views(lhs), |
104 |
| - _views(ex.args[2])) |
105 |
| - elseif ex.head == :ref |
106 |
| - Expr(:call, maybeview, map(_views, ex.args)...) |
107 |
| - else |
108 |
| - h = string(ex.head) |
109 |
| - # don't use view on the lhs of an op-assignment a[i...] += ... |
110 |
| - if last(h) == '=' && Meta.isexpr(ex.args[1], :ref) |
111 |
| - lhs = ex.args[1] |
112 |
| - |
113 |
| - # temp vars to avoid recomputing a and i, |
114 |
| - # which will be assigned in a let block: |
115 |
| - a = gensym(:a) |
116 |
| - i = [gensym(:i) for k = 1:length(lhs.args)-1] |
117 |
| - |
118 |
| - # for splatted indices like a[i, j...], we need to |
119 |
| - # splat the corresponding temp var. |
120 |
| - I = similar(i, Any) |
121 |
| - for k = 1:length(i) |
122 |
| - if Meta.isexpr(lhs.args[k+1], :...) |
123 |
| - I[k] = Expr(:..., i[k]) |
124 |
| - lhs.args[k+1] = lhs.args[k+1].args[1] # unsplat |
125 |
| - else |
126 |
| - I[k] = i[k] |
127 |
| - end |
128 |
| - end |
129 |
| - |
130 |
| - Expr(:let, |
131 |
| - Expr(first(h) == '.' ? :(.=) : :(=), :($a[$(I...)]), |
132 |
| - Expr(:call, Symbol(h[1:end-1]), |
133 |
| - :($maybeview($a, $(I...))), |
134 |
| - map(_views, ex.args[2:end])...)), |
135 |
| - :($a = $(_views(lhs.args[1]))), |
136 |
| - [:($(i[k]) = $(_views(lhs.args[k+1]))) for k=1:length(i)]...) |
137 |
| - else |
138 |
| - Expr(ex.head, map(_views, ex.args)...) |
139 |
| - end |
140 |
| - end |
141 |
| - end |
142 |
| - |
143 |
| - macro views(x) |
144 |
| - esc(_views(replace_ref_end!(x))) |
145 |
| - end |
146 |
| - export @views |
147 |
| -end |
148 |
| - |
149 |
| -# we can't define @. because that doesn't parse in Julia < 0.6, but |
150 |
| -# we can define @__dot__, which is what @. is sugar for: |
151 |
| -if !isdefined(Base, Symbol("@__dot__")) |
152 |
| - dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view) |
153 |
| - dottable(x::Symbol) = !Base.isoperator(x) || first(string(x)) != '.' || x == :.. # don't add dots to dot operators |
154 |
| - dottable(x::Expr) = x.head != :$ |
155 |
| - undot(x) = x |
156 |
| - function undot(x::Expr) |
157 |
| - if x.head == :.= |
158 |
| - Expr(:(=), x.args...) |
159 |
| - elseif x.head == :block # occurs in for x=..., y=... |
160 |
| - Expr(:block, map(undot, x.args)...) |
161 |
| - else |
162 |
| - x |
163 |
| - end |
164 |
| - end |
165 |
| - __dot__(x) = x |
166 |
| - function __dot__(x::Expr) |
167 |
| - dotargs = map(__dot__, x.args) |
168 |
| - if x.head == :call && dottable(x.args[1]) |
169 |
| - Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) |
170 |
| - elseif x.head == :$ |
171 |
| - x.args[1] |
172 |
| - elseif x.head == :let # don't add dots to "let x=... assignments |
173 |
| - Expr(:let, dotargs[1], map(undot, dotargs[2:end])...) |
174 |
| - elseif x.head == :for # don't add dots to for x=... assignments |
175 |
| - Expr(:for, undot(dotargs[1]), dotargs[2]) |
176 |
| - elseif (x.head == :(=) || x.head == :function || x.head == :macro) && |
177 |
| - Meta.isexpr(x.args[1], :call) # function or macro definition |
178 |
| - Expr(x.head, x.args[1], dotargs[2]) |
179 |
| - else |
180 |
| - head = string(x.head) |
181 |
| - if last(head) == '=' && first(head) != '.' |
182 |
| - Expr(Symbol('.',head), dotargs...) |
183 |
| - else |
184 |
| - Expr(x.head, dotargs...) |
185 |
| - end |
186 |
| - end |
187 |
| - end |
188 |
| - macro __dot__(x) |
189 |
| - esc(__dot__(x)) |
190 |
| - end |
191 |
| - macro dotcompat(x) |
192 |
| - esc(_compat(__dot__(x))) |
193 |
| - end |
194 |
| - export @__dot__, @dotcompat |
195 |
| -else |
196 |
| - # in 0.6, use the __dot__ function from Base.Broadcast |
197 |
| - macro dotcompat(x) |
198 |
| - esc(_compat(Base.Broadcast.__dot__(x))) |
199 |
| - end |
200 |
| - export @dotcompat |
| 1 | +# TODO deprecate |
| 2 | +# this was defined for use with Julia versions prior to 0.5 |
| 3 | +# (see https://github.com/JuliaLang/Compat.jl/pull/316) |
| 4 | +macro dotcompat(x) |
| 5 | + esc(_compat(Base.Broadcast.__dot__(x))) |
201 | 6 | end
|
| 7 | +export @dotcompat |
0 commit comments