X-Git-Url: http://nitlanguage.org diff --git a/src/transform.nit b/src/transform.nit index 2c0fb02..3499d9b 100644 --- a/src/transform.nit +++ b/src/transform.nit @@ -17,15 +17,15 @@ 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 @@ -41,7 +41,9 @@ private class TransformPhase do var val - var v = new TransformVisitor(self, npropdef.mpropdef.as(not null)) + var m = npropdef.mpropdef + if m == null then return + var v = new TransformVisitor(self, m) v.enter_visit(npropdef) val = new ASTValidationVisitor @@ -71,12 +73,6 @@ private class TransformVisitor node.full_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) - end - # Get a primitive method or display a fatal error on `location`. fun get_method(location: AExpr, name: String, recv: MClass): MMethod do @@ -95,6 +91,36 @@ redef class ANode 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 + + 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 + redef class AVardeclExpr # `var x = y` is replaced with `x = y` # @@ -187,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 - - var nthen = nif.n_then - var ndo = v.builder.make_do - ndo.break_mark = escapemark.continue_mark - nthen.add ndo + begin.add nif + nif.n_else.add v.builder.make_break(escapemark) - 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) - - var nbreak = v.builder.make_break(escapemark) - nif.n_else.add nbreak + # Next: call next + next.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null) - 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 @@ -282,29 +367,36 @@ redef class AArrayExpr # t.add(y) # t # ~~~ - redef fun accept_transform_visitor(v) + 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)]) + self.nnew = nnew + nblock.add nnew + super + for nexpr in self.n_exprs do - var nadd = v.builder.make_call(nnew.make_var_read, push_callsite.as(not null), [nexpr]) - nblock.add nadd + 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 # `[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 @@ -313,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