Merge: doc: fixed some typos and other misc. corrections
[nit.git] / src / transform.nit
index dac3c4f..3499d9b 100644 (file)
 module transform
 
 import astbuilder
-import astvalidation
 import semantize
 intrude import semantize::scope
+intrude import semantize::typing
 
 redef class ToolContext
        var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase])
 
        # --no-shortcut-range
-       var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
+       var opt_no_shortcut_range: OptionBool = new OptionBool("Always instantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
 
        redef init
        do
@@ -103,7 +103,21 @@ redef class AExpr
                        var nadd = v.builder.make_call(recv, na.push_callsite.as(not null), [self])
                        place.replace_with(nadd)
                end
+
+               visit_all(v)
+
+               if is_broken then return # Skip broken
+
+               accept_transform_visitor(v)
+       end
+
+       redef fun replace_with(other)
+       do
                super
+               if other isa AExpr then
+                       if other.implicit_cast_to == null then other.implicit_cast_to = implicit_cast_to
+                       other.vararg_decl = vararg_decl
+               end
        end
 end
 
@@ -199,87 +213,146 @@ redef class AForExpr
                var escapemark = self.break_mark
                assert escapemark != null
 
+               # Main block that will contain the whole for and will replace `self`
                var nblock = v.builder.make_block
 
+               # Part before the loop
+               var before = v.builder.make_block
+               nblock.add before
+
+               # The loop
+               var nloop = v.builder.make_loop
+               nloop.break_mark = escapemark
+               nblock.add nloop
+
+               # Part before the body inside the loop
+               var begin = v.builder.make_block
+               nloop.add begin
+
+               # The `do` block with then user code
+               var ndo = v.builder.make_do
+               ndo.break_mark = escapemark.continue_mark
+               nloop.add ndo
+
+               ndo.add self.n_block.as(not null)
+
+               # Fill up each part
+               for g in n_groups do
+                       g.transform_in(v, before, begin, nloop, nblock, escapemark)
+               end
+
+               replace_with(nblock)
+       end
+end
+
+redef class AForGroup
+       private fun transform_in(v: TransformVisitor, before, begin, next, finish: AExpr, escapemark: EscapeMark)
+       do
                var nexpr = n_expr
 
                # Shortcut on explicit range
                # Avoid the instantiation of the range and the iterator
                if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
+                       # Before: evaluate bounds
                        var variable = variables.first
-                       nblock.add v.builder.make_var_assign(variable, nexpr.n_expr)
+                       before.add v.builder.make_var_assign(variable, nexpr.n_expr)
                        var to = nexpr.n_expr2
-                       nblock.add to
-
-                       var nloop = v.builder.make_loop
-                       nloop.break_mark = escapemark
-                       nblock.add nloop
+                       before.add to
 
+                       # Begin: check variable
                        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])
-
                        var nif = v.builder.make_if(is_ok, null)
-                       nloop.add nif
-
-                       var nthen = nif.n_then
-                       var ndo = v.builder.make_do
-                       ndo.break_mark = escapemark.continue_mark
-                       nthen.add ndo
-
-                       ndo.add self.n_block.as(not null)
+                       begin.add nif
+                       nif.n_else.add v.builder.make_break(escapemark)
 
+                       # Next: increment one
                        var one = v.builder.make_int(1)
                        var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
-                       nthen.add v.builder.make_var_assign(variable, succ)
-
-                       var nbreak = v.builder.make_break(escapemark)
-                       nif.n_else.add nbreak
-
-                       replace_with(nblock)
+                       next.add v.builder.make_var_assign(variable, succ)
                        return
                end
 
-               nblock.add nexpr
-
+               # Before: evaluate expr, make the iterator
+               before.add nexpr
                var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
-               nblock.add iter
-
-               var nloop = v.builder.make_loop
-               nloop.break_mark = escapemark
-               nblock.add nloop
+               before.add iter
 
+               # Begin: check iterator `is_ok`
                var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
-
                var nif = v.builder.make_if(is_ok, null)
-               nloop.add nif
+               begin.add nif
+               nif.n_else.add v.builder.make_break(escapemark)
 
-               var nthen = nif.n_then
-               var ndo = v.builder.make_do
-               ndo.break_mark = escapemark.continue_mark
-               nthen.add ndo
-
-               if self.variables.length == 1 then
+               # Begin: assign automatic variables
+               if variables.length == 1 then
                        var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
-                       ndo.add v.builder.make_var_assign(variables.first, item)
-               else if self.variables.length == 2 then
+                       begin.add v.builder.make_var_assign(variables.first, item)
+               else if variables.length == 2 then
                        var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
-                       ndo.add v.builder.make_var_assign(variables[0], key)
+                       begin.add v.builder.make_var_assign(variables[0], key)
                        var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
-                       ndo.add v.builder.make_var_assign(variables[1], item)
+                       begin.add v.builder.make_var_assign(variables[1], item)
                else
                        abort
                end
 
-               ndo.add self.n_block.as(not null)
-
-               nthen.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
+               # Next: call next
+               next.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
 
-               var nbreak = v.builder.make_break(escapemark)
-               nif.n_else.add nbreak
-
-               var method_finish = self.method_finish
+               # Finish: call finish
+               var method_finish = method_finish
                if method_finish != null then
-                       nblock.add v.builder.make_call(iter.make_var_read, method_finish, null)
+                       finish.add v.builder.make_call(iter.make_var_read, method_finish, null)
                end
+       end
+end
+
+redef class AWithExpr
+       # is replaced with a do/end and injected calls to `start` and `finish`
+       #
+       # Basically, the following
+       #
+       # ~~~nitish
+       # with expr do
+       #   block
+       # end label l
+       # ~~~
+       #
+       # is transformed into
+       #
+       # ~~~nitish
+       # var x = expr
+       # do
+       #   x.start
+       #   block
+       # end label l
+       # x.finish
+       # ~~~
+       #
+       # The point is that `finish` is called even if the block is escaped.
+       redef fun accept_transform_visitor(v)
+       do
+               var escapemark = self.break_mark
+               assert escapemark != null
+
+               var nblock = v.builder.make_block
+
+               var nexpr = n_expr
+
+               nblock.add nexpr
+
+               var ndo = v.builder.make_do
+               ndo.break_mark = escapemark
+
+               var start = v.builder.make_call(nexpr.make_var_read, method_start.as(not null), null)
+
+               ndo.add start
+
+               ndo.add self.n_block.as(not null)
+
+               nblock.add ndo
+
+               nblock.add v.builder.make_call(nexpr.make_var_read, method_finish.as(not null), null)
 
                replace_with(nblock)
        end
@@ -296,6 +369,8 @@ redef class AArrayExpr
        # ~~~
        redef fun full_transform_visitor(v)
        do
+               if is_broken then return # Skip broken
+
                var nblock = v.builder.make_block
 
                var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.length)])
@@ -321,7 +396,7 @@ redef class ACrangeExpr
        # `[x..y]` is replaced with `new Range[X](x,y)`
        redef fun accept_transform_visitor(v)
        do
-               if parent isa AForExpr then return # to permit shortcut ranges
+               if parent isa AForGroup then return # to permit shortcut ranges
                replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
        end
 end
@@ -330,7 +405,7 @@ redef class AOrangeExpr
        # `[x..y[` is replaced with `new Range[X].without_last(x,y)`
        redef fun accept_transform_visitor(v)
        do
-               if parent isa AForExpr then return # to permit shortcut ranges
+               if parent isa AForGroup then return # to permit shortcut ranges
                replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
        end
 end