module transform
import astbuilder
-import auto_super_init
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
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
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)
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`.
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
+
+ visit_all(v)
+
+ if mtype == null and not is_typed 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
+ end
+ end
+end
+
redef class AVardeclExpr
# `var x = y` is replaced with `x = y`
#
redef class AWhileExpr
redef fun accept_transform_visitor(v)
do
- # TODO
+ var nloop = v.builder.make_loop
+ var nif = v.builder.make_if(n_expr, null)
+ nloop.add nif
+
+ var nblock = n_block
+ if nblock != null then nif.n_then.add nblock
+
+ var escapemark = self.break_mark.as(not null)
+ var nbreak = v.builder.make_break(escapemark)
+ nif.n_else.add nbreak
+
+ nloop.break_mark = self.break_mark
+ nloop.continue_mark = self.continue_mark
+
+ replace_with(nloop)
end
end
redef class AForExpr
redef fun accept_transform_visitor(v)
do
- # TODO
+ var escapemark = self.break_mark
+ assert escapemark != null
+
+ var nblock = v.builder.make_block
+
+ 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
+ var variable = variables.first
+ nblock.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
+
+ 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)
+
+ 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)
+ return
+ end
+
+ nblock.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
+
+ 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
+
+ if self.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
+ 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)
+ 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)
+ 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
+
+ var method_finish = self.method_finish
+ if method_finish != null then
+ nblock.add v.builder.make_call(iter.make_var_read, method_finish, null)
+ end
+
+ replace_with(nblock)
end
end
-redef class AArrayExpr
- # `[x,y]` is replaced with
+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
+ # ~~~
#
- # var t = new Array[X].with_capacity(2)
- # t.add(x)
- # t.add(y)
- # t
+ # The point is that `finish` is called even if the block is escaped.
redef fun accept_transform_visitor(v)
do
- var mtype = self.mtype.as(MClassType)
+ var escapemark = self.break_mark
+ assert escapemark != null
+
var nblock = v.builder.make_block
- var meth = v.get_method(self, "with_capacity", mtype.mclass)
- var nnew = v.builder.make_new(mtype, meth, [v.builder.make_int(n_exprs.n_exprs.length)])
- nblock.add nnew
+ var nexpr = n_expr
- var madd = v.get_method(self, "push", mtype.mclass)
- for nexpr in self.n_exprs.n_exprs do
- var nadd = v.builder.make_call(nnew.make_var_read, madd, [nexpr])
- nblock.add nadd
- end
- var nres = nnew.make_var_read
- nblock.add nres
+ 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 ASuperstringExpr
- # `"x{y}z"` is replaced with
+redef class AArrayExpr
+ # `[x,y]` is replaced with
#
- # var t = new Array[Object].with_capacity(3)
- # t.add("x")
- # t.add(y)
- # t.add("z")
- # t.to_s
- 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 arraytype = v.get_class(self, "Array").get_mtype([v.get_class(self, "Object").mclass_type])
- var meth = v.get_method(self, "with_capacity", arraytype.mclass)
- var nnew = v.builder.make_new(arraytype, meth, [v.builder.make_int(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
- var madd = v.get_method(self, "add", arraytype.mclass)
+ super
+
for nexpr in self.n_exprs do
- var nadd = v.builder.make_call(nnew.make_var_read, madd, [nexpr])
- nblock.add nadd
+ nblock.add nexpr
end
-
- var mtos = v.get_method(self, "to_s", arraytype.mclass)
- var ntos = v.builder.make_call(nnew.make_var_read, mtos, null)
- nblock.add ntos
+ 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
- var mtype = self.mtype.as(MClassType)
- replace_with(v.builder.make_new(mtype, init_callsite.mproperty, [n_expr, n_expr2]))
+ if parent isa AForExpr then return # to permit shortcut ranges
+ replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
end
end
# `[x..y[` is replaced with `new Range[X].without_last(x,y)`
redef fun accept_transform_visitor(v)
do
- var mtype = self.mtype.as(MClassType)
- replace_with(v.builder.make_new(mtype, init_callsite.mproperty, [n_expr, n_expr2]))
+ if parent isa AForExpr then return # to permit shortcut ranges
+ replace_with(v.builder.make_new(init_callsite.as(not null), [n_expr, n_expr2]))
end
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
var read_args = new Array[AExpr]
var write_args = new Array[AExpr]
- for a in raw_arguments.as(not null) do
+ for a in raw_arguments do
nblock.add(a)
read_args.add(a.make_var_read)
write_args.add(a.make_var_read)
end
- var nread = v.builder.make_call(n_expr.make_var_read, callsite.mproperty, read_args)
+ var nread = v.builder.make_call(n_expr.make_var_read, callsite.as(not null), read_args)
- var nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value])
+ var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
write_args.add(nnewvalue)
- var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.mproperty, write_args)
+ var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.as(not null), write_args)
nblock.add(nwrite)
replace_with(nblock)
var nread = v.builder.make_var_read(variable, read_type.as(not null))
- var nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value])
+ var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
var nwrite = v.builder.make_var_assign(variable, nnewvalue)
replace_with(nwrite)
var attribute = self.mproperty.as(not null)
var nread = v.builder.make_attr_read(n_expr.make_var_read, attribute)
- var nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value])
+ var nnewvalue = v.builder.make_call(nread, reassign_callsite.as(not null), [n_value])
var nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
nblock.add(nwrite)