transform: introduce `full_transform_visitor` for complex transformations
[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.mpropdef.as(not null))
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 is noinit
57 var mclassdef: MClassDef is noinit
58 var mpropdef: MPropDef
59 var builder: ASTBuilder is noinit
60
61 init
62 do
63 self.mclassdef = mpropdef.mclassdef
64 self.mmodule = mclassdef.mmodule
65 self.builder = new ASTBuilder(mmodule, mpropdef.mclassdef.bound_mtype)
66 end
67
68 redef fun visit(node)
69 do
70 if node isa AAnnotations then return
71 node.full_transform_visitor(self)
72 end
73
74 # Get a primitive class or display a fatal error on `location`.
75 fun get_class(location: AExpr, name: String): MClass
76 do
77 return mmodule.get_primitive_class(name)
78 end
79
80 # Get a primitive method or display a fatal error on `location`.
81 fun get_method(location: AExpr, name: String, recv: MClass): MMethod
82 do
83 return phase.toolcontext.modelbuilder.force_get_primitive_method(location, name, recv, mmodule)
84 end
85 end
86
87 redef class ANode
88 private fun full_transform_visitor(v: TransformVisitor)
89 do
90 visit_all(v)
91 accept_transform_visitor(v)
92 end
93 private fun accept_transform_visitor(v: TransformVisitor)
94 do
95 end
96 end
97
98 redef class AVardeclExpr
99 # `var x = y` is replaced with `x = y`
100 #
101 # Declarations are only useful for scope rules
102 # Once names are associated with real objects, ther declaration become useless
103 # Therefore, if there is no initial value, then just ignore it
104 # Else, replace it with a simple assignment
105 redef fun accept_transform_visitor(v)
106 do
107 var nexpr = n_expr
108 if nexpr == null then
109 # do nothing
110 # note: not detached because the collection is currently under iteration
111 else
112 var nvar = v.builder.make_var_assign(self.variable.as(not null), nexpr)
113 replace_with(nvar)
114 end
115 end
116 end
117
118 redef class AIfexprExpr
119 # is replaced with `AIfExpr`
120 # Expression if and statement-if use two distinct classes for historical reasons
121 # However, on can replace the `AIfexprExpr` with the simpler `AIfExpr`
122 redef fun accept_transform_visitor(v)
123 do
124 var nif = v.builder.make_if(n_expr, self.mtype)
125 nif.n_then.add(n_then)
126 nif.n_else.add(n_else)
127
128 replace_with(nif)
129 end
130 end
131
132 redef class AOrExpr
133 # `x or y` is replaced with `if x then x else y`
134 redef fun accept_transform_visitor(v)
135 do
136 var nif = v.builder.make_if(n_expr, self.mtype)
137 nif.n_then.add(n_expr.make_var_read)
138 nif.n_else.add(n_expr2)
139
140 replace_with(nif)
141 end
142 end
143
144 redef class AImpliesExpr
145 redef fun accept_transform_visitor(v)
146 do
147 # TODO
148 end
149 end
150
151 redef class AAndExpr
152 # `x and y` is replaced with `if x then y else x`
153 redef fun accept_transform_visitor(v)
154 do
155 var nif = v.builder.make_if(n_expr, self.mtype)
156 nif.n_then.add(n_expr2)
157 nif.n_else.add(n_expr.make_var_read)
158
159 replace_with(nif)
160 end
161 end
162
163 redef class AWhileExpr
164 redef fun accept_transform_visitor(v)
165 do
166 var nloop = v.builder.make_loop
167 var nif = v.builder.make_if(n_expr, null)
168 nloop.add nif
169
170 var nblock = n_block
171 if nblock != null then nif.n_then.add nblock
172
173 var escapemark = self.break_mark.as(not null)
174 var nbreak = v.builder.make_break(escapemark)
175 nif.n_else.add nbreak
176
177 nloop.break_mark = self.break_mark
178 nloop.continue_mark = self.continue_mark
179
180 replace_with(nloop)
181 end
182 end
183
184 redef class AForExpr
185 redef fun accept_transform_visitor(v)
186 do
187 var escapemark = self.break_mark
188 assert escapemark != null
189
190 var nblock = v.builder.make_block
191
192 var nexpr = n_expr
193
194 # Shortcut on explicit range
195 # Avoid the instantiation of the range and the iterator
196 if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
197 var variable = variables.first
198 nblock.add v.builder.make_var_assign(variable, nexpr.n_expr)
199 var to = nexpr.n_expr2
200 nblock.add to
201
202 var nloop = v.builder.make_loop
203 nloop.break_mark = escapemark
204 nblock.add nloop
205
206 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])
207
208 var nif = v.builder.make_if(is_ok, null)
209 nloop.add nif
210
211 var nthen = nif.n_then
212 var ndo = v.builder.make_do
213 ndo.break_mark = escapemark.continue_mark
214 nthen.add ndo
215
216 ndo.add self.n_block.as(not null)
217
218 var one = v.builder.make_int(1)
219 var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
220 nthen.add v.builder.make_var_assign(variable, succ)
221
222 var nbreak = v.builder.make_break(escapemark)
223 nif.n_else.add nbreak
224
225 replace_with(nblock)
226 return
227 end
228
229 nblock.add nexpr
230
231 var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
232 nblock.add iter
233
234 var nloop = v.builder.make_loop
235 nloop.break_mark = escapemark
236 nblock.add nloop
237
238 var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
239
240 var nif = v.builder.make_if(is_ok, null)
241 nloop.add nif
242
243 var nthen = nif.n_then
244 var ndo = v.builder.make_do
245 ndo.break_mark = escapemark.continue_mark
246 nthen.add ndo
247
248 if self.variables.length == 1 then
249 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
250 ndo.add v.builder.make_var_assign(variables.first, item)
251 else if self.variables.length == 2 then
252 var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
253 ndo.add v.builder.make_var_assign(variables[0], key)
254 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
255 ndo.add v.builder.make_var_assign(variables[1], item)
256 else
257 abort
258 end
259
260 ndo.add self.n_block.as(not null)
261
262 nthen.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
263
264 var nbreak = v.builder.make_break(escapemark)
265 nif.n_else.add nbreak
266
267 var method_finish = self.method_finish
268 if method_finish != null then
269 nblock.add v.builder.make_call(iter.make_var_read, method_finish, null)
270 end
271
272 replace_with(nblock)
273 end
274 end
275
276 redef class AArrayExpr
277 # `[x,y]` is replaced with
278 #
279 # ~~~nitish
280 # var t = new Array[X].with_capacity(2)
281 # t.add(x)
282 # t.add(y)
283 # t
284 # ~~~
285 redef fun accept_transform_visitor(v)
286 do
287 var nblock = v.builder.make_block
288
289 var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.length)])
290 nblock.add nnew
291
292 for nexpr in self.n_exprs do
293 var nadd = v.builder.make_call(nnew.make_var_read, push_callsite.as(not null), [nexpr])
294 nblock.add nadd
295 end
296 var nres = nnew.make_var_read
297 nblock.add nres
298
299 replace_with(nblock)
300 end
301 end
302
303 redef class ACrangeExpr
304 # `[x..y]` is replaced with `new Range[X](x,y)`
305 redef fun accept_transform_visitor(v)
306 do
307 if parent isa AForExpr then return # to permit shortcut ranges
308 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
309 end
310 end
311
312 redef class AOrangeExpr
313 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
314 redef fun accept_transform_visitor(v)
315 do
316 if parent isa AForExpr then return # to permit shortcut ranges
317 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
318 end
319 end
320
321 redef class AParExpr
322 # `(x)` is replaced with `x`
323 redef fun accept_transform_visitor(v)
324 do
325 replace_with(n_expr)
326 end
327 end
328
329 redef class ASendReassignFormExpr
330 # `x.foo(y)+=z` is replaced with
331 #
332 # ~~~nitish
333 # x.foo(y) = x.foo(y) + z
334 # ~~~
335 #
336 # witch is, in reality:
337 #
338 # ~~~nitish
339 # x."foo="(y, x.foo(y)."+"(z))
340 # ~~~
341 redef fun accept_transform_visitor(v)
342 do
343 var nblock = v.builder.make_block
344 nblock.add(n_expr)
345
346 var read_args = new Array[AExpr]
347 var write_args = new Array[AExpr]
348 for a in raw_arguments do
349 nblock.add(a)
350 read_args.add(a.make_var_read)
351 write_args.add(a.make_var_read)
352 end
353
354 var nread = v.builder.make_call(n_expr.make_var_read, callsite.as(not null), read_args)
355
356 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
357
358 write_args.add(nnewvalue)
359 var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.as(not null), write_args)
360 nblock.add(nwrite)
361
362 replace_with(nblock)
363 end
364 end
365
366 redef class AVarReassignExpr
367 # `v += z` is replaced with `v = v + z`
368 redef fun accept_transform_visitor(v)
369 do
370 var variable = self.variable.as(not null)
371
372 var nread = v.builder.make_var_read(variable, read_type.as(not null))
373
374 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
375 var nwrite = v.builder.make_var_assign(variable, nnewvalue)
376
377 replace_with(nwrite)
378 end
379 end
380
381 redef class AAttrReassignExpr
382 # `x.a += z` is replaced with `x.a = x.a + z`
383 redef fun accept_transform_visitor(v)
384 do
385 var nblock = v.builder.make_block
386 nblock.add(n_expr)
387 var attribute = self.mproperty.as(not null)
388
389 var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute)
390 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
391 var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
392 nblock.add(nwrite)
393
394 replace_with(nblock)
395 end
396 end