1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Thansformations that simplify the AST of expressions
16 # This module transform complex AST `AExpr` nodes into simplier ones
22 intrude import semantize
::scope
23 intrude import semantize
::typing
25 redef class ToolContext
26 var transform_phase
: Phase = new TransformPhase(self, [typing_phase
, auto_super_init_phase
])
29 var opt_no_shortcut_range
: OptionBool = new OptionBool("Always instantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
34 self.option_context
.add_option
(self.opt_no_shortcut_range
)
38 private class TransformPhase
41 redef fun process_npropdef
(npropdef
: APropdef)
45 var m
= npropdef
.mpropdef
46 if m
== null then return
47 var v
= new TransformVisitor(self, m
)
48 v
.enter_visit
(npropdef
)
50 val
= new ASTValidationVisitor
51 val
.enter_visit
(npropdef
)
55 private class TransformVisitor
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
66 self.mclassdef
= mpropdef
.mclassdef
67 self.mmodule
= mclassdef
.mmodule
68 self.builder
= new ASTBuilder(mmodule
, mpropdef
.mclassdef
.bound_mtype
)
73 if node
isa AAnnotations then return
74 node
.full_transform_visitor
(self)
77 # Get a primitive method or display a fatal error on `location`.
78 fun get_method
(location
: AExpr, name
: String, recv
: MClass): MMethod
80 return phase
.toolcontext
.modelbuilder
.force_get_primitive_method
(location
, name
, recv
, mmodule
)
85 private fun full_transform_visitor
(v
: TransformVisitor)
88 accept_transform_visitor
(v
)
90 private fun accept_transform_visitor
(v
: TransformVisitor)
96 redef fun full_transform_visitor
(v
: TransformVisitor)
98 var na
= comprehension
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
)
110 if is_broken
then return # Skip broken
112 accept_transform_visitor
(v
)
115 redef fun replace_with
(other
)
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
125 redef class AVardeclExpr
126 # `var x = y` is replaced with `x = y`
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
)
135 if nexpr
== null then
137 # note: not detached because the collection is currently under iteration
139 var nvar
= v
.builder
.make_var_assign
(self.variable
.as(not null), nexpr
)
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
)
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
)
160 # `x or y` is replaced with `if x then x else y`
161 redef fun accept_transform_visitor
(v
)
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
)
171 redef class AImpliesExpr
172 redef fun accept_transform_visitor
(v
)
179 # `x and y` is replaced with `if x then y else x`
180 redef fun accept_transform_visitor
(v
)
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
)
190 redef class AWhileExpr
191 redef fun accept_transform_visitor
(v
)
193 var nloop
= v
.builder
.make_loop
194 var nif
= v
.builder
.make_if
(n_expr
, null)
198 if nblock
!= null then nif
.n_then
.add nblock
200 var escapemark
= self.break_mark
.as(not null)
201 var nbreak
= v
.builder
.make_break
(escapemark
)
202 nif
.n_else
.add nbreak
204 nloop
.break_mark
= self.break_mark
205 nloop
.continue_mark
= self.continue_mark
212 redef fun accept_transform_visitor
(v
)
214 var escapemark
= self.break_mark
215 assert escapemark
!= null
217 # Main block that will contain the whole for and will replace `self`
218 var nblock
= v
.builder
.make_block
220 # Part before the loop
221 var before
= v
.builder
.make_block
225 var nloop
= v
.builder
.make_loop
226 nloop
.break_mark
= escapemark
229 # Part before the body inside the loop
230 var begin
= v
.builder
.make_block
233 # The `do` block with then user code
234 var ndo
= v
.builder
.make_do
235 ndo
.break_mark
= escapemark
.continue_mark
238 ndo
.add
self.n_block
.as(not null)
242 g
.transform_in
(v
, before
, begin
, nloop
, nblock
, escapemark
)
249 redef class AForGroup
250 private fun transform_in
(v
: TransformVisitor, before
, begin
, next
, finish
: AExpr, escapemark
: EscapeMark)
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
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)
267 nif
.n_else
.add v
.builder
.make_break
(escapemark
)
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
)
276 # Before: evaluate expr, make the iterator
278 var iter
= v
.builder
.make_call
(nexpr
.make_var_read
, method_iterator
.as(not null), null)
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)
285 nif
.n_else
.add v
.builder
.make_break
(escapemark
)
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
)
301 next
.add v
.builder
.make_call
(iter
.make_var_read
, method_next
.as(not null), null)
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)
311 redef class AWithExpr
312 # is replaced with a do/end and injected calls to `start` and `finish`
314 # Basically, the following
322 # is transformed into
333 # The point is that `finish` is called even if the block is escaped.
334 redef fun accept_transform_visitor
(v
)
336 var escapemark
= self.break_mark
337 assert escapemark
!= null
339 var nblock
= v
.builder
.make_block
345 var ndo
= v
.builder
.make_do
346 ndo
.break_mark
= escapemark
348 var start
= v
.builder
.make_call
(nexpr
.make_var_read
, method_start
.as(not null), null)
352 ndo
.add
self.n_block
.as(not null)
356 nblock
.add v
.builder
.make_call
(nexpr
.make_var_read
, method_finish
.as(not null), null)
362 redef class AArrayExpr
363 # `[x,y]` is replaced with
366 # var t = new Array[X].with_capacity(2)
371 redef fun full_transform_visitor
(v
)
373 if is_broken
then return # Skip broken
375 var nblock
= v
.builder
.make_block
377 var nnew
= v
.builder
.make_new
(with_capacity_callsite
.as(not null), [v
.builder
.make_int
(n_exprs
.length
)])
384 for nexpr
in self.n_exprs
do
387 var nres
= nnew
.make_var_read
393 private var nnew
: ANewExpr is noinit
396 redef class ACrangeExpr
397 # `[x..y]` is replaced with `new Range[X](x,y)`
398 redef fun accept_transform_visitor
(v
)
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
]))
405 redef class AOrangeExpr
406 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
407 redef fun accept_transform_visitor
(v
)
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
]))
415 # `(x)` is replaced with `x`
416 redef fun accept_transform_visitor
(v
)
422 redef class ASendReassignFormExpr
423 # `x.foo(y)+=z` is replaced with
426 # x.foo(y) = x.foo(y) + z
429 # witch is, in reality:
432 # x."foo="(y, x.foo(y)."+"(z))
434 redef fun accept_transform_visitor
(v
)
436 var nblock
= v
.builder
.make_block
439 var read_args
= new Array[AExpr]
440 var write_args
= new Array[AExpr]
441 for a
in raw_arguments
do
443 read_args
.add
(a
.make_var_read
)
444 write_args
.add
(a
.make_var_read
)
447 var nread
= v
.builder
.make_call
(n_expr
.make_var_read
, callsite
.as(not null), read_args
)
449 var nnewvalue
= v
.builder
.make_call
(nread
, reassign_callsite
.as(not null), [n_value
])
451 write_args
.add
(nnewvalue
)
452 var nwrite
= v
.builder
.make_call
(n_expr
.make_var_read
, write_callsite
.as(not null), write_args
)
459 redef class AVarReassignExpr
460 # `v += z` is replaced with `v = v + z`
461 redef fun accept_transform_visitor
(v
)
463 var variable
= self.variable
.as(not null)
465 var nread
= v
.builder
.make_var_read
(variable
, read_type
.as(not null))
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
)
474 redef class AAttrReassignExpr
475 # `x.a += z` is replaced with `x.a = x.a + z`
476 redef fun accept_transform_visitor
(v
)
478 var nblock
= v
.builder
.make_block
480 var attribute
= self.mproperty
.as(not null)
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
)