838b9bcb53fe371be2f7869cf03d41697b2d3ccc
[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 intrude import semantize::typing
24
25 redef class ToolContext
26 var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase])
27
28 # --no-shortcut-range
29 var opt_no_shortcut_range: OptionBool = new OptionBool("Always instantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
30
31 redef init
32 do
33 super
34 self.option_context.add_option(self.opt_no_shortcut_range)
35 end
36 end
37
38 private class TransformPhase
39 super Phase
40
41 redef fun process_npropdef(npropdef: APropdef)
42 do
43 var val
44
45 var m = npropdef.mpropdef
46 if m == null then return
47 var v = new TransformVisitor(self, m)
48 v.enter_visit(npropdef)
49
50 val = new ASTValidationVisitor
51 val.enter_visit(npropdef)
52 end
53 end
54
55 private class TransformVisitor
56 super Visitor
57
58 var phase: TransformPhase
59 var mmodule: MModule is noinit
60 var mclassdef: MClassDef is noinit
61 var mpropdef: MPropDef
62 var builder: ASTBuilder is noinit
63
64 init
65 do
66 self.mclassdef = mpropdef.mclassdef
67 self.mmodule = mclassdef.mmodule
68 self.builder = new ASTBuilder(mmodule, mpropdef.mclassdef.bound_mtype)
69 end
70
71 redef fun visit(node)
72 do
73 if node isa AAnnotations then return
74 node.full_transform_visitor(self)
75 end
76
77 # Get a primitive method or display a fatal error on `location`.
78 fun get_method(location: AExpr, name: String, recv: MClass): MMethod
79 do
80 return phase.toolcontext.modelbuilder.force_get_primitive_method(location, name, recv, mmodule)
81 end
82 end
83
84 redef class ANode
85 private fun full_transform_visitor(v: TransformVisitor)
86 do
87 visit_all(v)
88 accept_transform_visitor(v)
89 end
90 private fun accept_transform_visitor(v: TransformVisitor)
91 do
92 end
93 end
94
95 redef class AExpr
96 redef fun full_transform_visitor(v: TransformVisitor)
97 do
98 var na = comprehension
99 if na != null then
100 # We are building a comprehension array `array`
101 # Replace `self` with `array.push(self)`
102 var place = detach_with_placeholder
103 var recv = na.nnew.make_var_read
104 var nadd = v.builder.make_call(recv, na.push_callsite.as(not null), [self])
105 place.replace_with(nadd)
106 end
107
108 visit_all(v)
109
110 if mtype == null and not is_typed then return # Skip broken
111
112 accept_transform_visitor(v)
113 end
114
115 redef fun replace_with(other)
116 do
117 super
118 if other isa AExpr then
119 if other.implicit_cast_to == null then other.implicit_cast_to = implicit_cast_to
120 end
121 end
122 end
123
124 redef class AVardeclExpr
125 # `var x = y` is replaced with `x = y`
126 #
127 # Declarations are only useful for scope rules
128 # Once names are associated with real objects, ther declaration become useless
129 # Therefore, if there is no initial value, then just ignore it
130 # Else, replace it with a simple assignment
131 redef fun accept_transform_visitor(v)
132 do
133 var nexpr = n_expr
134 if nexpr == null then
135 # do nothing
136 # note: not detached because the collection is currently under iteration
137 else
138 var nvar = v.builder.make_var_assign(self.variable.as(not null), nexpr)
139 replace_with(nvar)
140 end
141 end
142 end
143
144 redef class AIfexprExpr
145 # is replaced with `AIfExpr`
146 # Expression if and statement-if use two distinct classes for historical reasons
147 # However, on can replace the `AIfexprExpr` with the simpler `AIfExpr`
148 redef fun accept_transform_visitor(v)
149 do
150 var nif = v.builder.make_if(n_expr, self.mtype)
151 nif.n_then.add(n_then)
152 nif.n_else.add(n_else)
153
154 replace_with(nif)
155 end
156 end
157
158 redef class AOrExpr
159 # `x or y` is replaced with `if x then x else y`
160 redef fun accept_transform_visitor(v)
161 do
162 var nif = v.builder.make_if(n_expr, self.mtype)
163 nif.n_then.add(n_expr.make_var_read)
164 nif.n_else.add(n_expr2)
165
166 replace_with(nif)
167 end
168 end
169
170 redef class AImpliesExpr
171 redef fun accept_transform_visitor(v)
172 do
173 # TODO
174 end
175 end
176
177 redef class AAndExpr
178 # `x and y` is replaced with `if x then y else x`
179 redef fun accept_transform_visitor(v)
180 do
181 var nif = v.builder.make_if(n_expr, self.mtype)
182 nif.n_then.add(n_expr2)
183 nif.n_else.add(n_expr.make_var_read)
184
185 replace_with(nif)
186 end
187 end
188
189 redef class AWhileExpr
190 redef fun accept_transform_visitor(v)
191 do
192 var nloop = v.builder.make_loop
193 var nif = v.builder.make_if(n_expr, null)
194 nloop.add nif
195
196 var nblock = n_block
197 if nblock != null then nif.n_then.add nblock
198
199 var escapemark = self.break_mark.as(not null)
200 var nbreak = v.builder.make_break(escapemark)
201 nif.n_else.add nbreak
202
203 nloop.break_mark = self.break_mark
204 nloop.continue_mark = self.continue_mark
205
206 replace_with(nloop)
207 end
208 end
209
210 redef class AForExpr
211 redef fun accept_transform_visitor(v)
212 do
213 var escapemark = self.break_mark
214 assert escapemark != null
215
216 var nblock = v.builder.make_block
217
218 var nexpr = n_expr
219
220 # Shortcut on explicit range
221 # Avoid the instantiation of the range and the iterator
222 if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
223 var variable = variables.first
224 nblock.add v.builder.make_var_assign(variable, nexpr.n_expr)
225 var to = nexpr.n_expr2
226 nblock.add to
227
228 var nloop = v.builder.make_loop
229 nloop.break_mark = escapemark
230 nblock.add nloop
231
232 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])
233
234 var nif = v.builder.make_if(is_ok, null)
235 nloop.add nif
236
237 var nthen = nif.n_then
238 var ndo = v.builder.make_do
239 ndo.break_mark = escapemark.continue_mark
240 nthen.add ndo
241
242 ndo.add self.n_block.as(not null)
243
244 var one = v.builder.make_int(1)
245 var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
246 nthen.add v.builder.make_var_assign(variable, succ)
247
248 var nbreak = v.builder.make_break(escapemark)
249 nif.n_else.add nbreak
250
251 replace_with(nblock)
252 return
253 end
254
255 nblock.add nexpr
256
257 var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
258 nblock.add iter
259
260 var nloop = v.builder.make_loop
261 nloop.break_mark = escapemark
262 nblock.add nloop
263
264 var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
265
266 var nif = v.builder.make_if(is_ok, null)
267 nloop.add nif
268
269 var nthen = nif.n_then
270 var ndo = v.builder.make_do
271 ndo.break_mark = escapemark.continue_mark
272 nthen.add ndo
273
274 if self.variables.length == 1 then
275 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
276 ndo.add v.builder.make_var_assign(variables.first, item)
277 else if self.variables.length == 2 then
278 var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
279 ndo.add v.builder.make_var_assign(variables[0], key)
280 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
281 ndo.add v.builder.make_var_assign(variables[1], item)
282 else
283 abort
284 end
285
286 ndo.add self.n_block.as(not null)
287
288 nthen.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
289
290 var nbreak = v.builder.make_break(escapemark)
291 nif.n_else.add nbreak
292
293 var method_finish = self.method_finish
294 if method_finish != null then
295 nblock.add v.builder.make_call(iter.make_var_read, method_finish, null)
296 end
297
298 replace_with(nblock)
299 end
300 end
301
302 redef class AWithExpr
303 # is replaced with a do/end and injected calls to `start` and `finish`
304 #
305 # Basically, the following
306 #
307 # ~~~nitish
308 # with expr do
309 # block
310 # end label l
311 # ~~~
312 #
313 # is transformed into
314 #
315 # ~~~nitish
316 # var x = expr
317 # do
318 # x.start
319 # block
320 # end label l
321 # x.finish
322 # ~~~
323 #
324 # The point is that `finish` is called even if the block is escaped.
325 redef fun accept_transform_visitor(v)
326 do
327 var escapemark = self.break_mark
328 assert escapemark != null
329
330 var nblock = v.builder.make_block
331
332 var nexpr = n_expr
333
334 nblock.add nexpr
335
336 var ndo = v.builder.make_do
337 ndo.break_mark = escapemark
338
339 var start = v.builder.make_call(nexpr.make_var_read, method_start.as(not null), null)
340
341 ndo.add start
342
343 ndo.add self.n_block.as(not null)
344
345 nblock.add ndo
346
347 nblock.add v.builder.make_call(nexpr.make_var_read, method_finish.as(not null), null)
348
349 replace_with(nblock)
350 end
351 end
352
353 redef class AArrayExpr
354 # `[x,y]` is replaced with
355 #
356 # ~~~nitish
357 # var t = new Array[X].with_capacity(2)
358 # t.add(x)
359 # t.add(y)
360 # t
361 # ~~~
362 redef fun full_transform_visitor(v)
363 do
364 var nblock = v.builder.make_block
365
366 var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.length)])
367 self.nnew = nnew
368
369 nblock.add nnew
370
371 super
372
373 for nexpr in self.n_exprs do
374 nblock.add nexpr
375 end
376 var nres = nnew.make_var_read
377 nblock.add nres
378
379 replace_with(nblock)
380 end
381
382 private var nnew: ANewExpr is noinit
383 end
384
385 redef class ACrangeExpr
386 # `[x..y]` is replaced with `new Range[X](x,y)`
387 redef fun accept_transform_visitor(v)
388 do
389 if parent isa AForExpr then return # to permit shortcut ranges
390 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
391 end
392 end
393
394 redef class AOrangeExpr
395 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
396 redef fun accept_transform_visitor(v)
397 do
398 if parent isa AForExpr then return # to permit shortcut ranges
399 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
400 end
401 end
402
403 redef class AParExpr
404 # `(x)` is replaced with `x`
405 redef fun accept_transform_visitor(v)
406 do
407 replace_with(n_expr)
408 end
409 end
410
411 redef class ASendReassignFormExpr
412 # `x.foo(y)+=z` is replaced with
413 #
414 # ~~~nitish
415 # x.foo(y) = x.foo(y) + z
416 # ~~~
417 #
418 # witch is, in reality:
419 #
420 # ~~~nitish
421 # x."foo="(y, x.foo(y)."+"(z))
422 # ~~~
423 redef fun accept_transform_visitor(v)
424 do
425 var nblock = v.builder.make_block
426 nblock.add(n_expr)
427
428 var read_args = new Array[AExpr]
429 var write_args = new Array[AExpr]
430 for a in raw_arguments do
431 nblock.add(a)
432 read_args.add(a.make_var_read)
433 write_args.add(a.make_var_read)
434 end
435
436 var nread = v.builder.make_call(n_expr.make_var_read, callsite.as(not null), read_args)
437
438 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
439
440 write_args.add(nnewvalue)
441 var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.as(not null), write_args)
442 nblock.add(nwrite)
443
444 replace_with(nblock)
445 end
446 end
447
448 redef class AVarReassignExpr
449 # `v += z` is replaced with `v = v + z`
450 redef fun accept_transform_visitor(v)
451 do
452 var variable = self.variable.as(not null)
453
454 var nread = v.builder.make_var_read(variable, read_type.as(not null))
455
456 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
457 var nwrite = v.builder.make_var_assign(variable, nnewvalue)
458
459 replace_with(nwrite)
460 end
461 end
462
463 redef class AAttrReassignExpr
464 # `x.a += z` is replaced with `x.a = x.a + z`
465 redef fun accept_transform_visitor(v)
466 do
467 var nblock = v.builder.make_block
468 nblock.add(n_expr)
469 var attribute = self.mproperty.as(not null)
470
471 var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute)
472 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
473 var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
474 nblock.add(nwrite)
475
476 replace_with(nblock)
477 end
478 end