Rename REAMDE to README.md
[nit.git] / src / transform.nit
index 2a4a230..ebe2c73 100644 (file)
@@ -20,9 +20,19 @@ 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 instantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
+
+       redef init
+       do
+               super
+               self.option_context.add_option(self.opt_no_shortcut_range)
+       end
 end
 
 private class TransformPhase
@@ -32,7 +42,9 @@ private class TransformPhase
        do
                var val
 
-               var v = new TransformVisitor(self, npropdef)
+               var m = npropdef.mpropdef
+               if m == null then return
+               var v = new TransformVisitor(self, m)
                v.enter_visit(npropdef)
 
                val = new ASTValidationVisitor
@@ -44,15 +56,13 @@ private class TransformVisitor
        super Visitor
 
        var phase: TransformPhase
-       var mmodule: MModule
-       var mclassdef: MClassDef
+       var mmodule: MModule is noinit
+       var mclassdef: MClassDef is noinit
        var mpropdef: MPropDef
-       var builder: ASTBuilder
+       var builder: ASTBuilder is noinit
 
-       init(phase: TransformPhase, npropdef: APropdef)
+       init
        do
-               self.phase = phase
-               self.mpropdef = npropdef.mpropdef.as(not null)
                self.mclassdef = mpropdef.mclassdef
                self.mmodule = mclassdef.mmodule
                self.builder = new ASTBuilder(mmodule, mpropdef.mclassdef.bound_mtype)
@@ -61,14 +71,7 @@ private class TransformVisitor
        redef fun visit(node)
        do
                if node isa AAnnotations then return
-               node.visit_all(self)
-               node.accept_transform_visitor(self)
-       end
-
-       # Get a primitive class or display a fatal error on `location`.
-       fun get_class(location: AExpr, name: String): MClass
-       do
-               return mmodule.get_primitive_class(name)
+               node.full_transform_visitor(self)
        end
 
        # Get a primitive method or display a fatal error on `location`.
@@ -79,11 +82,40 @@ private class TransformVisitor
 end
 
 redef class ANode
+       private fun full_transform_visitor(v: TransformVisitor)
+       do
+               visit_all(v)
+               accept_transform_visitor(v)
+       end
        private fun accept_transform_visitor(v: TransformVisitor)
        do
        end
 end
 
+redef class AExpr
+       redef fun full_transform_visitor(v: TransformVisitor)
+       do
+               var na = comprehension
+               if na != null then
+                       # We are building a comprehension array `array`
+                       # Replace `self` with `array.push(self)`
+                       var place = detach_with_placeholder
+                       var recv = na.nnew.make_var_read
+                       var nadd = v.builder.make_call(recv, na.push_callsite.as(not null), [self])
+                       place.replace_with(nadd)
+               end
+               super
+       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
+               end
+       end
+end
+
 redef class AVardeclExpr
        # `var x = y` is replaced with `x = y`
        #
@@ -182,7 +214,7 @@ redef class AForExpr
 
                # Shortcut on explicit range
                # Avoid the instantiation of the range and the iterator
-               if self.variables.length == 1 and nexpr isa ARangeExpr then
+               if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
                        var variable = variables.first
                        nblock.add v.builder.make_var_assign(variable, nexpr.n_expr)
                        var to = nexpr.n_expr2
@@ -262,29 +294,87 @@ redef class AForExpr
        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
+end
+
 redef class AArrayExpr
        # `[x,y]` is replaced with
        #
-       #     var t = new Array[X].with_capacity(2)
-       #     t.add(x)
-       #     t.add(y)
-       #     t
-       redef fun accept_transform_visitor(v)
+       # ~~~nitish
+       # var t = new Array[X].with_capacity(2)
+       # t.add(x)
+       # t.add(y)
+       # t
+       # ~~~
+       redef fun full_transform_visitor(v)
        do
                var nblock = v.builder.make_block
 
-               var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.n_exprs.length)])
+               var nnew = v.builder.make_new(with_capacity_callsite.as(not null), [v.builder.make_int(n_exprs.length)])
+               self.nnew = nnew
+
                nblock.add nnew
 
-               for nexpr in self.n_exprs.n_exprs do
-                       var nadd = v.builder.make_call(nnew.make_var_read, push_callsite.as(not null), [nexpr])
-                       nblock.add nadd
+               super
+
+               for nexpr in self.n_exprs do
+                       nblock.add nexpr
                end
                var nres = nnew.make_var_read
                nblock.add nres
 
                replace_with(nblock)
        end
+
+       private var nnew: ANewExpr is noinit
 end
 
 redef class ACrangeExpr
@@ -316,11 +406,15 @@ end
 redef class ASendReassignFormExpr
        # `x.foo(y)+=z` is replaced with
        #
-       #     x.foo(y) = x.foo(y) + z
+       # ~~~nitish
+       # x.foo(y) = x.foo(y) + z
+       # ~~~
        #
        # witch is, in reality:
        #
-       #     x."foo="(y, x.foo(y)."+"(z))
+       # ~~~nitish
+       # x."foo="(y, x.foo(y)."+"(z))
+       # ~~~
        redef fun accept_transform_visitor(v)
        do
                var nblock = v.builder.make_block