new modules astbuilder, astvalidation, astprinter, and transform
authorJean Privat <jean@pryen.org>
Mon, 12 Aug 2013 18:44:20 +0000 (14:44 -0400)
committerJean Privat <jean@pryen.org>
Tue, 13 Aug 2013 18:01:23 +0000 (14:01 -0400)
Signed-off-by: Jean Privat <jean@pryen.org>

src/astbuilder.nit [new file with mode: 0644]
src/astprinter.nit [new file with mode: 0644]
src/astvalidation.nit [new file with mode: 0644]
src/nit.nit
src/nitg.nit
src/transform.nit [new file with mode: 0644]

diff --git a/src/astbuilder.nit b/src/astbuilder.nit
new file mode 100644 (file)
index 0000000..e6182a0
--- /dev/null
@@ -0,0 +1,309 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Instantiation and transformation of semantic nodes in the AST of expressions and statements
+module astbuilder
+
+intrude import typing
+intrude import literal
+intrude import parser
+intrude import scope
+
+# General factory to build semantic nodes in the AST of expressions
+class ASTBuilder
+       # The module used as reference for the building
+       # It is used to gather types and other stufs
+       var mmodule: MModule
+
+       # The anchor used for some mechanism relying on types
+       var anchor: nullable MClassType
+
+       # Make a new Int literal
+       fun make_int(value: Int): AIntExpr
+       do
+               return new AIntExpr.make(value, mmodule.get_primitive_class("Int").mclass_type)
+       end
+
+       # Make a new instatiation
+       fun make_new(mtype: MClassType, constructor: MMethod, args: nullable Array[AExpr]): ANewExpr
+       do
+               return new ANewExpr.make(mtype, constructor, args)
+       end
+
+       # Make a new message send
+       fun make_call(recv: AExpr, mmethod: MMethod, args: nullable Array[AExpr]): ACallExpr
+       do
+               var mtype = mmethod.intro.msignature.return_mtype
+               if mtype != null then mtype = mtype.resolve_for(recv.mtype.as(not null), anchor, mmodule, true)
+               return new ACallExpr.make(recv, mmethod, args, mtype)
+       end
+
+       # Make a new, empty, sequence of statements
+       fun make_block: ABlockExpr
+       do
+               return new ABlockExpr.make
+       end
+
+       # Make a new variable read
+       fun make_var_read(variable: Variable): AVarExpr
+       do
+               return new AVarExpr.make(variable)
+       end
+
+       # Make a new variable assignment
+       fun make_var_assign(variable: Variable, value: AExpr): AVarAssignExpr
+       do
+               return new AVarAssignExpr.make(variable, value)
+       end
+
+       # Make a new attribute read
+       fun make_attr_read(recv: AExpr, attribute: MAttribute): AAttrExpr
+       do
+               var mtype = attribute.intro.static_mtype.resolve_for(recv.mtype.as(not null), anchor, mmodule, true)
+               return new AAttrExpr.make(recv, attribute, mtype)
+       end
+
+       # Make a new attribute assignment
+       fun make_attr_assign(recv: AExpr, attribute: MAttribute, value: AExpr): AAttrAssignExpr
+       do
+               return new AAttrAssignExpr.make(recv, attribute, value)
+       end
+
+       # Make a new escapable block
+       fun make_do: ADoExpr
+       do
+               return new ADoExpr.make
+       end
+
+       # Make a new condinionnal
+       # `mtype` is the return type of the whole if, in case of a ternary operator.
+       fun make_if(condition: AExpr, mtype: nullable MType): AIfExpr
+       do
+               return new AIfExpr.make(condition, mtype)
+       end
+end
+
+redef class AExpr
+       # Return a new variable read that contains the value of the expression
+       # This method take care efficiently of creating and initalising an anonymous local variable
+       #
+       # Note: since this method do side-effects (AST replacement), there could be unexpected effects when used as
+       # argument of other methods related to AST transformations.
+       fun make_var_read: AVarExpr
+       do
+               var variable = self.variable_cache
+               if variable == null then
+                       assert parent != null
+                       var place = detach_with_placeholder
+                       variable = new Variable("")
+                       variable.declared_type = self.mtype
+                       var nvar = new AVarAssignExpr.make(variable, self)
+                       place.replace_with(nvar)
+                       self.variable_cache = variable
+               end
+               return new AVarExpr.make(variable)
+       end
+
+       private var variable_cache: nullable Variable
+
+       # The `detach` method completely remove the node in the parent.
+       # Owever, sometime, it is useful to keep the emplacement of the removed child.
+       #
+       # The standard usecase is the insertion of a node beetwen a parent `p` and a child `p.c`.
+       # To create the new node `n`, we need to attach the child to it.
+       # But, to put `n` where `c` was in `p`, the place has to be remembered.
+       #
+       #     var p: AExpr
+       #     var c = p.c
+       #     var h = c.detach_with_placeholder
+       #     var n = astbuilder.make_XXX(c)
+       #     h.replace_with(n)
+       fun detach_with_placeholder: AExpr
+       do
+               var h = new APlaceholderExpr.make
+               self.replace_with(h)
+               return h
+       end
+
+
+       # Add `expr` at the end of the block
+       #
+       # REQUIRE: self isa ABlockExpr
+       #
+       # Note: this method, aimed to `ABlockExpr` is promoted to `AExpr` because of the limitations of the hierarchies generated by sablecc3
+       fun add(expr: AExpr)
+       do
+               abort
+       end
+end
+
+# A placeholder for a `AExpr` node
+# Instances are transiantly used to mark some specific emplacments in the AST
+# during complex transformations.
+#
+# Their must not appear in a valid AST
+#
+# @see AExpr::detach_with_placeholder
+class APlaceholderExpr
+       super AExpr
+       private init make
+       do
+       end
+end
+
+redef class ABlockExpr
+       private init make
+       do
+               self.is_typed = true
+       end
+
+       redef fun add(expr: AExpr)
+       do
+               n_expr.add expr
+       end
+end
+
+redef class ADoExpr
+       private init make
+       do
+               _n_kwdo = new TKwdo
+               escapemark = new EscapeMark(null, false)
+       end
+
+       # Make a new break expression of the given do
+       fun make_break: ABreakExpr
+       do
+               var escapemark = self.escapemark
+               if escapemark == null then
+                       escapemark = new EscapeMark(null, false)
+                       self.escapemark = escapemark
+               end
+               return new ABreakExpr.make(escapemark)
+       end
+end
+
+redef class ABreakExpr
+       private init make(escapemark: EscapeMark)
+       do
+               self.escapemark = escapemark
+       end
+end
+
+redef class AIfExpr
+       private init make(condition: AExpr, mtype: nullable MType)
+       do
+               _n_kwif = new TKwif
+               _n_expr = condition
+               _n_expr.parent = self
+               _n_then = new ABlockExpr.make
+               _n_else = new ABlockExpr.make
+               self.mtype = mtype
+               self.is_typed = true
+       end
+end
+
+redef class AType
+       private init make
+       do
+               _n_id = new TClassid
+       end
+end
+
+redef class AIntExpr
+       private init make(value: Int, t: MType)
+       do
+               self.value = value
+               self._n_number = new TNumber # dummy
+               self.mtype = t
+       end
+end
+
+redef class ANewExpr
+       private init make(mtype: MClassType, mmethod: MMethod, args: nullable Array[AExpr])
+       do
+               _n_kwnew = new TKwnew
+               _n_type = new AType.make
+               _n_args = new AListExprs
+               if args != null then
+                       n_args.n_exprs.add_all(args)
+               end
+               callsite = new CallSite(self, mtype, true, mmethod, mmethod.intro, mmethod.intro.msignature.as(not null), false)
+               self.mtype = mtype
+       end
+end
+
+redef class ACallExpr
+       private init make(recv: AExpr, mmethod: MMethod, args: nullable Array[AExpr], t: nullable MType)
+       do
+               self._n_expr = recv
+               recv.parent = self
+               self.raw_arguments = args or else new Array[AExpr]
+               _n_args = new AListExprs
+               _n_id = new TId
+               if args != null then
+                       self.n_args.n_exprs.add_all(args)
+               end
+               var mtype = recv.mtype.as(not null)
+               callsite = new CallSite(self, mtype, true, mmethod, mmethod.intro, mmethod.intro.msignature.as(not null), false)
+               self.mtype = t
+               self.is_typed = true
+       end
+end
+
+redef class AAttrExpr
+       private init make(recv: AExpr, attribute: MAttribute, t: MType)
+       do
+               _n_expr = recv
+               recv.parent = self
+               _n_id = new TAttrid
+               mproperty = attribute
+               mtype = t
+       end
+end
+
+redef class AAttrAssignExpr
+       private init make(recv: AExpr, attribute: MAttribute, value: AExpr)
+       do
+               _n_expr = recv
+               recv.parent = self
+               _n_id = new TAttrid
+               _n_value = value
+               value.parent = self
+               _n_assign = new TAssign
+               mproperty = attribute
+               mtype = value.mtype
+       end
+end
+
+redef class AVarExpr
+       private init make(v: Variable)
+       do
+               _n_id = new TId
+               variable = v
+               mtype = v.declared_type
+       end
+end
+
+redef class AVarAssignExpr
+       private init make(v: Variable, value: AExpr)
+       do
+               _n_id = new TId
+               _n_value = value
+               value.parent = self
+               _n_assign = new TAssign
+               variable = v
+               mtype = value.mtype
+       end
+end
+
diff --git a/src/astprinter.nit b/src/astprinter.nit
new file mode 100644 (file)
index 0000000..97ff1fd
--- /dev/null
@@ -0,0 +1,166 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# print AST in an human form
+module astprinter
+
+import typing
+import phase
+intrude import parser
+import literal
+
+private class ASTPrinterVisitor
+       super Visitor
+       redef fun visit(node)
+       do
+               node.accept_printer(self)
+       end
+
+       var out = new List[String]
+       var indent_level = 0
+
+       var has_eol = true
+
+       fun eol
+       do
+               if has_eol then return
+               out.add("\n")
+               for x in [0..indent_level[ do out.add("\t")
+               has_eol = true
+       end
+
+       var last_current: nullable ANode
+
+       fun write(s: String)
+       do
+               if last_current != current_node then
+                       last_current = current_node
+                       var l = current_node._location
+                       if l != null then
+                               eol
+                               out.add(s)
+                               out.add("\t# {l.colored_line("0;32").split_with('\n').first}")
+                               has_eol = false
+                               eol
+                               return
+                       end
+               end
+               out.add(s)
+               has_eol = false
+       end
+
+       fun indent do indent_level += 1
+       fun unindent do indent_level -= 1
+end
+
+redef class ANode
+       # print the tree (using the semantic information) on screen
+       # This method is used to debug AST transformations
+       fun print_tree
+       do
+               var v = new ASTPrinterVisitor
+               v.enter_visit(self)
+               v.eol
+               for s in v.out do
+                       printn s
+               end
+       end
+
+       private fun accept_printer(v: ASTPrinterVisitor)
+       do
+               v.eol
+               v.write("({inspect}")
+               v.indent
+               visit_all(v)
+               v.write(")")
+               v.unindent
+       end
+end
+
+redef class ABlockExpr
+       redef fun accept_printer(v)
+       do
+               for x in n_expr do
+                       v.enter_visit(x)
+                       v.eol
+               end
+       end
+end
+
+redef class AIntExpr
+       redef fun accept_printer(v)
+       do
+               v.write(value.to_s)
+       end
+end
+
+redef class ANewExpr
+       redef fun accept_printer(v)
+       do
+               v.write("new {mtype.as(not null)}.{mproperty.as(not null)}")
+               if not n_args.n_exprs.is_empty then
+                       v.write("(")
+                       v.indent
+                       var is_first = true
+                       for a in n_args.n_exprs do
+                               if is_first then is_first = false else v.write(",")
+                               v.enter_visit(a)
+                       end
+                       v.unindent
+                       v.write(")")
+               end
+       end
+end
+
+redef class ASendExpr
+       redef fun accept_printer(v)
+       do
+               v.enter_visit(n_expr)
+               v.write(".{mproperty.name}")
+               if not raw_arguments.is_empty then
+                       v.write("(")
+                       v.indent
+                       var is_first = true
+                       for a in raw_arguments.as(not null) do
+                               if is_first then is_first = false else v.write(",")
+                               v.enter_visit(a)
+                       end
+                       v.unindent
+                       v.write(")")
+               end
+       end
+end
+
+redef class AVarExpr
+       redef fun accept_printer(v)
+       do
+               var name = variable.name
+               if name == "" then name = "t{variable.object_id}"
+               v.write(name)
+       end
+end
+
+redef class AVarAssignExpr
+       redef fun accept_printer(v)
+       do
+               var name = variable.name
+               if name == "" then name = "t{variable.object_id}"
+               v.write("{name} = ")
+               v.indent
+               v.enter_visit(n_value)
+               v.unindent
+       end
+end
diff --git a/src/astvalidation.nit b/src/astvalidation.nit
new file mode 100644 (file)
index 0000000..913ee21
--- /dev/null
@@ -0,0 +1,87 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Check the consitency of AST
+module astvalidation
+
+private import typing
+intrude import parser
+import astbuilder
+
+class ASTValidationVisitor
+       super Visitor
+       redef fun visit(node)
+       do
+               path.unshift(node)
+               node.accept_ast_validation(self)
+               path.shift
+       end
+       private var path = new List[ANode]
+       private var seen = new HashSet[ANode]
+end
+
+redef class ANode
+       private fun accept_ast_validation(v: ASTValidationVisitor)
+       do
+               var parent = self.parent
+
+               if v.path.length > 1 then
+                       var path_parent = v.path[1]
+                       if parent == null then
+                               self.parent = path_parent
+                               #debug "PARENT: expected parent: {path_parent}"
+                       else if parent != path_parent then
+                               self.parent = path_parent
+                               debug "PARENT: expected parent: {path_parent}, got {parent}"
+                       end
+               end
+
+               if v.seen.has(self) then
+                       debug "DUPLICATE: already seen node. NOTATREE"
+               end
+               v.seen.add(self)
+
+               if _location == null then
+                       #debug "LOCATION: unlocated node {v.path.join(", ")}"
+                       _location = self.parent.location
+               end
+
+               visit_all(v)
+       end
+end
+
+redef class AAnnotations
+       redef fun accept_ast_validation(v)
+       do
+               # Do not enter in annotations
+       end
+end
+
+redef class AExpr
+       redef fun accept_ast_validation(v)
+       do
+               super
+               if mtype == null and not is_typed then
+                       debug "TYPING: untyped expression"
+               end
+       end
+end
+
+redef class APlaceholderExpr
+       redef fun accept_ast_validation(v)
+       do
+               super
+               debug "PARENT: remaining placeholder"
+       end
+end
index e3d6f6d..caaf869 100644 (file)
@@ -19,6 +19,7 @@ module nit
 
 import modelbuilder
 import frontend
+import transform
 import naive_interpreter
 import debugger
 #import interpretor_type_test
index 58bf0f1..636b0b4 100644 (file)
@@ -19,6 +19,7 @@ module nitg
 
 import modelbuilder
 import frontend
+import transform
 import rapid_type_analysis
 import global_compiler
 import separate_erasure_compiler
diff --git a/src/transform.nit b/src/transform.nit
new file mode 100644 (file)
index 0000000..af19ef6
--- /dev/null
@@ -0,0 +1,308 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Thansformations that simplify the AST of expressions
+# This module transform complex AST `AExpr` nodes into simplier ones
+module transform
+
+import astbuilder
+import auto_super_init
+import astvalidation
+
+redef class ToolContext
+       var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase])
+end
+
+private class TransformPhase
+       super Phase
+
+       redef fun process_npropdef(npropdef: APropdef)
+       do
+               var val
+
+               var v = new TransformVisitor(self, npropdef)
+               v.enter_visit(npropdef)
+
+               val = new ASTValidationVisitor
+               val.enter_visit(npropdef)
+       end
+end
+
+private class TransformVisitor
+       super Visitor
+
+       var phase: TransformPhase
+       var mmodule: MModule
+       var mclassdef: MClassDef
+       var mpropdef: MPropDef
+       var builder: ASTBuilder
+
+       init(phase: TransformPhase, npropdef: APropdef)
+       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)
+       end
+
+       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)
+       end
+
+       # Get a primitive method or display a fatal error on `location`.
+       fun get_method(location: AExpr, name: String, recv: MClass): MMethod
+       do
+               return phase.toolcontext.modelbuilder.force_get_primitive_method(location, name, recv, mmodule)
+       end
+end
+
+redef class ANode
+       private fun accept_transform_visitor(v: TransformVisitor)
+       do
+       end
+end
+
+redef class AVardeclExpr
+       # `var x = y` is replaced with `x = y`
+       #
+       # Declarations are only useful for scope rules
+       # Once names are associated with real objects, ther declaration become useless
+       # Therefore, if there is no initial value, then just detach it
+       # Else, replace it with a simple assignment
+       redef fun accept_transform_visitor(v)
+       do
+               var nexpr = n_expr
+               if nexpr == null then
+                       detach
+               else
+                       var nvar = v.builder.make_var_assign(self.variable.as(not null), nexpr)
+                       replace_with(nvar)
+               end
+       end
+end
+
+redef class AIfexprExpr
+       # is replaced with `AIfExpr`
+       # Expression if and statement-if use two distinct classes for historical reasons
+       # However, on can replace the `AIfexprExpr` with the simpler `AIfExpr`
+       redef fun accept_transform_visitor(v)
+       do
+               var nif = v.builder.make_if(n_expr, self.mtype)
+               nif.n_then.add(n_then)
+               nif.n_else.add(n_else)
+
+               replace_with(nif)
+       end
+end
+
+redef class AOrExpr
+       # `x or y` is replaced with `if x then x else y`
+       redef fun accept_transform_visitor(v)
+       do
+               var nif = v.builder.make_if(n_expr, self.mtype)
+               nif.n_then.add(n_expr.make_var_read)
+               nif.n_else.add(n_expr2)
+
+               replace_with(nif)
+       end
+end
+
+redef class AAndExpr
+       # `x and y` is replaced with `if x then y else x`
+       redef fun accept_transform_visitor(v)
+       do
+               var nif = v.builder.make_if(n_expr, self.mtype)
+               nif.n_then.add(n_expr2)
+               nif.n_else.add(n_expr.make_var_read)
+
+               replace_with(nif)
+       end
+end
+
+redef class AWhileExpr
+       redef fun accept_transform_visitor(v)
+       do
+               # TODO
+       end
+end
+
+redef class AForExpr
+       redef fun accept_transform_visitor(v)
+       do
+               # TODO
+       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)
+       do
+               var mtype = self.mtype.as(MClassType)
+               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 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
+
+               replace_with(nblock)
+       end
+end
+
+redef class ASuperstringExpr
+       # `"x{y}z"` 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)
+       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)])
+               nblock.add nnew
+
+               var madd = v.get_method(self, "add", arraytype.mclass)
+               for nexpr in self.n_exprs do
+                       var nadd = v.builder.make_call(nnew.make_var_read, madd, [nexpr])
+                       nblock.add nadd
+               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
+
+               replace_with(nblock)
+       end
+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)
+               var meth = v.get_method(self, "init", mtype.mclass)
+
+               replace_with(v.builder.make_new(mtype, meth, [n_expr, n_expr2]))
+       end
+end
+
+redef class AOrangeExpr
+       # `[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)
+               var meth = v.get_method(self, "without_last", mtype.mclass)
+
+               replace_with(v.builder.make_new(mtype, meth, [n_expr, n_expr2]))
+       end
+end
+
+redef class AParExpr
+       # `(x)` is replaced with `x`
+       redef fun accept_transform_visitor(v)
+       do
+               replace_with(n_expr)
+       end
+end
+
+redef class ASendReassignFormExpr
+       # `x.foo(y)+=z` is replaced with
+       #
+       #     x.foo(y) = x.foo(y) + z
+       #
+       # witch is, in reality:
+       #
+       #     x."foo="(y, x.foo(y)."+"(z))
+       redef fun accept_transform_visitor(v)
+       do
+               var nblock = v.builder.make_block
+               nblock.add(n_expr)
+
+               var read_args = new Array[AExpr]
+               var write_args = new Array[AExpr]
+               for a in raw_arguments.as(not null) 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 nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value])
+
+               write_args.add(nnewvalue)
+               var nwrite = v.builder.make_call(n_expr.make_var_read, write_callsite.mproperty, write_args)
+               nblock.add(nwrite)
+
+               replace_with(nblock)
+       end
+end
+
+redef class AVarReassignExpr
+       # `v += z` is replaced with `v = v + z`
+       redef fun accept_transform_visitor(v)
+       do
+               var variable = self.variable.as(not null)
+
+               var nread = v.builder.make_var_read(variable)
+               var nnewvalue = v.builder.make_call(nread, reassign_callsite.mproperty, [n_value])
+               var nwrite = v.builder.make_var_assign(variable, nnewvalue)
+
+               replace_with(nwrite)
+       end
+end
+
+redef class AAttrReassignExpr
+       # `x.a += z` is replaced with `x.a = x.a + z`
+       redef fun accept_transform_visitor(v)
+       do
+               var nblock = v.builder.make_block
+               nblock.add(n_expr)
+               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 nwrite = v.builder.make_attr_assign(n_expr.make_var_read, attribute, nnewvalue)
+               nblock.add(nwrite)
+
+               replace_with(nblock)
+       end
+end