|
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