3e5865bf11af7435b9f70739a71a9756659391e2
[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 is_broken 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 other.vararg_decl = vararg_decl
121 end
122 end
123 end
124
125 redef class AVardeclExpr
126 # `var x = y` is replaced with `x = y`
127 #
128 # Declarations are only useful for scope rules
129 # Once names are associated with real objects, ther declaration become useless
130 # Therefore, if there is no initial value, then just ignore it
131 # Else, replace it with a simple assignment
132 redef fun accept_transform_visitor(v)
133 do
134 var nexpr = n_expr
135 if nexpr == null then
136 # do nothing
137 # note: not detached because the collection is currently under iteration
138 else
139 var nvar = v.builder.make_var_assign(self.variable.as(not null), nexpr)
140 replace_with(nvar)
141 end
142 end
143 end
144
145 redef class AIfexprExpr
146 # is replaced with `AIfExpr`
147 # Expression if and statement-if use two distinct classes for historical reasons
148 # However, on can replace the `AIfexprExpr` with the simpler `AIfExpr`
149 redef fun accept_transform_visitor(v)
150 do
151 var nif = v.builder.make_if(n_expr, self.mtype)
152 nif.n_then.add(n_then)
153 nif.n_else.add(n_else)
154
155 replace_with(nif)
156 end
157 end
158
159 redef class AOrExpr
160 # `x or y` is replaced with `if x then x else y`
161 redef fun accept_transform_visitor(v)
162 do
163 var nif = v.builder.make_if(n_expr, self.mtype)
164 nif.n_then.add(n_expr.make_var_read)
165 nif.n_else.add(n_expr2)
166
167 replace_with(nif)
168 end
169 end
170
171 redef class AImpliesExpr
172 redef fun accept_transform_visitor(v)
173 do
174 # TODO
175 end
176 end
177
178 redef class AAndExpr
179 # `x and y` is replaced with `if x then y else x`
180 redef fun accept_transform_visitor(v)
181 do
182 var nif = v.builder.make_if(n_expr, self.mtype)
183 nif.n_then.add(n_expr2)
184 nif.n_else.add(n_expr.make_var_read)
185
186 replace_with(nif)
187 end
188 end
189
190 redef class AWhileExpr
191 redef fun accept_transform_visitor(v)
192 do
193 var nloop = v.builder.make_loop
194 var nif = v.builder.make_if(n_expr, null)
195 nloop.add nif
196
197 var nblock = n_block
198 if nblock != null then nif.n_then.add nblock
199
200 var escapemark = self.break_mark.as(not null)
201 var nbreak = v.builder.make_break(escapemark)
202 nif.n_else.add nbreak
203
204 nloop.break_mark = self.break_mark
205 nloop.continue_mark = self.continue_mark
206
207 replace_with(nloop)
208 end
209 end
210
211 redef class AForExpr
212 redef fun accept_transform_visitor(v)
213 do
214 var escapemark = self.break_mark
215 assert escapemark != null
216
217 # Main block that will contain the whole for and will replace `self`
218 var nblock = v.builder.make_block
219
220 # Part before the loop
221 var before = v.builder.make_block
222 nblock.add before
223
224 # The loop
225 var nloop = v.builder.make_loop
226 nloop.break_mark = escapemark
227 nblock.add nloop
228
229 # Part before the body inside the loop
230 var begin = v.builder.make_block
231 nloop.add begin
232
233 # The `do` block with then user code
234 var ndo = v.builder.make_do
235 ndo.break_mark = escapemark.continue_mark
236 nloop.add ndo
237
238 ndo.add self.n_block.as(not null)
239
240 # Fill up each part
241 for g in n_groups do
242 g.transform_in(v, before, begin, nloop, nblock, escapemark)
243 end
244
245 replace_with(nblock)
246 end
247 end
248
249 redef class AForGroup
250 private fun transform_in(v: TransformVisitor, before, begin, next, finish: AExpr, escapemark: EscapeMark)
251 do
252 var nexpr = n_expr
253
254 # Shortcut on explicit range
255 # Avoid the instantiation of the range and the iterator
256 if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
257 # Before: evaluate bounds
258 var variable = variables.first
259 before.add v.builder.make_var_assign(variable, nexpr.n_expr)
260 var to = nexpr.n_expr2
261 before.add to
262
263 # Begin: check variable
264 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])
265 var nif = v.builder.make_if(is_ok, null)
266 begin.add nif
267 nif.n_else.add v.builder.make_break(escapemark)
268
269 # Next: increment one
270 var one = v.builder.make_int(1)
271 var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
272 next.add v.builder.make_var_assign(variable, succ)
273 return
274 end
275
276 # Before: evaluate expr, make the iterator
277 before.add nexpr
278 var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
279 before.add iter
280
281 # Begin: check iterator `is_ok`
282 var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
283 var nif = v.builder.make_if(is_ok, null)
284 begin.add nif
285 nif.n_else.add v.builder.make_break(escapemark)
286
287 # Begin: assign automatic variables
288 if variables.length == 1 then
289 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
290 begin.add v.builder.make_var_assign(variables.first, item)
291 else if variables.length == 2 then
292 var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
293 begin.add v.builder.make_var_assign(variables[0], key)
294 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
295 begin.add v.builder.make_var_assign(variables[1], item)
296 else
297 abort
298 end
299
300 # Next: call next
301 next.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
302
303 # Finish: call finish
304 var method_finish = method_finish
305 if method_finish != null then
306 finish.add v.builder.make_call(iter.make_var_read, method_finish, null)
307 end
308 end
309 end
310
311 redef class AWithExpr
312 # is replaced with a do/end and injected calls to `start` and `finish`
313 #
314 # Basically, the following
315 #
316 # ~~~nitish
317 # with expr do
318 # block
319 # end label l
320 # ~~~
321 #
322 # is transformed into
323 #
324 # ~~~nitish
325 # var x = expr
326 # do
327 # x.start
328 # block
329 # end label l
330 # x.finish
331 # ~~~
332 #
333 # The point is that `finish` is called even if the block is escaped.
334 redef fun accept_transform_visitor(v)
335 do
336 var escapemark = self.break_mark
337 assert escapemark != null
338
339 var nblock = v.builder.make_block
340
341 var nexpr = n_expr
342
343 nblock.add nexpr
344
345 var ndo = v.builder.make_do
346 ndo.break_mark = escapemark
347
348 var start = v.builder.make_call(nexpr.make_var_read, method_start.as(not null), null)
349
350 ndo.add start
351
352 ndo.add self.n_block.as(not null)
353
354 nblock.add ndo
355
356 nblock.add v.builder.make_call(nexpr.make_var_read, method_finish.as(not null), null)
357
358 replace_with(nblock)
359 end
360 end
361
362 redef class AArrayExpr
363 # `[x,y]` is replaced with
364 #
365 # ~~~nitish
366 # var t = new Array[X].with_capacity(2)
367 # t.add(x)
368 # t.add(y)
369 # t
370 # ~~~
371 redef fun full_transform_visitor(v)
372 do
373 if is_broken then return # Skip broken
374
375 var nblock = v.builder.make_block
376
377 var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.length)])
378 self.nnew = nnew
379
380 nblock.add nnew
381
382 super
383
384 for nexpr in self.n_exprs do
385 nblock.add nexpr
386 end
387 var nres = nnew.make_var_read
388 nblock.add nres
389
390 replace_with(nblock)
391 end
392
393 private var nnew: ANewExpr is noinit
394 end
395
396 redef class ACrangeExpr
397 # `[x..y]` is replaced with `new Range[X](x,y)`
398 redef fun accept_transform_visitor(v)
399 do
400 if parent isa AForGroup then return # to permit shortcut ranges
401 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
402 end
403 end
404
405 redef class AOrangeExpr
406 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
407 redef fun accept_transform_visitor(v)
408 do
409 if parent isa AForGroup then return # to permit shortcut ranges
410 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
411 end
412 end
413
414 redef class AParExpr
415 # `(x)` is replaced with `x`
416 redef fun accept_transform_visitor(v)
417 do
418 replace_with(n_expr)
419 end
420 end
421
422 redef class ASendReassignFormExpr
423 # `x.foo(y)+=z` is replaced with
424 #
425 # ~~~nitish
426 # x.foo(y) = x.foo(y) + z
427 # ~~~
428 #
429 # witch is, in reality:
430 #
431 # ~~~nitish
432 # x."foo="(y, x.foo(y)."+"(z))
433 # ~~~
434 redef fun accept_transform_visitor(v)
435 do
436 var nblock = v.builder.make_block
437 nblock.add(n_expr)
438
439 var read_args = new Array[AExpr]
440 var write_args = new Array[AExpr]
441 for a in raw_arguments do
442 nblock.add(a)
443 read_args.add(a.make_var_read)
444 write_args.add(a.make_var_read)
445 end
446
447 var nread = v.builder.make_call(n_expr.make_var_read, callsite.as(not null), read_args)
448
449 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
450
451 write_args.add(nnewvalue)
452 var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.as(not null), write_args)
453 nblock.add(nwrite)
454
455 replace_with(nblock)
456 end
457 end
458
459 redef class AVarReassignExpr
460 # `v += z` is replaced with `v = v + z`
461 redef fun accept_transform_visitor(v)
462 do
463 var variable = self.variable.as(not null)
464
465 var nread = v.builder.make_var_read(variable, read_type.as(not null))
466
467 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
468 var nwrite = v.builder.make_var_assign(variable, nnewvalue)
469
470 replace_with(nwrite)
471 end
472 end
473
474 redef class AAttrReassignExpr
475 # `x.a += z` is replaced with `x.a = x.a + z`
476 redef fun accept_transform_visitor(v)
477 do
478 var nblock = v.builder.make_block
479 nblock.add(n_expr)
480 var attribute = self.mproperty.as(not null)
481
482 var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute)
483 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
484 var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
485 nblock.add(nwrite)
486
487 replace_with(nblock)
488 end
489 end