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