transform: shortcut range
[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 end
27
28 private class TransformPhase
29 super Phase
30
31 redef fun process_npropdef(npropdef: APropdef)
32 do
33 var val
34
35 var v = new TransformVisitor(self, npropdef)
36 v.enter_visit(npropdef)
37
38 val = new ASTValidationVisitor
39 val.enter_visit(npropdef)
40 end
41 end
42
43 private class TransformVisitor
44 super Visitor
45
46 var phase: TransformPhase
47 var mmodule: MModule
48 var mclassdef: MClassDef
49 var mpropdef: MPropDef
50 var builder: ASTBuilder
51
52 init(phase: TransformPhase, npropdef: APropdef)
53 do
54 self.phase = phase
55 self.mpropdef = npropdef.mpropdef.as(not null)
56 self.mclassdef = mpropdef.mclassdef
57 self.mmodule = mclassdef.mmodule
58 self.builder = new ASTBuilder(mmodule, mpropdef.mclassdef.bound_mtype)
59 end
60
61 redef fun visit(node)
62 do
63 if node isa AAnnotations then return
64 node.visit_all(self)
65 node.accept_transform_visitor(self)
66 end
67
68 # Get a primitive class or display a fatal error on `location`.
69 fun get_class(location: AExpr, name: String): MClass
70 do
71 return mmodule.get_primitive_class(name)
72 end
73
74 # Get a primitive method or display a fatal error on `location`.
75 fun get_method(location: AExpr, name: String, recv: MClass): MMethod
76 do
77 return phase.toolcontext.modelbuilder.force_get_primitive_method(location, name, recv, mmodule)
78 end
79 end
80
81 redef class ANode
82 private fun accept_transform_visitor(v: TransformVisitor)
83 do
84 end
85 end
86
87 redef class AVardeclExpr
88 # `var x = y` is replaced with `x = y`
89 #
90 # Declarations are only useful for scope rules
91 # Once names are associated with real objects, ther declaration become useless
92 # Therefore, if there is no initial value, then just ignore it
93 # Else, replace it with a simple assignment
94 redef fun accept_transform_visitor(v)
95 do
96 var nexpr = n_expr
97 if nexpr == null then
98 # do nothing
99 # note: not detached because the collection is currently under iteration
100 else
101 var nvar = v.builder.make_var_assign(self.variable.as(not null), nexpr)
102 replace_with(nvar)
103 end
104 end
105 end
106
107 redef class AIfexprExpr
108 # is replaced with `AIfExpr`
109 # Expression if and statement-if use two distinct classes for historical reasons
110 # However, on can replace the `AIfexprExpr` with the simpler `AIfExpr`
111 redef fun accept_transform_visitor(v)
112 do
113 var nif = v.builder.make_if(n_expr, self.mtype)
114 nif.n_then.add(n_then)
115 nif.n_else.add(n_else)
116
117 replace_with(nif)
118 end
119 end
120
121 redef class AOrExpr
122 # `x or y` is replaced with `if x then x else y`
123 redef fun accept_transform_visitor(v)
124 do
125 var nif = v.builder.make_if(n_expr, self.mtype)
126 nif.n_then.add(n_expr.make_var_read)
127 nif.n_else.add(n_expr2)
128
129 replace_with(nif)
130 end
131 end
132
133 redef class AImpliesExpr
134 redef fun accept_transform_visitor(v)
135 do
136 # TODO
137 end
138 end
139
140 redef class AAndExpr
141 # `x and y` is replaced with `if x then y else x`
142 redef fun accept_transform_visitor(v)
143 do
144 var nif = v.builder.make_if(n_expr, self.mtype)
145 nif.n_then.add(n_expr2)
146 nif.n_else.add(n_expr.make_var_read)
147
148 replace_with(nif)
149 end
150 end
151
152 redef class AWhileExpr
153 redef fun accept_transform_visitor(v)
154 do
155 var nloop = v.builder.make_loop
156 var nif = v.builder.make_if(n_expr, null)
157 nloop.add nif
158
159 var nblock = n_block
160 if nblock != null then nif.n_then.add nblock
161
162 var escapemark = self.break_mark.as(not null)
163 var nbreak = v.builder.make_break(escapemark)
164 nif.n_else.add nbreak
165
166 nloop.break_mark = self.break_mark
167 nloop.continue_mark = self.continue_mark
168
169 replace_with(nloop)
170 end
171 end
172
173 redef class AForExpr
174 redef fun accept_transform_visitor(v)
175 do
176 var escapemark = self.break_mark
177 assert escapemark != null
178
179 var nblock = v.builder.make_block
180
181 var nexpr = n_expr
182
183 # Shortcut on explicit range
184 # Avoid the instantiation of the range and the iterator
185 if self.variables.length == 1 and nexpr isa ARangeExpr then
186 var variable = variables.first
187 nblock.add v.builder.make_var_assign(variable, nexpr.n_expr)
188 var to = nexpr.n_expr2
189 nblock.add to
190
191 var nloop = v.builder.make_loop
192 nloop.break_mark = escapemark
193 nblock.add nloop
194
195 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])
196
197 var nif = v.builder.make_if(is_ok, null)
198 nloop.add nif
199
200 var nthen = nif.n_then
201 var ndo = v.builder.make_do
202 ndo.break_mark = escapemark.continue_mark
203 nthen.add ndo
204
205 ndo.add self.n_block.as(not null)
206
207 var one = v.builder.make_int(1)
208 var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
209 nthen.add v.builder.make_var_assign(variable, succ)
210
211 var nbreak = v.builder.make_break(escapemark)
212 nif.n_else.add nbreak
213
214 replace_with(nblock)
215 return
216 end
217
218 nblock.add nexpr
219
220 var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
221 nblock.add iter
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(iter.make_var_read, method_is_ok.as(not null), null)
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 if self.variables.length == 1 then
238 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
239 ndo.add v.builder.make_var_assign(variables.first, item)
240 else if self.variables.length == 2 then
241 var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
242 ndo.add v.builder.make_var_assign(variables[0], key)
243 var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
244 ndo.add v.builder.make_var_assign(variables[1], item)
245 else
246 abort
247 end
248
249 ndo.add self.n_block.as(not null)
250
251 nthen.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
252
253 var nbreak = v.builder.make_break(escapemark)
254 nif.n_else.add nbreak
255
256 var method_finish = self.method_finish
257 if method_finish != null then
258 nblock.add v.builder.make_call(iter.make_var_read, method_finish, null)
259 end
260
261 replace_with(nblock)
262 end
263 end
264
265 redef class AArrayExpr
266 # `[x,y]` is replaced with
267 #
268 # var t = new Array[X].with_capacity(2)
269 # t.add(x)
270 # t.add(y)
271 # t
272 redef fun accept_transform_visitor(v)
273 do
274 var nblock = v.builder.make_block
275
276 var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.n_exprs.length)])
277 nblock.add nnew
278
279 for nexpr in self.n_exprs.n_exprs do
280 var nadd = v.builder.make_call(nnew.make_var_read, push_callsite.as(not null), [nexpr])
281 nblock.add nadd
282 end
283 var nres = nnew.make_var_read
284 nblock.add nres
285
286 replace_with(nblock)
287 end
288 end
289
290 redef class ACrangeExpr
291 # `[x..y]` is replaced with `new Range[X](x,y)`
292 redef fun accept_transform_visitor(v)
293 do
294 if parent isa AForExpr then return # to permit shortcut ranges
295 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
296 end
297 end
298
299 redef class AOrangeExpr
300 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
301 redef fun accept_transform_visitor(v)
302 do
303 if parent isa AForExpr then return # to permit shortcut ranges
304 replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
305 end
306 end
307
308 redef class AParExpr
309 # `(x)` is replaced with `x`
310 redef fun accept_transform_visitor(v)
311 do
312 replace_with(n_expr)
313 end
314 end
315
316 redef class ASendReassignFormExpr
317 # `x.foo(y)+=z` is replaced with
318 #
319 # x.foo(y) = x.foo(y) + z
320 #
321 # witch is, in reality:
322 #
323 # x."foo="(y, x.foo(y)."+"(z))
324 redef fun accept_transform_visitor(v)
325 do
326 var nblock = v.builder.make_block
327 nblock.add(n_expr)
328
329 var read_args = new Array[AExpr]
330 var write_args = new Array[AExpr]
331 for a in raw_arguments do
332 nblock.add(a)
333 read_args.add(a.make_var_read)
334 write_args.add(a.make_var_read)
335 end
336
337 var nread = v.builder.make_call(n_expr.make_var_read, callsite.as(not null), read_args)
338
339 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
340
341 write_args.add(nnewvalue)
342 var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.as(not null), write_args)
343 nblock.add(nwrite)
344
345 replace_with(nblock)
346 end
347 end
348
349 redef class AVarReassignExpr
350 # `v += z` is replaced with `v = v + z`
351 redef fun accept_transform_visitor(v)
352 do
353 var variable = self.variable.as(not null)
354
355 var nread = v.builder.make_var_read(variable, read_type.as(not null))
356
357 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
358 var nwrite = v.builder.make_var_assign(variable, nnewvalue)
359
360 replace_with(nwrite)
361 end
362 end
363
364 redef class AAttrReassignExpr
365 # `x.a += z` is replaced with `x.a = x.a + z`
366 redef fun accept_transform_visitor(v)
367 do
368 var nblock = v.builder.make_block
369 nblock.add(n_expr)
370 var attribute = self.mproperty.as(not null)
371
372 var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute)
373 var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
374 var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
375 nblock.add(nwrite)
376
377 replace_with(nblock)
378 end
379 end