transform: move up shortcut-range from abstract_compiler to transform
[nit.git] / src / transform.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Thansformations that simplify the AST of expressions
16 # This module transform complex AST `AExpr` nodes into simplier ones
17 module transform
18
19 import astbuilder
20 import astvalidation
21 import semantize
22 intrude import semantize::scope
23
24 redef class ToolContext
25 var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase])
26
27 # --no-shortcut-range
28 var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
29
30 redef init
31 do
32 super
33 self.option_context.add_option(self.opt_no_shortcut_range)
34 end
35 end
36
37 private class TransformPhase
38 super Phase
39
40 redef fun process_npropdef(npropdef: APropdef)
41 do
42 var val
43
44 var v = new TransformVisitor(self, npropdef)
45 v.enter_visit(npropdef)
46
47 val = new ASTValidationVisitor
48 val.enter_visit(npropdef)
49 end
50 end
51
52 private class TransformVisitor
53 super Visitor
54
55 var phase: TransformPhase
56 var mmodule: MModule
57 var mclassdef: MClassDef
58 var mpropdef: MPropDef
59 var builder: ASTBuilder
60
61 init(phase: TransformPhase, npropdef: APropdef)
62 do
63 self.phase = phase
64 self.mpropdef = npropdef.mpropdef.as(not null)
65 self.mclassdef = mpropdef.mclassdef
66 self.mmodule = mclassdef.mmodule
67 self.builder = new ASTBuilder(mmodule, mpropdef.mclassdef.bound_mtype)
68 end
69
70 redef fun visit(node)
71 do
72 if node isa AAnnotations then return
73 node.visit_all(self)
74 node.accept_transform_visitor(self)
75 end
76
77 # Get a primitive class or display a fatal error on `location`.
78 fun get_class(location: AExpr, name: String): MClass
79 do
80 return mmodule.get_primitive_class(name)
81 end
82
83 # Get a primitive method or display a fatal error on `location`.
84 fun get_method(location: AExpr, name: String, recv: MClass): MMethod
85 do
86 return phase.toolcontext.modelbuilder.force_get_primitive_method(location, name, recv, mmodule)
87 end
88 end
89
90 redef class ANode
91 private fun accept_transform_visitor(v: TransformVisitor)
92 do
93 end
94 end
95
96 redef class AVardeclExpr
97 # `var x = y` is replaced with `x = y`
98 #
99 # Declarations are only useful for scope rules
100 # Once names are associated with real objects, ther declaration become useless
101 # Therefore, if there is no initial value, then just ignore it
102 # Else, replace it with a simple assignment
103 redef fun accept_transform_visitor(v)
104 do
105 var nexpr = n_expr
106 if nexpr == null then
107 # do nothing
108 # note: not detached because the collection is currently under iteration
109 else
110 var nvar = v.builder.make_var_assign(self.variable.as(not null), nexpr)
111 replace_with(nvar)
112 end
113 end
114 end
115
116 redef class AIfexprExpr
117 # is replaced with `AIfExpr`
118 # Expression if and statement-if use two distinct classes for historical reasons
119 # However, on can replace the `AIfexprExpr` with the simpler `AIfExpr`
120 redef fun accept_transform_visitor(v)
121 do
122 var nif = v.builder.make_if(n_expr, self.mtype)
123 nif.n_then.add(n_then)
124 nif.n_else.add(n_else)
125
126 replace_with(nif)
127 end
128 end
129
130 redef class AOrExpr
131 # `x or y` is replaced with `if x then x else y`
132 redef fun accept_transform_visitor(v)
133 do
134 var nif = v.builder.make_if(n_expr, self.mtype)
135 nif.n_then.add(n_expr.make_var_read)
136 nif.n_else.add(n_expr2)
137
138 replace_with(nif)
139 end
140 end
141
142 redef class AImpliesExpr
143 redef fun accept_transform_visitor(v)
144 do
145 # TODO
146 end
147 end
148
149 redef class AAndExpr
150 # `x and y` is replaced with `if x then y else x`
151 redef fun accept_transform_visitor(v)
152 do
153 var nif = v.builder.make_if(n_expr, self.mtype)
154 nif.n_then.add(n_expr2)
155 nif.n_else.add(n_expr.make_var_read)
156
157 replace_with(nif)
158 end
159 end
160
161 redef class AWhileExpr
162 redef fun accept_transform_visitor(v)
163 do
164 var nloop = v.builder.make_loop
165 var nif = v.builder.make_if(n_expr, null)
166 nloop.add nif
167
168 var nblock = n_block
169 if nblock != null then nif.n_then.add nblock
170
171 var escapemark = self.break_mark.as(not null)
172 var nbreak = v.builder.make_break(escapemark)
173 nif.n_else.add nbreak
174
175 nloop.break_mark = self.break_mark
176 nloop.continue_mark = self.continue_mark
177
178 replace_with(nloop)
179 end
180 end
181
182 redef class AForExpr
183 redef fun accept_transform_visitor(v)
184 do
185 var escapemark = self.break_mark
186 assert escapemark != null
187
188 var nblock = v.builder.make_block
189
190 var nexpr = n_expr
191
192 # Shortcut on explicit range
193 # Avoid the instantiation of the range and the iterator
194 if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
195 var variable = variables.first
196 nblock.add v.builder.make_var_assign(variable, nexpr.n_expr)
197 var to = nexpr.n_expr2
198 nblock.add to
199
200 var nloop = v.builder.make_loop
201 nloop.break_mark = escapemark
202 nblock.add nloop
203
204 var is_ok = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_lt.as(not null), [to.make_var_read])
205
206 var nif = v.builder.make_if(is_ok, null)
207 nloop.add nif
208
209 var nthen = nif.n_then
210 var ndo = v.builder.make_do
211 ndo.break_mark = escapemark.continue_mark
212 nthen.add ndo
213
214 ndo.add self.n_block.as(not null)
215
216 var one = v.builder.make_int(1)
217 var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
218 nthen.add v.builder.make_var_assign(variable, succ)
219
220 var nbreak = v.builder.make_break(escapemark)
221 nif.n_else.add nbreak
222
223 replace_with(nblock)
224 return
225 end
226
227 nblock.add nexpr
228
229 var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
230 nblock.add iter
231
232 var nloop = v.builder.make_loop
233 nloop.break_mark = escapemark
234 nblock.add nloop
235
236 var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
237
238 var nif = v.builder.make_if(is_ok, null)
239 nloop.add nif
240
241 var nthen = nif.n_then
242 var ndo = v.builder.make_do
243 ndo.break_mark = escapemark.continue_mark
244 nthen.add ndo
245
246 if self.variables.length == 1 then
247 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
248 ndo.add v.builder.make_var_assign(variables.first, item)
249 else if self.variables.length == 2 then
250 var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
251 ndo.add v.builder.make_var_assign(variables[0], key)
252 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
253 ndo.add v.builder.make_var_assign(variables[1], item)
254 else
255 abort
256 end
257
258 ndo.add self.n_block.as(not null)
259
260 nthen.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
261
262 var nbreak = v.builder.make_break(escapemark)
263 nif.n_else.add nbreak
264
265 var method_finish = self.method_finish
266 if method_finish != null then
267 nblock.add v.builder.make_call(iter.make_var_read, method_finish, null)
268 end
269
270 replace_with(nblock)
271 end
272 end
273
274 redef class AArrayExpr
275 # `[x,y]` is replaced with
276 #
277 # var t = new Array[X].with_capacity(2)
278 # t.add(x)
279 # t.add(y)
280 # t
281 redef fun accept_transform_visitor(v)
282 do
283 var nblock = v.builder.make_block
284
285 var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.n_exprs.length)])
286 nblock.add nnew
287
288 for nexpr in self.n_exprs.n_exprs do
289 var nadd = v.builder.make_call(nnew.make_var_read, push_callsite.as(not null), [nexpr])
290 nblock.add nadd
291 end
292 var nres = nnew.make_var_read
293 nblock.add nres
294
295 replace_with(nblock)
296 end
297 end
298
299 redef class ACrangeExpr
300 # `[x..y]` is replaced with `new Range[X](x,y)`
301 redef fun accept_transform_visitor(v)
302 do
303 if parent isa AForExpr then return # to permit shortcut ranges
304 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
305 end
306 end
307
308 redef class AOrangeExpr
309 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
310 redef fun accept_transform_visitor(v)
311 do
312 if parent isa AForExpr then return # to permit shortcut ranges
313 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
314 end
315 end
316
317 redef class AParExpr
318 # `(x)` is replaced with `x`
319 redef fun accept_transform_visitor(v)
320 do
321 replace_with(n_expr)
322 end
323 end
324
325 redef class ASendReassignFormExpr
326 # `x.foo(y)+=z` is replaced with
327 #
328 # x.foo(y) = x.foo(y) + z
329 #
330 # witch is, in reality:
331 #
332 # x."foo="(y, x.foo(y)."+"(z))
333 redef fun accept_transform_visitor(v)
334 do
335 var nblock = v.builder.make_block
336 nblock.add(n_expr)
337
338 var read_args = new Array[AExpr]
339 var write_args = new Array[AExpr]
340 for a in raw_arguments do
341 nblock.add(a)
342 read_args.add(a.make_var_read)
343 write_args.add(a.make_var_read)
344 end
345
346 var nread = v.builder.make_call(n_expr.make_var_read, callsite.as(not null), read_args)
347
348 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
349
350 write_args.add(nnewvalue)
351 var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.as(not null), write_args)
352 nblock.add(nwrite)
353
354 replace_with(nblock)
355 end
356 end
357
358 redef class AVarReassignExpr
359 # `v += z` is replaced with `v = v + z`
360 redef fun accept_transform_visitor(v)
361 do
362 var variable = self.variable.as(not null)
363
364 var nread = v.builder.make_var_read(variable, read_type.as(not null))
365
366 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
367 var nwrite = v.builder.make_var_assign(variable, nnewvalue)
368
369 replace_with(nwrite)
370 end
371 end
372
373 redef class AAttrReassignExpr
374 # `x.a += z` is replaced with `x.a = x.a + z`
375 redef fun accept_transform_visitor(v)
376 do
377 var nblock = v.builder.make_block
378 nblock.add(n_expr)
379 var attribute = self.mproperty.as(not null)
380
381 var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute)
382 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
383 var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
384 nblock.add(nwrite)
385
386 replace_with(nblock)
387 end
388 end