Merge: doc: fixed some typos and other misc. corrections
[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 semantize
21 intrude import semantize::scope
22 intrude import semantize::typing
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 instantiate 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 m = npropdef.mpropdef
45 if m == null then return
46 var v = new TransformVisitor(self, m)
47 v.enter_visit(npropdef)
48
49 val = new ASTValidationVisitor
50 val.enter_visit(npropdef)
51 end
52 end
53
54 private class TransformVisitor
55 super Visitor
56
57 var phase: TransformPhase
58 var mmodule: MModule is noinit
59 var mclassdef: MClassDef is noinit
60 var mpropdef: MPropDef
61 var builder: ASTBuilder is noinit
62
63 init
64 do
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.full_transform_visitor(self)
74 end
75
76 # Get a primitive method or display a fatal error on `location`.
77 fun get_method(location: AExpr, name: String, recv: MClass): MMethod
78 do
79 return phase.toolcontext.modelbuilder.force_get_primitive_method(location, name, recv, mmodule)
80 end
81 end
82
83 redef class ANode
84 private fun full_transform_visitor(v: TransformVisitor)
85 do
86 visit_all(v)
87 accept_transform_visitor(v)
88 end
89 private fun accept_transform_visitor(v: TransformVisitor)
90 do
91 end
92 end
93
94 redef class AExpr
95 redef fun full_transform_visitor(v: TransformVisitor)
96 do
97 var na = comprehension
98 if na != null then
99 # We are building a comprehension array `array`
100 # Replace `self` with `array.push(self)`
101 var place = detach_with_placeholder
102 var recv = na.nnew.make_var_read
103 var nadd = v.builder.make_call(recv, na.push_callsite.as(not null), [self])
104 place.replace_with(nadd)
105 end
106
107 visit_all(v)
108
109 if is_broken then return # Skip broken
110
111 accept_transform_visitor(v)
112 end
113
114 redef fun replace_with(other)
115 do
116 super
117 if other isa AExpr then
118 if other.implicit_cast_to == null then other.implicit_cast_to = implicit_cast_to
119 other.vararg_decl = vararg_decl
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 # Main block that will contain the whole for and will replace `self`
217 var nblock = v.builder.make_block
218
219 # Part before the loop
220 var before = v.builder.make_block
221 nblock.add before
222
223 # The loop
224 var nloop = v.builder.make_loop
225 nloop.break_mark = escapemark
226 nblock.add nloop
227
228 # Part before the body inside the loop
229 var begin = v.builder.make_block
230 nloop.add begin
231
232 # The `do` block with then user code
233 var ndo = v.builder.make_do
234 ndo.break_mark = escapemark.continue_mark
235 nloop.add ndo
236
237 ndo.add self.n_block.as(not null)
238
239 # Fill up each part
240 for g in n_groups do
241 g.transform_in(v, before, begin, nloop, nblock, escapemark)
242 end
243
244 replace_with(nblock)
245 end
246 end
247
248 redef class AForGroup
249 private fun transform_in(v: TransformVisitor, before, begin, next, finish: AExpr, escapemark: EscapeMark)
250 do
251 var nexpr = n_expr
252
253 # Shortcut on explicit range
254 # Avoid the instantiation of the range and the iterator
255 if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
256 # Before: evaluate bounds
257 var variable = variables.first
258 before.add v.builder.make_var_assign(variable, nexpr.n_expr)
259 var to = nexpr.n_expr2
260 before.add to
261
262 # Begin: check variable
263 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])
264 var nif = v.builder.make_if(is_ok, null)
265 begin.add nif
266 nif.n_else.add v.builder.make_break(escapemark)
267
268 # Next: increment one
269 var one = v.builder.make_int(1)
270 var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
271 next.add v.builder.make_var_assign(variable, succ)
272 return
273 end
274
275 # Before: evaluate expr, make the iterator
276 before.add nexpr
277 var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
278 before.add iter
279
280 # Begin: check iterator `is_ok`
281 var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
282 var nif = v.builder.make_if(is_ok, null)
283 begin.add nif
284 nif.n_else.add v.builder.make_break(escapemark)
285
286 # Begin: assign automatic variables
287 if variables.length == 1 then
288 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
289 begin.add v.builder.make_var_assign(variables.first, item)
290 else if variables.length == 2 then
291 var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
292 begin.add v.builder.make_var_assign(variables[0], key)
293 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
294 begin.add v.builder.make_var_assign(variables[1], item)
295 else
296 abort
297 end
298
299 # Next: call next
300 next.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
301
302 # Finish: call finish
303 var method_finish = method_finish
304 if method_finish != null then
305 finish.add v.builder.make_call(iter.make_var_read, method_finish, null)
306 end
307 end
308 end
309
310 redef class AWithExpr
311 # is replaced with a do/end and injected calls to `start` and `finish`
312 #
313 # Basically, the following
314 #
315 # ~~~nitish
316 # with expr do
317 # block
318 # end label l
319 # ~~~
320 #
321 # is transformed into
322 #
323 # ~~~nitish
324 # var x = expr
325 # do
326 # x.start
327 # block
328 # end label l
329 # x.finish
330 # ~~~
331 #
332 # The point is that `finish` is called even if the block is escaped.
333 redef fun accept_transform_visitor(v)
334 do
335 var escapemark = self.break_mark
336 assert escapemark != null
337
338 var nblock = v.builder.make_block
339
340 var nexpr = n_expr
341
342 nblock.add nexpr
343
344 var ndo = v.builder.make_do
345 ndo.break_mark = escapemark
346
347 var start = v.builder.make_call(nexpr.make_var_read, method_start.as(not null), null)
348
349 ndo.add start
350
351 ndo.add self.n_block.as(not null)
352
353 nblock.add ndo
354
355 nblock.add v.builder.make_call(nexpr.make_var_read, method_finish.as(not null), null)
356
357 replace_with(nblock)
358 end
359 end
360
361 redef class AArrayExpr
362 # `[x,y]` is replaced with
363 #
364 # ~~~nitish
365 # var t = new Array[X].with_capacity(2)
366 # t.add(x)
367 # t.add(y)
368 # t
369 # ~~~
370 redef fun full_transform_visitor(v)
371 do
372 if is_broken then return # Skip broken
373
374 var nblock = v.builder.make_block
375
376 var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.length)])
377 self.nnew = nnew
378
379 nblock.add nnew
380
381 super
382
383 for nexpr in self.n_exprs do
384 nblock.add nexpr
385 end
386 var nres = nnew.make_var_read
387 nblock.add nres
388
389 replace_with(nblock)
390 end
391
392 private var nnew: ANewExpr is noinit
393 end
394
395 redef class ACrangeExpr
396 # `[x..y]` is replaced with `new Range[X](x,y)`
397 redef fun accept_transform_visitor(v)
398 do
399 if parent isa AForGroup then return # to permit shortcut ranges
400 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
401 end
402 end
403
404 redef class AOrangeExpr
405 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
406 redef fun accept_transform_visitor(v)
407 do
408 if parent isa AForGroup then return # to permit shortcut ranges
409 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
410 end
411 end
412
413 redef class AParExpr
414 # `(x)` is replaced with `x`
415 redef fun accept_transform_visitor(v)
416 do
417 replace_with(n_expr)
418 end
419 end
420
421 redef class ASendReassignFormExpr
422 # `x.foo(y)+=z` is replaced with
423 #
424 # ~~~nitish
425 # x.foo(y) = x.foo(y) + z
426 # ~~~
427 #
428 # witch is, in reality:
429 #
430 # ~~~nitish
431 # x."foo="(y, x.foo(y)."+"(z))
432 # ~~~
433 redef fun accept_transform_visitor(v)
434 do
435 var nblock = v.builder.make_block
436 nblock.add(n_expr)
437
438 var read_args = new Array[AExpr]
439 var write_args = new Array[AExpr]
440 for a in raw_arguments do
441 nblock.add(a)
442 read_args.add(a.make_var_read)
443 write_args.add(a.make_var_read)
444 end
445
446 var nread = v.builder.make_call(n_expr.make_var_read, callsite.as(not null), read_args)
447
448 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
449
450 write_args.add(nnewvalue)
451 var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.as(not null), write_args)
452 nblock.add(nwrite)
453
454 replace_with(nblock)
455 end
456 end
457
458 redef class AVarReassignExpr
459 # `v += z` is replaced with `v = v + z`
460 redef fun accept_transform_visitor(v)
461 do
462 var variable = self.variable.as(not null)
463
464 var nread = v.builder.make_var_read(variable, read_type.as(not null))
465
466 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
467 var nwrite = v.builder.make_var_assign(variable, nnewvalue)
468
469 replace_with(nwrite)
470 end
471 end
472
473 redef class AAttrReassignExpr
474 # `x.a += z` is replaced with `x.a = x.a + z`
475 redef fun accept_transform_visitor(v)
476 do
477 var nblock = v.builder.make_block
478 nblock.add(n_expr)
479 var attribute = self.mproperty.as(not null)
480
481 var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute)
482 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
483 var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
484 nblock.add(nwrite)
485
486 replace_with(nblock)
487 end
488 end