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