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