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
24 redef class ToolContext
25 var transform_phase
: Phase = new TransformPhase(self, [typing_phase
, auto_super_init_phase
])
28 var opt_no_shortcut_range
: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
33 self.option_context
.add_option
(self.opt_no_shortcut_range
)
37 private class TransformPhase
40 redef fun process_npropdef
(npropdef
: APropdef)
44 var m
= npropdef
.mpropdef
45 if m
== null then return
46 var v
= new TransformVisitor(self, m
)
47 v
.enter_visit
(npropdef
)
49 val
= new ASTValidationVisitor
50 val
.enter_visit
(npropdef
)
54 private class TransformVisitor
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
65 self.mclassdef
= mpropdef
.mclassdef
66 self.mmodule
= mclassdef
.mmodule
67 self.builder
= new ASTBuilder(mmodule
, mpropdef
.mclassdef
.bound_mtype
)
72 if node
isa AAnnotations then return
73 node
.full_transform_visitor
(self)
76 # Get a primitive class or display a fatal error on `location`.
77 fun get_class
(location
: AExpr, name
: String): MClass
79 return mmodule
.get_primitive_class
(name
)
82 # Get a primitive method or display a fatal error on `location`.
83 fun get_method
(location
: AExpr, name
: String, recv
: MClass): MMethod
85 return phase
.toolcontext
.modelbuilder
.force_get_primitive_method
(location
, name
, recv
, mmodule
)
90 private fun full_transform_visitor
(v
: TransformVisitor)
93 accept_transform_visitor
(v
)
95 private fun accept_transform_visitor
(v
: TransformVisitor)
101 redef fun full_transform_visitor
(v
: TransformVisitor)
103 var na
= comprehension
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
)
116 redef class AVardeclExpr
117 # `var x = y` is replaced with `x = y`
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
)
126 if nexpr
== null then
128 # note: not detached because the collection is currently under iteration
130 var nvar
= v
.builder
.make_var_assign
(self.variable
.as(not null), nexpr
)
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
)
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
)
151 # `x or y` is replaced with `if x then x else y`
152 redef fun accept_transform_visitor
(v
)
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
)
162 redef class AImpliesExpr
163 redef fun accept_transform_visitor
(v
)
170 # `x and y` is replaced with `if x then y else x`
171 redef fun accept_transform_visitor
(v
)
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
)
181 redef class AWhileExpr
182 redef fun accept_transform_visitor
(v
)
184 var nloop
= v
.builder
.make_loop
185 var nif
= v
.builder
.make_if
(n_expr
, null)
189 if nblock
!= null then nif
.n_then
.add nblock
191 var escapemark
= self.break_mark
.as(not null)
192 var nbreak
= v
.builder
.make_break
(escapemark
)
193 nif
.n_else
.add nbreak
195 nloop
.break_mark
= self.break_mark
196 nloop
.continue_mark
= self.continue_mark
203 redef fun accept_transform_visitor
(v
)
205 var escapemark
= self.break_mark
206 assert escapemark
!= null
208 var nblock
= v
.builder
.make_block
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
220 var nloop
= v
.builder
.make_loop
221 nloop
.break_mark
= escapemark
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
])
226 var nif
= v
.builder
.make_if
(is_ok
, null)
229 var nthen
= nif
.n_then
230 var ndo
= v
.builder
.make_do
231 ndo
.break_mark
= escapemark
.continue_mark
234 ndo
.add
self.n_block
.as(not null)
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
)
240 var nbreak
= v
.builder
.make_break
(escapemark
)
241 nif
.n_else
.add nbreak
249 var iter
= v
.builder
.make_call
(nexpr
.make_var_read
, method_iterator
.as(not null), null)
252 var nloop
= v
.builder
.make_loop
253 nloop
.break_mark
= escapemark
256 var is_ok
= v
.builder
.make_call
(iter
.make_var_read
, method_is_ok
.as(not null), null)
258 var nif
= v
.builder
.make_if
(is_ok
, null)
261 var nthen
= nif
.n_then
262 var ndo
= v
.builder
.make_do
263 ndo
.break_mark
= escapemark
.continue_mark
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
)
278 ndo
.add
self.n_block
.as(not null)
280 nthen
.add v
.builder
.make_call
(iter
.make_var_read
, method_next
.as(not null), null)
282 var nbreak
= v
.builder
.make_break
(escapemark
)
283 nif
.n_else
.add nbreak
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)
294 redef class AArrayExpr
295 # `[x,y]` is replaced with
298 # var t = new Array[X].with_capacity(2)
303 redef fun full_transform_visitor
(v
)
305 var nblock
= v
.builder
.make_block
307 var nnew
= v
.builder
.make_new
(with_capacity_callsite
.as(not null), [v
.builder
.make_int
(n_exprs
.length
)])
314 for nexpr
in self.n_exprs
do
317 var nres
= nnew
.make_var_read
323 private var nnew
: ANewExpr is noinit
326 redef class ACrangeExpr
327 # `[x..y]` is replaced with `new Range[X](x,y)`
328 redef fun accept_transform_visitor
(v
)
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
]))
335 redef class AOrangeExpr
336 # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
337 redef fun accept_transform_visitor
(v
)
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
]))
345 # `(x)` is replaced with `x`
346 redef fun accept_transform_visitor
(v
)
352 redef class ASendReassignFormExpr
353 # `x.foo(y)+=z` is replaced with
356 # x.foo(y) = x.foo(y) + z
359 # witch is, in reality:
362 # x."foo="(y, x.foo(y)."+"(z))
364 redef fun accept_transform_visitor
(v
)
366 var nblock
= v
.builder
.make_block
369 var read_args
= new Array[AExpr]
370 var write_args
= new Array[AExpr]
371 for a
in raw_arguments
do
373 read_args
.add
(a
.make_var_read
)
374 write_args
.add
(a
.make_var_read
)
377 var nread
= v
.builder
.make_call
(n_expr
.make_var_read
, callsite
.as(not null), read_args
)
379 var nnewvalue
= v
.builder
.make_call
(nread
, reassign_callsite
.as(not null), [n_value
])
381 write_args
.add
(nnewvalue
)
382 var nwrite
= v
.builder
.make_call
(n_expr
.make_var_read
, write_callsite
.as(not null), write_args
)
389 redef class AVarReassignExpr
390 # `v += z` is replaced with `v = v + z`
391 redef fun accept_transform_visitor
(v
)
393 var variable
= self.variable
.as(not null)
395 var nread
= v
.builder
.make_var_read
(variable
, read_type
.as(not null))
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
)
404 redef class AAttrReassignExpr
405 # `x.a += z` is replaced with `x.a = x.a + z`
406 redef fun accept_transform_visitor
(v
)
408 var nblock
= v
.builder
.make_block
410 var attribute
= self.mproperty
.as(not null)
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
)