icode: introduce intermediate code representation
authorJean Privat <jean@pryen.org>
Sun, 26 Jul 2009 00:32:21 +0000 (20:32 -0400)
committerJean Privat <jean@pryen.org>
Mon, 27 Jul 2009 07:36:11 +0000 (03:36 -0400)
Missing parts are mainly:
 * identify bests ICode classes
 * keep casted variable static type in icode
 * remove MMSrc* classes dependency on AST

Signed-off-by: Jean Privat <jean@pryen.org>

33 files changed:
src/analysis/allocate_iregister_slots.nit [new file with mode: 0644]
src/analysis/analysis.nit [new file with mode: 0644]
src/analysis/icode_dump.nit [new file with mode: 0644]
src/analysis/inline_methods.nit [new file with mode: 0644]
src/compiling/compiling.nit
src/compiling/compiling_base.nit
src/compiling/compiling_global.nit
src/compiling/compiling_icode.nit [new file with mode: 0644]
src/compiling/compiling_methods.nit [deleted file]
src/icode/icode.nit [new file with mode: 0644]
src/icode/icode_base.nit [new file with mode: 0644]
src/icode/icode_builder.nit [new file with mode: 0644]
src/icode/icode_tools.nit [new file with mode: 0644]
src/metamodel/static_type.nit
src/nitc.nit
src/syntax/icode_generation.nit [new file with mode: 0644]
src/syntax/syntax.nit
src/syntax/syntax_base.nit
src/syntax/typing.nit
tests/sav/base_attr_isset_alt3.sav
tests/sav/base_attr_isset_alt4.sav
tests/sav/base_attr_nullable_alt1.sav
tests/sav/base_attr_nullable_alt2.sav
tests/sav/base_attr_nullable_alt3.sav
tests/sav/base_attr_nullable_alt4.sav
tests/sav/base_attr_nullable_alt5.sav
tests/sav/base_attr_nullable_int_alt1.sav
tests/sav/base_attr_nullable_int_alt2.sav
tests/sav/base_attr_nullable_int_alt3.sav
tests/sav/base_attr_nullable_int_alt4.sav
tests/sav/base_attr_nullable_int_alt5.sav
tests/sav/base_closure_raf_alt15.fail
tests/sav/test_id.sav

diff --git a/src/analysis/allocate_iregister_slots.nit b/src/analysis/allocate_iregister_slots.nit
new file mode 100644 (file)
index 0000000..3d22c30
--- /dev/null
@@ -0,0 +1,294 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# iregisters slot allocation
+package allocate_iregister_slots
+
+import icode
+
+# The main class that performs the iregister slot allocation
+# The algorithm is quite naive but works
+# Things TODO:
+#  * flow control
+#  * register aliasing
+#  * IMove optimization
+class IRegisterSlotAllocationVisitor
+special ICodeVisitor
+       # The visitor works in two pass:
+       # First pass is used to detect first and last iregisters occurences and slot groups
+       # Second pass is used to assign an effective slot to iregsiters
+       var _pass: Int = 0
+
+       redef fun visit_iregister_read(ic: ICode, r: IRegister)
+       do
+               var pass = _pass
+               if pass == 0 then
+                       if r._is_local and r._local_iroutine != _current_ir then
+                               if r._local_iroutine == null then
+                                       r._local_iroutine = _current_ir
+                               else
+                                       r._is_local = false
+                               end
+                       end
+                       _lasts[r] = ic
+                       r._slot_index = null
+               else if pass == 1 and _lasts.has_key(r) and _lasts[r] == ic then
+                       free(r)
+               end
+       end
+
+       redef fun visit_iregister_write(ic: ICode, r: IRegister)
+       do
+               var pass = _pass
+               if pass == 0 then
+                       # Process locality
+                       if r._is_local and r._local_iroutine != _current_ir then
+                               if r._local_iroutine == null then
+                                       r._local_iroutine = _current_ir
+                               else
+                                       r._is_local = false
+                               end
+                       end
+                       r._slot_index = null
+                       # The first write make it live
+                       if not _firsts.has_key(r) then _firsts[r] = ic
+                       # A read iregistre is stile live on a write
+                       if _lasts.has_key(r) then _lasts[r] = ic
+               else if pass == 1 then
+                       if _firsts[r] == ic then
+                               register(r)
+                       else if _lasts.has_key(r) and _lasts[r] == ic then
+                               free(r)
+                       end
+               end
+       end
+
+       # Iregister from outside a loop/closure cannot die inside the loop/closure
+       # So, the only register that can die in loops/closure are those born inside the loop/closure
+
+       # Registers born in the current loop/closure
+       # So they can die immediatly if needed
+       var _live: HashSet[IRegister] = new HashSet[IRegister]
+
+       # Registers born outsde the current loop/closure that wanted to die
+       # Thez will be effectively freed at the end of the loop/closure
+       var _deferred: HashSet[IRegister] = new HashSet[IRegister]
+
+       # Free the deferred that can be freed
+       # "Can" because of neested loops/closures
+       # new_def will be cleared and used as the new _deferred attribute
+       fun deferred_free(new_def: HashSet[IRegister])
+       do
+               var old_def = _deferred
+               if not old_def.is_empty then
+                       new_def.clear
+                       _deferred = new_def
+                       for r in old_def do
+                               free(r)
+                       end
+               end
+       end
+
+       redef fun visit_icode(ic)
+       do
+               if _pass == 1 and ic isa ILoop then
+                       var old_live = _live
+                       var new_live = new HashSet[IRegister]
+                       _live = new_live
+                       super
+                       _live = old_live
+                       deferred_free(new_live)
+               else
+                       super
+               end
+       end
+
+       redef fun visit_closure_defs(closdefs)
+       do
+               if _pass == 1 then
+                       var old_live = _live
+                       var new_live = new HashSet[IRegister]
+                       _live = new_live
+                       super
+                       _live = old_live
+                       deferred_free(new_live)
+               else
+                       super
+               end
+       end
+
+       # The current iroutine
+       # Used to detect locality of iregisters
+       var _current_ir: IRoutine
+
+       redef fun visit_iroutine(ir)
+       do
+               var res = ir.result
+               if _pass == 0 then
+                       var old_ir = _current_ir
+                       _current_ir = ir
+                       for r in ir.params do
+                               _firsts[r] = self
+                       end
+                       super
+                       if res != null then
+                               _lasts[res] = self
+                       end
+                       _current_ir = old_ir
+               else
+                       var old_bool_slots = _bool_slots
+                       _bool_slots = new SlotGroup
+                       var old_std_slots = _std_slots
+                       if ir isa IClosureDef then
+                               _std_slots = new SlotGroup
+                       end
+                       for r in ir.params do
+                               register(r)
+                       end
+                       super
+                       if res != null then free(res)
+                       ir._bool_slots_nb = _bool_slots._next_index
+                       _bool_slots = old_bool_slots
+                       ir._std_slots_nb = _std_slots._next_index
+                       _std_slots = old_std_slots
+               end
+       end
+
+       # Global slots are used for non local registers and some main iroutine registers
+       var _global_slots: SlotGroup = new SlotGroup
+
+       # Standad slots are used for local generic registers
+       # In main iroutine, _global_slots == _std_slots
+       var _std_slots: SlotGroup
+
+       # Bool slots are used to store local boolean registers
+       var _bool_slots: SlotGroup = new SlotGroup
+
+       # Assign a slot to a register according to its locality and its type
+       fun register(r: IRegister)
+       do
+               if not _lasts.has_key(r) then return
+               assert r._slot_index == null
+               _live.add(r)
+               if not r._is_local then
+                       _global_slots.register(r)
+               else if (r.stype.local_class.name == once ("Bool".to_symbol)) then
+                       r._in_bool_slots = true
+                       _bool_slots.register(r)
+               else
+                       _std_slots.register(r)
+               end
+       end
+
+       # Release a register, thus its slot can be reused
+       fun free(r: IRegister)
+       do
+               var i = r._slot_index
+               if i == null then return
+               if not _live.has(r) then
+                       _deferred.add(r)
+               else if _lasts.has_key(r) then
+                       if r._in_bool_slots then
+                               _bool_slots.free(r)
+                       else if r._is_local then
+                               _std_slots.free(r)
+                       else
+                               _global_slots.free(r)
+                       end
+                       _lasts.remove_at(r) # Free a register only once
+               end
+       end
+
+       # What is the first occurences of iregisters
+       # So that a slot is not needed before
+       var _firsts: HashMap[IRegister, Object] = new HashMap[IRegister, Object]
+
+       # What is the last occurences of iregisters
+       # So that a slot may be no more needed after
+       # ("may" because of loops/closure)
+       var _lasts: HashMap[IRegister, Object] = new HashMap[IRegister, Object]
+
+       # Run the slot allocation
+       fun iroutine_slot_allocation
+       do
+               var ir = _current_ir
+               visit_iroutine(ir)
+               _pass = 1
+               visit_iroutine(ir)
+       end
+
+       init(ir: IRoutine)
+       do
+               _current_ir = ir
+               _std_slots = _global_slots
+       end
+end
+
+# Group or equivalent slots shared by registers
+class SlotGroup
+       # The free slots in the group
+       var _free: List[Int] = new List[Int]
+
+       # The next free slot
+       var _next_index: Int = 0
+
+       # Assign a slot to the register
+       fun register(r: IRegister)
+       do
+               if _free.is_empty then
+                       r._slot_index = _next_index
+                       _next_index += 1
+               else
+                       r._slot_index = _free.pop
+               end
+       end
+
+       # Reuse the slot of the register
+       fun free(r: IRegister)
+       do
+               _free.push(r._slot_index.as(not null))
+       end
+end
+
+redef class IRoutine
+       # The number of standard slots (stored in an array)
+       readable var _std_slots_nb: Int = 0
+
+       # The number of bool slots
+       readable var _bool_slots_nb: Int = 0
+
+       fun allocate_iregister_slots
+       do
+               var v = new IRegisterSlotAllocationVisitor(self)
+               v.iroutine_slot_allocation
+       end
+end
+
+redef class IRegister
+       # The slot index of the register in its slot group
+       # Is null if the iregister is dead
+       # (or if iregister slot allocation is not performed yet)
+       readable writable var _slot_index: nullable Int
+
+       # Is the register local to a single iroutine?
+       readable writable var _is_local: Bool = true
+
+       # If is_local, then what iroutine
+       readable writable var _local_iroutine: nullable IRoutine
+
+       # Is the register stored in the bool slot groups?
+       readable writable var _in_bool_slots: Bool = false
+end
diff --git a/src/analysis/analysis.nit b/src/analysis/analysis.nit
new file mode 100644 (file)
index 0000000..7d9b028
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Intermediate code analysis and optimizations
+package analysis
+
+import icode
+import icode_dump
+import allocate_iregister_slots
+import inline_methods
+
+redef class IRoutine
+       # Perfom all optimizations
+       fun optimize
+       do
+               inline_methods
+               allocate_iregister_slots
+       end
+end
diff --git a/src/analysis/icode_dump.nit b/src/analysis/icode_dump.nit
new file mode 100644 (file)
index 0000000..3554816
--- /dev/null
@@ -0,0 +1,356 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Output the intermediate code unded a textual representation
+package icode_dump
+
+import icode
+import allocate_iregister_slots
+
+redef class IRoutine
+       # Output the intermediate code representation of the iroutine
+       fun dump(icd: ICodeDumper)
+       do
+               if not params.is_empty then
+                       var a = new Array[String]
+                       for p in params do
+                               a.add(icd.register(p))
+                       end
+                       icd.write "Parameters: {a.join(", ")}"
+               end
+               var r = result
+               if r != null then
+                       icd.write "Result: {icd.register(r)}"
+               end
+               var closdecls = closure_decls
+               if closdecls != null then
+                       for c in closdecls do
+                               c.dump(icd)
+                       end
+               end
+               body.dump(icd)
+       end
+end
+
+redef class IClosureDecl
+       # Output the intermediate code representation of the iclosuredecl
+       fun dump(icd: ICodeDumper)
+       do
+               icd.write "Closure: {icd.closdecl(self)}"
+               if default != null then
+                       icd.indent
+                       default.dump(icd)
+                       icd.unindent
+               end
+       end
+end
+
+class ICodeDumper
+       var _ids: HashMap[Object, String] = new HashMap[Object, String]
+       var _last_value: Int = 0
+
+       # Return the name of e
+       # If e is unknown, a new name is gived
+       fun register(e: IRegister): String
+       do
+               if _ids.has_key(e) then
+                       return _ids[e]
+               else
+                       var i = e.slot_index
+                       if i == null then
+                               _last_value += 1
+                               var s = "r{_last_value}"
+                               _ids[e] = s
+                               return s
+                       else
+                               _last_value += 1
+                               var s = "REG{i}(r{_last_value})"
+                               _ids[e] = s
+                               return s
+                       end
+               end
+       end
+
+       # Returns the names of es (separated with comma)
+       fun register_all(es: nullable Collection[IRegister]): String
+       do
+               if es == null then return ""
+               var a = new Array[String]
+               for e in es do
+                       a.add(register(e))
+               end
+               return a.join(", ")
+       end
+
+       var _last_clos: Int = 0
+
+       # Return the name of e
+       # If e is unknown, a new name is gived
+       fun closdecl(e: IClosureDecl): String
+       do
+               if _ids.has_key(e) then
+                       return _ids[e]
+               else
+                       _last_clos += 1
+                       var s = "clos{_last_clos}"
+                       _ids[e] = s
+                       return s
+               end
+       end
+
+       var _last_label: Int = 0
+       # Return the name of e
+       # If e is unknown, a new name is gived
+       fun lab(e: ISeq): String
+       do
+               if _ids.has_key(e) then
+                       return _ids[e]
+               else
+                       _last_label += 1
+                       var s = "[l{_last_label}]"
+                       _ids[e] = s
+                       return s
+               end
+       end
+
+       var _last_line: Int = 0
+       # Return the line index of e
+       fun line(e: ICode): String
+       do
+               if _ids.has_key(e) then
+                       return _ids[e]
+               else
+                       _last_line += 1
+                       var s = "{_last_line}"
+                       _ids[e] = s
+                       return s
+               end
+       end
+
+       # Is the label e known? (because we goto to it)
+       fun has_lab(e: ISeq): Bool
+       do
+               return _ids.has_key(e)
+       end
+
+       # Output something
+       fun write(s: String)
+       do
+               for i in [0.._indent_level[ do
+                       printn "    "
+               end
+               print s
+       end
+
+       var _indent_level: Int = 0
+
+       # Indent the next writes
+       fun indent do _indent_level += 1
+
+       # Outdent the next writes
+       fun unindent do _indent_level -= 1
+end
+
+redef class ICode
+       # Output the intermediate code representation
+       fun dump(icd: ICodeDumper)
+       do
+               var result = result
+               var s = ""
+               var l = location
+               if l != null then
+                       s = "        ... {l}"
+               end
+               if result == null then
+                       icd.write "{icd.line(self)}: {dump_intern(icd)}{s}"
+               else
+                       icd.write "{icd.line(self)}: {icd.register(result)} := {dump_intern(icd)}{s}"
+               end
+       end
+
+       # Output the intermediate code representation (inner method)
+       fun dump_intern(icd: ICodeDumper): String do return "???"
+end
+
+redef class ICodeN
+       redef fun dump(icd: ICodeDumper)
+       do
+               super
+               var closure_defs = closure_defs
+               if closure_defs != null then
+                       for clos in closure_defs do
+                               if clos == null then
+                                       icd.write "CLOSURE = NULL"
+                               else
+                                       icd.write "CLOSURE"
+                                       icd.indent
+                                       clos.dump(icd)
+                                       icd.unindent
+                               end
+                       end
+               end
+       end
+end
+
+redef class ISeq
+       redef fun dump(icd)
+       do
+               for ic in icodes do
+                       ic.dump(icd)
+               end
+               if icd.has_lab(self) then icd.write("{icd.lab(self)}:")
+       end
+end
+
+redef class IIf
+       redef fun dump(icd)
+       do
+               icd.write "IF({icd.register(expr)}) \{"
+               icd.indent
+               then_seq.dump(icd)
+               icd.unindent
+               icd.write "} ELSE \{"
+               icd.indent
+               else_seq.dump(icd)
+               icd.unindent
+               icd.write "}"
+       end
+end
+
+redef class ILoop
+       redef fun dump(icd)
+       do
+               icd.write "LOOP \{"
+               icd.indent
+               for ic in icodes do
+                       ic.dump(icd)
+               end
+               icd.unindent
+               icd.write "}"
+               if icd.has_lab(self) then icd.write("{icd.lab(self)}:")
+       end
+end
+
+redef class IEscape
+       redef fun dump_intern(icd)
+       do
+               return "ESCAPE {icd.lab(seq)}"
+       end
+end
+
+redef class IAbort
+       redef fun dump_intern(icd)
+       do
+               var pl = property_location
+               if pl != null then
+                       return "ABORT (\"{texts.join("\", \"")}\") in {pl.full_name}"
+               else
+                       return "ABORT (\"{texts.join("\", \"")}\")"
+               end
+       end
+end
+
+redef class ICall
+       redef fun dump_intern(icd)
+       do
+               return "CALL {property.full_name}({icd.register_all(exprs)})"
+       end
+end
+
+redef class IClosCall
+       redef fun dump_intern(icd)
+       do
+               return "CLOS_CALL {icd.closdecl(closure_decl)}({icd.register_all(exprs)})"
+       end
+end
+
+redef class IAttrRead
+       redef fun dump_intern(icd)
+       do
+               return "ATTR_READ {property.full_name}({icd.register(expr)})"
+       end
+end
+
+redef class IAttrWrite
+       redef fun dump_intern(icd)
+       do
+               return "ATTR_WRITE {property.full_name}({icd.register(expr1)}) := {icd.register(expr2)}"
+       end
+end
+
+redef class IAttrIsset
+       redef fun dump_intern(icd)
+       do
+               return "ATTR_ISSET {property.full_name}({icd.register(expr)})"
+       end
+end
+
+redef class ITypeCheck
+       redef fun dump_intern(icd)
+       do
+               return "CHECKTYPE {icd.register(expr)} isa {stype}"
+       end
+end
+
+redef class INative
+       redef fun dump_intern(icd)
+       do
+               if exprs.is_empty then
+                       return "NATIVE \"{code}\""
+               else
+                       return "NATIVE \"{code}\"({icd.register_all(exprs)})"
+               end
+       end
+end
+
+redef class IMove
+       redef fun dump_intern(icd)
+       do
+               return "{icd.register(expr)}"
+       end
+end
+
+redef class IIs
+       redef fun dump_intern(icd)
+       do
+               return "{icd.register(expr1)} is {icd.register(expr2)}"
+       end
+end
+
+redef class INot
+       redef fun dump_intern(icd)
+       do
+               return "NOT {icd.register(expr)}"
+       end
+end
+
+redef class IOnce
+       redef fun dump(icd)
+       do
+               icd.write "{icd.register(result.as(not null))} := ONCE \{"
+               icd.indent
+               body.dump(icd)
+               icd.unindent
+               icd.write "}"
+       end
+end
+
+redef class IHasClos
+       redef fun dump_intern(icd)
+       do
+               return "HASCLOS {icd.closdecl(closure_decl)}"
+       end
+end
diff --git a/src/analysis/inline_methods.nit b/src/analysis/inline_methods.nit
new file mode 100644 (file)
index 0000000..80f0aac
--- /dev/null
@@ -0,0 +1,66 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Detect inlinable methods and inline them
+package inline_methods
+
+import icode
+
+private class InlineMethodVisitor
+special ICodeVisitor
+       var _pass: Int = 0
+
+       redef fun visit_icode(ic)
+       do
+               if ic isa ICall then
+                       var m = ic.property
+                       if m.iroutine != null and ic.is_inlinable then
+                               var ir = m.iroutine.as(not null)
+                               var seq = new ISeq
+                               current_icode.insert_before(seq)
+                               var e = ir.inline_in_seq(seq, ic.exprs)
+                               var r = ic.result
+                               if r != null then
+                                       assert e != null
+                                       current_icode.insert_before(new IMove(r, e))
+                               end
+                               current_icode.delete
+                               visit_icode(seq)
+                       end
+               end
+               super
+       end
+end
+
+redef class ICall
+       fun is_inlinable: Bool
+       do
+               var m = property
+               var mn = m.name
+               var cn = m.local_class.name
+               return (m.is_intern and cn != once ("Object".to_symbol)) or
+                       (cn == (once ("Array".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol)))) or
+                       (cn == (once ("AbstractArrayRead".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol))))
+       end
+end
+
+redef class IRoutine
+       fun inline_methods
+       do
+               var v = new InlineMethodVisitor
+               v.visit_iroutine(self)
+       end
+end
index 883e388..8dfd072 100644 (file)
@@ -18,9 +18,8 @@
 package compiling
 
 import compiling_base
-private import compiling_methods
 private import compiling_global
-private import syntax
+private import compiling_icode
 
 redef class MMModule
        # Compile the program
index 0e3d6f0..ec63914 100644 (file)
@@ -17,7 +17,7 @@
 # Common things for NIT compilation and C generation
 package compiling_base
 
-import syntax
+import mmloader
 private import utils
 import primitive_info
 
index 31bd86b..b66c57a 100644 (file)
@@ -17,9 +17,7 @@
 # Compute and generate tables for classes and modules.
 package compiling_global
 
-#import compiling_base
-private import compiling_methods
-private import syntax
+private import compiling_icode
 
 # Something that store color of table elements
 class ColorContext
@@ -493,7 +491,7 @@ redef class MMModule
                        if not c isa MMConcreteClass then continue
                        for pg in c.global_properties do
                                var p = c[pg]
-                               if p.local_class == c then
+                               if p.local_class == c and p isa MMMethod then
                                        p.compile_property_to_c(v)
                                end
                                if pg.is_init_for(c) then
@@ -867,57 +865,57 @@ redef class MMLocalClass
 
                var pi = primitive_info
                if pi == null then
-                       v.cfc = new CFunctionContext(v)
-                       v.nmc = new NitMethodContext(null)
-                       var s = "val_t NEW_{name}(void)"
-                       v.add_instr(s + " \{")
-                       v.indent
-                       var ctx_old = v.ctx
-                       v.ctx = new CContext
-
-                       var self_var = new ParamVariable(once ("self".to_symbol), null)
-                       var self_var_cname = v.cfc.register_variable(self_var)
-                       v.nmc.method_params = [self_var]
-
-                       v.add_instr("obj_t obj;")
-                       v.add_instr("obj = alloc(sizeof(val_t) * {itab.length});")
-                       v.add_instr("obj->vft = (classtable_elt_t*)VFT_{name};")
-                       v.add_assignment(self_var_cname, "OBJ2VAL(obj)")
+                       var iself = new IRegister(get_type)
+                       var iselfa = [iself]
+                       var iroutine = new IRoutine(new Array[IRegister], iself)
+                       var icb = new ICodeBuilder(module, iroutine, null)
+                       var obj = new INative("OBJ2VAL(obj)", null)
+                       obj.result = iself
+                       icb.stmt(obj)
 
                        for g in global_properties do
                                var p = self[g]
                                var t = p.signature.return_type
                                if p isa MMAttribute and t != null then
+                                       var ir = p.iroutine
+                                       if ir == null then continue
                                        # FIXME: Not compatible with sep compilation
-                                       assert p isa MMSrcAttribute
-                                       var np = p.node
-                                       var ne = np.n_expr
-                                       if ne != null then
-                                               var e = ne.compile_expr(v)
-                                               v.add_instr("{p.global.attr_access}(obj) = {e};")
-                                       end
+                                       var e = ir.inline_in_seq(icb.seq, iselfa).as(not null)
+                                       icb.stmt(new IAttrWrite(p, iself, e))
                                end
                        end
-                       v.add_instr("return OBJ2VAL(obj);")
-                       v.cfc.generate_var_decls
+
+                       var cname = "NEW_{name}"
+                       var args = iroutine.compile_signature_to_c(v, cname, "new {name}", null, null)
+                       var ctx_old = v.ctx
+                       v.ctx = new CContext
+                       v.add_decl("obj_t obj;")
+                       v.add_instr("obj = alloc(sizeof(val_t) * {itab.length});")
+                       v.add_instr("obj->vft = (classtable_elt_t*)VFT_{name};")
+                       var r = iroutine.compile_to_c(v, cname, args).as(not null)
+                       v.add_instr("return {r};")
                        ctx_old.append(v.ctx)
                        v.ctx = ctx_old
                        v.unindent
                        v.add_instr("}")
 
                        # Compile CHECKNAME
-                       var s = "void CHECKNEW_{name}(val_t self, char *from)"
-                       v.add_instr(s + " \{")
-                       v.indent
-                       var ctx_old = v.ctx
-                       v.ctx = new CContext
+                       var iself = new IRegister(get_type)
+                       var iselfa = [iself]
+                       var iroutine = new IRoutine(iselfa, null)
+                       var icb = new ICodeBuilder(module, iroutine, null)
                        for g in global_properties do
                                var p = self[g]
                                var t = p.signature.return_type
                                if p isa MMAttribute and t != null and not t.is_nullable then
-                                       v.add_instr("if ({p.global.attr_access}(self) == NIT_NULL) \{ fprintf(stderr, \"Uninitialized attribute %s at %s.\\n\", \"{p.full_name}\", from); nit_exit(1); \}")
+                                       icb.add_attr_check(p, iself)
                                end
                        end
+                       var cname = "CHECKNEW_{name}"
+                       var args = iroutine.compile_signature_to_c(v, cname, "check new {name}", null, null)
+                       var ctx_old = v.ctx
+                       v.ctx = new CContext
+                       iroutine.compile_to_c(v, cname, args)
                        ctx_old.append(v.ctx)
                        v.ctx = ctx_old
                        v.unindent
@@ -930,21 +928,32 @@ redef class MMLocalClass
                                var p = self[g]
                                # FIXME skip invisible constructors
                                if not p.global.is_init_for(self) then continue
-                               var params = new Array[String]
-                               var args = ["self"]
-                               for i in [0..p.signature.arity[ do
-                                       params.add("val_t p{i}")
-                                       args.add("p{i}")
-                               end
-                               args.add("init_table")
-                               var s = "val_t NEW_{self}_{p.global.intro.cname}({params.join(", ")}) \{"
-                               v.add_instr(s)
-                               v.indent
+                               assert p isa MMMethod
+
+                               var iself = new IRegister(get_type)
+                               var iparams = new Array[IRegister]
+                               for i in [0..p.signature.arity[ do iparams.add(new IRegister(p.signature[i]))
+                               var iroutine = new IRoutine(iparams, iself)
+                               iroutine.location = p.iroutine.location
+                               var icb = new ICodeBuilder(module, iroutine, p)
+
+                               var inew = new INative("NEW_{name}()", null)
+                               inew.result = iself
+                               icb.stmt(inew)
+                               var iargs = [iself]
+                               iargs.add_all(iparams)
+                               icb.stmt(new INative("{p.cname}(@@@{", @@@"*iparams.length}, init_table)", iargs))
+                               icb.stmt(new INative("CHECKNEW_{name}(@@@)", [iself]))
+
+                               var cname = "NEW_{self}_{p.global.intro.cname}"
+                               var new_args = iroutine.compile_signature_to_c(v, cname, "new {self} {p.full_name}", null, null)
+                               var ctx_old = v.ctx
+                               v.ctx = new CContext
                                v.add_instr(init_table_decl)
-                               v.add_instr("val_t self = NEW_{name}();")
-                               v.add_instr("{p.cname}({args.join(", ")});")
-                               v.add_instr("CHECKNEW_{name}(self, \"{p.full_name} for {self}\");")
-                               v.add_instr("return self;")
+                               var e = iroutine.compile_to_c(v, cname, new_args).as(not null)
+                               v.add_instr("return {e};")
+                               ctx_old.append(v.ctx)
+                               v.ctx = ctx_old
                                v.unindent
                                v.add_instr("}")
                        end
@@ -963,3 +972,44 @@ redef class MMLocalClass
        end
 end
 
+redef class MMMethod
+       fun compile_property_to_c(v: CompilerVisitor)
+       do
+               var ir = iroutine
+               assert ir != null
+
+               var more_params: nullable String = null
+               if global.is_init then more_params = "int* init_table"
+               var args = ir.compile_signature_to_c(v, cname, full_name, null, more_params)
+               var ctx_old = v.ctx
+               v.ctx = new CContext
+
+               v.out_contexts.clear
+
+               var itpos: nullable String = null
+               if global.is_init then
+                       itpos = "itpos{v.new_number}"
+                       v.add_decl("int {itpos} = VAL2OBJ({args.first})->vft[{local_class.global.init_table_pos_id}].i;")
+                       v.add_instr("if (init_table[{itpos}]) return;")
+               end
+
+               var s = ir.compile_to_c(v, cname, args)
+
+               if itpos != null then
+                       v.add_instr("init_table[{itpos}] = 1;")
+               end
+               if s == null then
+                       v.add_instr("return;")
+               else
+                       v.add_instr("return ", s, ";")
+               end
+
+               ctx_old.append(v.ctx)
+               v.ctx = ctx_old
+               v.unindent
+               v.add_instr("}")
+
+               for ctx in v.out_contexts do v.ctx.merge(ctx)
+       end
+end
+
diff --git a/src/compiling/compiling_icode.nit b/src/compiling/compiling_icode.nit
new file mode 100644 (file)
index 0000000..5f44ca1
--- /dev/null
@@ -0,0 +1,803 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Generate C code from intermediate code representation
+package compiling_icode
+
+import icode
+private import analysis
+import compiling_base
+
+# Compiler context from ICode to C
+class I2CCompilerVisitor
+       # Associate things
+       var _ids: HashMap[Object, String] = new HashMap[Object, String]
+       # Associate other things
+       var _ids2: HashMap[Object, String] = new HashMap[Object, String]
+
+       # Return the string associated with a register
+       fun register(e: IRegister): String
+       do
+               if e.stype isa MMTypeNone then return "NIT_NULL"
+               var ids = _ids
+               if closure and not e.is_local then ids = _ids2
+               if ids.has_key(e) then
+                       return ids[e]
+               else
+                       var i = e.slot_index
+                       if i == null then
+                               # The register is dead
+                               var s = "NIT_NULL"
+                               ids[e] = s
+                               return s
+                       else
+                               var s: String
+                               var strs: HashMap[Int, String]
+                               if e.in_bool_slots then
+                                       strs = once new HashMap[Int, String]
+                                       if not strs.has_key(i) then strs[i] = "REGB{i}"
+                               else if closure and not e.is_local then
+                                       strs = once new HashMap[Int, String]
+                                       if not strs.has_key(i) then strs[i] = "closctx->variable[{i}]"
+                               else
+                                       strs = once new HashMap[Int, String]
+                                       if not strs.has_key(i) then strs[i] = "REG[{i}]"
+                               end
+                               s = strs[i]
+                               ids[e] = s
+                               return s
+                       end
+               end
+       end
+
+       # Return the strings associated with registers
+       fun registers(a: Collection[IRegister]): Array[String]
+       do
+               var r = new Array[String].with_capacity(a.length)
+               for e in a do
+                       r.add(register(e))
+               end
+               return r
+       end
+
+       var _last_number: Int = 0
+       # Give a new unique number (unique for the visitor)
+       fun new_number: Int
+       do
+               _last_number += 1
+               return _last_number
+       end
+
+       # Return the string associated with a escape label
+       fun lab(e: ISeq): String
+       do
+               if _ids.has_key(e) then
+                       return _ids[e]
+               else
+                       var s = "label{new_number}"
+                       _ids[e] = s
+                       return s
+               end
+       end
+
+       # The rank (number) of each closure
+       readable var _closures: HashMap[IClosureDecl, Int] = new HashMap[IClosureDecl, Int]
+
+       # The functionnal type of each closure
+       readable var _clostypes: HashMap[IClosureDecl, String] = new HashMap[IClosureDecl, String]
+
+       # label locally accessibles
+       readable writable var _local_labels: HashSet[ISeq] = new HashSet[ISeq]
+
+       # Not local escaped labels
+       # The integer value is an index identifying the label
+       readable writable var _escaped_labels: HashMap[ISeq, Int] = new HashMap[ISeq, Int]
+
+       # Register a escape to a non local label and return an index identifying the label
+       fun register_escape_label(e: ISeq): Int
+       do
+               if _escaped_labels.has_key(e) then
+                       return _escaped_labels[e]
+               else
+                       var res = _escaped_labels.length + 1
+                       _escaped_labels[e] = res
+                       return res
+               end
+       end
+
+       # Add a C label mark (if needed)
+       fun add_label(e: ISeq)
+       do
+               if _ids.has_key(e) then
+                       add_instr("{_ids[e]}: while(0);")
+               end
+       end
+
+       # Add a goto to a label (even outside a closure)
+       fun add_goto(seq: ISeq)
+       do
+               if local_labels.has(seq) then
+                       add_instr("goto {lab(seq)};")
+               else
+                       assert closure
+                       var ind = register_escape_label(seq)
+                       add_instr("closctx->has_broke = (val_t*){ind};")
+                       add_instr("goto {lab(return_label.as(not null))};")
+               end
+       end
+
+       # Are we in a closure ?
+       readable writable var _closure: Bool = false
+
+       # The current compiler visitor
+       readable var _visitor: CompilerVisitor
+
+       # The current compiled iroutine
+       readable var _iroutine: IRoutine
+
+       # The return label of the current compiling C function
+       readable writable var _return_label: nullable ISeq = null
+
+       fun add_decl(s: String)
+       do
+               visitor.add_decl(s)
+       end
+
+       fun add_instr(s: String)
+       do
+               var l = _next_location
+               if l != null then
+                       visitor.add_instr("/* ", l.file, ":", l.line_start.to_s, " */")
+                       _next_location = null
+               end
+               visitor.add_instr(s)
+       end
+
+       fun indent
+       do
+               visitor.indent
+       end
+
+       fun unindent
+       do
+               visitor.unindent
+       end
+
+       fun add_assignment(to, from: String)
+       do
+               visitor.add_assignment(to, from)
+       end
+
+       var _last_location: nullable Location = null
+       var _next_location: nullable Location = null
+
+       # Add location information in a comment
+       # Do nothing if the last location added is the same
+       fun add_location(l: nullable Location)
+       do
+               var last = _last_location
+               if last == l or l == null then return
+               _last_location = l
+               if last != null and last.file == l.file and last.line_start == l.line_start then
+                       return
+               else
+                       _next_location = l
+               end
+       end
+
+       # The C fonction name of the iroutine
+       readable var _basecname: String
+
+       init(v: CompilerVisitor, ir: IRoutine, cname: String)
+       do
+               _visitor = v
+               _iroutine = ir
+               _basecname = cname
+       end
+end
+
+redef class IRoutine
+       # Declare and start a C function that match the routine
+       # Return what must be given to compile_inside_to_c or to compile_to_c
+       # After the method, an openinig { and and indent is added.
+       # So, do not forget to add a sub_context, to unintent and to add a closing }
+       fun compile_signature_to_c(v: CompilerVisitor, cname: String, human_name: nullable String, before_params, after_params: nullable String): Array[String]
+       do
+               var cargs = new Array[String]
+               var cparams = new Array[String]
+               if before_params != null then cparams.add(before_params)
+               for i in [0..params.length[ do
+                       cargs.add("p{i}")
+                       cparams.add("val_t p{i}")
+               end
+               if closure_decls != null then
+                       for i in [0..closure_decls.length[ do
+                               var closcn = "CLOS_{cname}_{i}"
+                               var cs = closure_decls[i].closure.signature
+                               var subparams = new Array[String] # Parameters of the closure
+                               subparams.add("struct WBT_ *")
+                               for j in [0..cs.arity[ do
+                                       subparams.add("val_t")
+                               end
+                               var rr = "void"
+                               if cs.return_type != null then rr = "val_t"
+                               v.add_decl("typedef {rr} (*{closcn})({subparams.join(", ")});")
+                               cargs.add("wd{i}")
+                               cparams.add("struct WBT_ *wd{i}")
+                       end
+               end
+               if after_params != null then cparams.add(after_params)
+               var r = "void"
+               if result != null then r = "val_t"
+               var p: String
+               if cparams.is_empty then
+                       p = "void"
+               else
+                       p = cparams.join(", ")
+               end
+               if human_name != null then v.add_decl("#define LOCATE_", cname, " \"", human_name, "\"")
+               v.add_decl(r, " ", cname, "(", p, ");")
+               v.add_decl("typedef ", r, " (*", cname, "_t)(", p, ");")
+               v.add_instr(r, " ", cname, "(", p, ")\{")
+               v.indent
+               return cargs
+       end
+
+       # Compile the body of the routine, return the result value is any
+       fun compile_inside_to_c(v: I2CCompilerVisitor, args: Array[String]): nullable String
+       do
+               # Add the trace
+               var ll = 0
+               if location != null then
+                       ll = location.line_start
+               end
+               v.add_decl("struct trace_t trace = \{NULL, NULL, {ll}, LOCATE_{v.basecname}\};")
+               v.add_instr("trace.prev = tracehead; tracehead = &trace;")
+               v.add_instr("trace.file = LOCATE_{v.visitor.module.name};")
+
+               # Add local variables
+               if std_slots_nb == 0 then
+                       v.add_decl("val_t *REG = NULL;")
+               else
+                       v.add_decl("val_t REG[{std_slots_nb}];")
+               end
+               for i in [0..bool_slots_nb[ do
+                       v.add_decl("val_t REGB{i};")
+               end
+               var iclosdecls = closure_decls
+               if iclosdecls != null then
+                       v.add_decl("struct WBT_ *CREG[{iclosdecls.length}];")
+               else
+                       v.add_decl("struct WBT_ **CREG = NULL;")
+               end
+               var i = 0
+               for r in params do
+                       if r.slot_index != null then v.add_assignment(v.register(r), args[i])
+                       i += 1
+               end
+               var iclosdecls = closure_decls
+               if iclosdecls != null then
+                       for i in [0..iclosdecls.length[ do
+                               var iclosdecl = iclosdecls[i]
+                               v.add_instr("CREG[{i}] = {args[params.length+i]};")
+                               v.closures[iclosdecl] = i
+                               var cs = iclosdecl.closure.signature # Closure signature
+                               var subparams = new Array[String] # Parameters of the closure
+                               subparams.add("struct WBT_ *")
+                               for j in [0..cs.arity[ do
+                                       var p = "val_t"
+                                       subparams.add(p)
+                               end
+                               var r = "void"
+                               if cs.return_type != null then r = "val_t"
+                               v.clostypes[iclosdecl] = "{r} (*)({subparams.join(", ")})"
+                       end
+               end
+               v.add_decl("val_t tmp;")
+
+               # Prepare return
+               var old_rl = v.return_label
+               v.return_label = body
+
+               # Compile body
+               body.compile_to_c(v)
+
+               v.add_instr("tracehead = trace.prev;")
+               v.return_label = old_rl
+               var r = result
+               if r != null then
+                       return v.register(r)
+               else
+                       return null
+               end
+       end
+
+       # Full compilation of the routine
+       # Including optimization and other stuff.
+       # cv must be in the correct function
+       fun compile_to_c(cv: CompilerVisitor, cname: String, args: Array[String]): nullable String
+       do
+               optimize
+               var v = new I2CCompilerVisitor(cv, self, cname)
+               return compile_inside_to_c(v, args)
+       end
+end
+
+redef class ICode
+       # Full compilation of the icode
+       fun compile_to_c(v: I2CCompilerVisitor)
+       do
+               v.add_location(location)
+               store_result(v, inner_compile_to_c(v))
+       end
+
+       # Is a result really needed
+       private fun need_result: Bool
+       do
+               var r = result
+               return r != null and r.slot_index != null
+       end
+
+       # Store s in the result value of self
+       private fun store_result(v: I2CCompilerVisitor, s: nullable String)
+       do
+               var r = result
+               if r != null and r.slot_index != null then
+                       assert s != null
+                       v.add_assignment(v.register(r), s)
+               else if s != null and not self isa IMove then
+                       v.add_instr(s + ";")
+               end
+       end
+
+       # Compilation of without the result assigment
+       # Return the right value is case of expression
+       # Return the full expression (witout ;) in case of statement
+       private fun inner_compile_to_c(v: I2CCompilerVisitor): nullable String is abstract
+end
+
+redef class ISeq
+       redef fun inner_compile_to_c(v)
+       do
+               v.local_labels.add(self)
+               for ic in icodes do
+                       ic.compile_to_c(v)
+               end
+               v.add_label(self)
+               return null
+       end
+end
+
+redef class IIf
+       redef fun inner_compile_to_c(v)
+       do
+               v.add_instr("if (UNTAG_Bool({v.register(expr)})) \{")
+               if not then_seq.icodes.is_empty then
+                       v.indent
+                       then_seq.inner_compile_to_c(v)
+                       v.unindent
+               end
+               if not else_seq.icodes.is_empty then
+                       v.add_instr("} else \{")
+                       v.indent
+                       else_seq.inner_compile_to_c(v)
+                       v.unindent
+               end
+               v.add_instr("}")
+               return null
+       end
+end
+
+redef class ILoop
+       redef fun inner_compile_to_c(v)
+       do
+               v.local_labels.add(self)
+               v.add_instr("while(1) \{")
+               v.indent
+               for ic in icodes do
+                       ic.compile_to_c(v)
+               end
+               v.unindent
+               v.add_instr("}")
+               v.add_label(self)
+               return null
+       end
+end
+
+redef class IEscape
+       redef fun inner_compile_to_c(v)
+       do
+               v.add_goto(seq)
+               return null
+       end
+end
+
+redef class IAbsCall
+       redef fun compile_to_c(v)
+       do
+               v.add_location(location)
+               var args = v.registers(exprs)
+
+               # Compile closure definitions
+               var old_el = v.escaped_labels
+               var closdefs = closure_defs
+               var closcns: nullable Array[String] = null
+               if closdefs != null then
+                       v.escaped_labels = new HashMap[ISeq, Int]
+                       closcns = new Array[String]
+                       for cd in closdefs do
+                               if cd != null then
+                                       var cn = cd.compile_closure(v)
+                                       args.add(cn)
+                                       closcns.add(cn)
+                               else
+                                       args.add("NULL")
+                               end
+                       end
+               end
+
+               var s = compile_call_to_c(v, args)
+               var r: nullable String = s
+
+               # Intercept escapes
+               if closcns != null then
+                       var els = v.escaped_labels
+                       v.escaped_labels = old_el
+                       # Is there possible escapes?
+                       if not els.is_empty then
+                               # Call in a tmp variable to avoid 'break' overwrite
+                               if need_result then
+                                       r = "tmp"
+                                       v.add_assignment(r, s)
+                               else
+                                       r = null
+                                       v.add_instr(s + ";")
+                               end
+                               # What is the escape index (if any?)
+                               # It's computed as the union of has_broke
+                               var switcha = new Array[String]
+                               for cn in closcns do
+                                       switcha.add("((int)({cn}->has_broke))")
+                               end
+                               var switch = switcha.join(" | ")
+                               # What are the expected escape indexes
+                               v.add_instr("switch ({switch}) \{")
+                               v.indent
+                               # No escape occured, continue as usual
+                               v.add_instr("case 0: break;")
+                               var lls = v.local_labels
+                               var iels = els.iterator
+                               var forward_escape = false
+                               while iels.is_ok do
+                                       var seq = iels.key
+                                       if lls.has(seq) then
+                                               # Local escape occured
+                                               v.add_instr("case {iels.item}: goto {v.lab(seq)};")
+                                       else
+                                               # Forward escape occured: register the escape label
+                                               assert v.closure
+                                               v.register_escape_label(seq)
+                                               forward_escape = true
+                                       end
+                                       iels.next
+                               end
+                               # Forward escape occured, just pass to the next one
+                               if forward_escape then
+                                       v.add_instr("default: closctx->has_broke = (val_t*)({switch}); goto {v.lab(v.return_label.as(not null))};")
+                               end
+                               v.unindent
+                               v.add_instr("\}")
+                       end
+               end
+
+               store_result(v, r)
+       end
+
+       redef fun inner_compile_to_c(v) do abort
+
+       # The single invocation witout fancy stuffs
+       private fun compile_call_to_c(v: I2CCompilerVisitor, args: Array[String]): String is abstract
+end
+
+redef class ICall
+       redef fun compile_call_to_c(v, args)
+       do
+               var prop = property
+               if prop.global.is_init then args.add("init_table")
+               if prop.name == (once ("add".to_symbol)) and prop.local_class.name == (once ("Array".to_symbol)) then
+                       return "{prop.cname}({args.join(", ")})"
+               else
+                       return "{prop.global.meth_call}({args[0]})({args.join(", ")})"
+               end
+       end
+end
+
+redef class ISuper
+       redef fun compile_call_to_c(v, args)
+       do
+               var prop = property
+               if prop.global.is_init then args.add("init_table")
+               return "{prop.super_meth_call}({args[0]})({args.join(", ")})"
+       end
+end
+
+redef class INew
+       redef fun compile_call_to_c(v, args)
+       do
+               return "NEW_{stype.local_class}_{property.global.intro.cname}({args.join(", ")})"
+       end
+end
+
+redef class INative
+       redef fun inner_compile_to_c(v)
+       do
+               if exprs.is_empty then
+                       return code
+               else
+                       var res = new Buffer
+                       var i = 0
+                       var c = code.split_with("@@@")
+                       for s in c do
+                               res.append(s)
+                               if i < exprs.length and i < c.length-1 then
+                                       res.append(v.register(exprs[i]))
+                               end
+                               i += 1
+                       end
+                       return res.to_s
+               end
+       end
+end
+
+redef class IAbort
+       redef fun inner_compile_to_c(v)
+       do
+               var s = new Buffer.from("fprintf(stderr")
+               for t in texts do
+                       s.append(", \"{t}\"")
+               end
+               s.append(");")
+               v.add_instr(s.to_s)
+
+               var ll = location
+               var pl = property_location
+               s = new Buffer.from("fprintf(stderr, \"")
+               if pl != null then s.append(" in %s")
+               s.append(" (%s")
+               if ll != null then
+                       s.append(":%d")
+               end
+               s.append(")\\n\", ")
+               if pl != null then s.append("LOCATE_{pl.cname}, ")
+               s.append("LOCATE_{module_location.name}")
+               if ll != null then
+                       s.append(", {ll.line_start}")
+               end
+               s.append(");")
+               v.add_instr(s.to_s)
+
+               v.add_instr("nit_exit(1);")
+               return null
+       end
+end
+
+redef class IMove
+       redef fun inner_compile_to_c(v)
+       do
+               return v.register(expr)
+       end
+end
+
+redef class IAttrRead
+       redef fun inner_compile_to_c(v)
+       do
+               return "{property.global.attr_access}({v.register(expr)})"
+       end
+end
+
+redef class IAttrIsset
+       redef fun inner_compile_to_c(v)
+       do
+               return "TAG_Bool({property.global.attr_access}({v.register(expr)})!=NIT_NULL)"
+       end
+end
+
+redef class IAttrWrite
+       redef fun inner_compile_to_c(v)
+       do
+               v.add_instr("{property.global.attr_access}({v.register(expr1)}) = {v.register(expr2)};")
+               return null
+       end
+end
+
+redef class ITypeCheck
+       redef fun inner_compile_to_c(v)
+       do
+               # FIXME handle formaltypes
+               var g = stype.local_class.global
+               var recv = v.register(expr)
+               var s = ""
+               if expr.stype.is_nullable then
+                       if stype.is_nullable then
+                               s = "({recv}==NIT_NULL) || "
+                       else if stype.as_nullable == expr.stype then
+                               return "TAG_Bool({recv}!=NIT_NULL)"
+                       else
+                               s = "({recv}!=NIT_NULL) && "
+                       end
+               end
+               return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {stype}*/"
+       end
+end
+
+redef class IIs
+       redef fun inner_compile_to_c(v)
+       do
+               var t1 = expr1.stype
+               var t2 = expr2.stype
+               if t1 isa MMTypeNone then
+                       if t2 isa MMTypeNone then
+                               return "TAG_Bool(1)"
+                       else if t2.is_nullable then
+                               return "TAG_Bool({v.register(expr2)}==NIT_NULL)"
+                       else
+                               return "TAG_Bool(0)"
+                       end
+               else if t1.is_nullable then
+                       if t2 isa MMTypeNone then
+                               return "TAG_Bool({v.register(expr1)}==NIT_NULL)"
+                       else if t2.is_nullable then
+                               return "TAG_Bool(IS_EQUAL_NN({v.register(expr1)},{v.register(expr2)}))"
+                       else
+                               return "TAG_Bool(IS_EQUAL_ON({v.register(expr2)},{v.register(expr1)}))"
+                       end
+               else
+                       if t2 isa MMTypeNone then
+                               return "TAG_Bool(0)"
+                       else if t2.is_nullable then
+                               return "TAG_Bool(IS_EQUAL_ON({v.register(expr1)},{v.register(expr2)}))"
+                       else
+                               return "TAG_Bool(IS_EQUAL_OO({v.register(expr1)},{v.register(expr2)}))"
+                       end
+               end
+       end
+end
+
+redef class INot
+       redef fun inner_compile_to_c(v)
+       do
+               return "TAG_Bool(!UNTAG_Bool({v.register(expr)}))"
+       end
+end
+
+redef class IOnce
+       redef fun inner_compile_to_c(v)
+       do
+               var i = v.new_number
+               var res = result.as(not null)
+               if res.stype.is_nullable then
+                       v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value */")
+                       v.add_instr("if (!once_bool_{i}) \{")
+               else
+                       # Since the value is not nullable, we use the null value to represent the boolean
+                       v.add_decl("static val_t once_value_{i}; /* Once value */")
+                       v.add_instr("if (!once_value_{i}) \{")
+               end
+               v.indent
+               body.compile_to_c(v)
+               var e = v.register(result.as(not null))
+               v.add_instr("once_value_{i} = {e};")
+               if res.stype.is_nullable then v.add_instr("once_bool_{i} = true;")
+               v.unindent
+               v.add_instr("} else {e} = once_value_{i};")
+               return e
+       end
+end
+
+redef class IClosCall
+       redef fun compile_to_c(v: I2CCompilerVisitor)
+       do
+               v.add_location(location)
+               var ivar: String
+               if v.closure then
+                       ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
+               else
+                       ivar = "CREG[{v.closures[closure_decl]}]"
+               end
+               var args = [ivar]
+               args.append(v.registers(exprs))
+
+               var s = "(({v.clostypes[closure_decl]})({ivar}->fun))({args.join(", ")})"
+               store_result(v, s)
+
+               v.add_instr("if ({ivar}->has_broke) \{")
+               v.indent
+               var bs = break_seq
+               if bs != null then
+                       bs.compile_to_c(v)
+               end
+               v.add_goto(v.iroutine.body)
+               v.unindent
+               v.add_instr("\}")
+       end
+
+       redef fun inner_compile_to_c(v) do abort
+end
+
+redef class IHasClos
+       redef fun inner_compile_to_c(v)
+       do
+               var ivar: String
+               if v.closure then
+                       ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
+               else
+                       ivar = "CREG[{v.closures[closure_decl]}]"
+               end
+               return "TAG_Bool({ivar} != NULL)"
+       end
+end
+
+
+redef class IClosureDef
+       fun compile_closure(v: I2CCompilerVisitor): String
+       do
+               var cfc_old = v.closure
+               v.closure = true
+               var lls_old = v.local_labels
+               v.local_labels = new HashSet[ISeq]
+
+               var cv = v.visitor
+               var ctx_old = cv.ctx
+               cv.ctx = new CContext
+               cv.out_contexts.add(cv.ctx)
+
+               var cname = "OC_{v.basecname}_{v.new_number}"
+               var args = compile_signature_to_c(v.visitor, cname, null, "struct WBT_ *closctx", null)
+               var ctx_old2 = cv.ctx
+               cv.ctx = new CContext
+
+               var s = compile_inside_to_c(v, args)
+               if s == null then
+                       v.add_instr("return;")
+               else
+                       v.add_instr("return {s};")
+               end
+
+               ctx_old2.append(cv.ctx)
+               cv.ctx = ctx_old2
+               v.unindent
+               v.add_instr("}")
+               cv.ctx = ctx_old
+
+               v.closure = cfc_old
+
+               # Build closure
+               var closcnv = "wbclos{v.new_number}"
+               v.add_decl("struct WBT_ {closcnv};")
+               v.add_instr("{closcnv}.fun = (fun_t){cname};")
+               v.add_instr("{closcnv}.has_broke = NULL;")
+               if cfc_old then
+                       v.add_instr("{closcnv}.variable = closctx->variable;")
+                       v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;")
+               else
+                       v.add_instr("{closcnv}.variable = REG;")
+                       v.add_instr("{closcnv}.closurevariable = CREG;")
+               end
+
+               v.local_labels = lls_old
+               return "(&{closcnv})"
+       end
+end
diff --git a/src/compiling/compiling_methods.nit b/src/compiling/compiling_methods.nit
deleted file mode 100644 (file)
index f189355..0000000
+++ /dev/null
@@ -1,1837 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 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.
-
-# Compile method bodies, statments and expressions to C.
-package compiling_methods
-
-import compiling_base
-private import syntax
-
-redef class CompilerVisitor
-       # Compile a statment node
-       fun compile_stmt(n: nullable PExpr)
-       do
-               if n == null then return
-               #add_instr("/* Compile stmt {n.locate} */")
-               n.prepare_compile_stmt(self)
-               var i = cfc._variable_index
-               n.compile_stmt(self)
-               cfc._variable_index = i
-       end
-
-       # Compile is expression node
-       fun compile_expr(n: PExpr): String
-       do
-               #add_instr("/* Compile expr {n.locate} */")
-               var i = cfc._variable_index
-               var s = n.compile_expr(self)
-               cfc._variable_index = i
-               if s[0] == ' ' or cfc.is_valid_variable(s) then
-                       return s
-               end
-               var v = cfc.get_var("Result")
-               add_assignment(v, s)
-               return v
-       end
-
-       # Ensure that a c expression is a var
-       fun ensure_var(s: String, comment: String): String
-       do
-               if cfc.is_valid_variable(s) then
-                       add_instr("/* Ensure var {s}: {comment}*/")
-                       return s
-               end
-               var v = cfc.get_var(null)
-               add_assignment(v, "{s} /* Ensure var: {comment}*/")
-               return v
-       end
-
-       readable writable var _cfc: nullable CFunctionContext
-
-       readable writable var _nmc: nullable NitMethodContext
-
-       # Generate an fprintf to display an error location
-       fun printf_locate_error(node: PNode): String
-       do
-               var s = new Buffer.from("fprintf(stderr, \"")
-               if nmc != null then s.append(" in %s")
-               s.append(" (%s:%d)\\n\", ")
-               if nmc != null then s.append("LOCATE_{nmc.method.cname}, ")
-               s.append("LOCATE_{module.name}, {node.location.line_start});")
-               return s.to_s
-       end
-
-       fun invoke_super_init_calls_after(start_prop: nullable MMMethod)
-       do
-               var n = nmc.method.node
-               assert n isa AConcreteInitPropdef
-
-               if n.super_init_calls.is_empty then return
-               var i = 0
-               var j = 0
-               #var s = ""
-               if start_prop != null then
-                       while n.super_init_calls[i] != start_prop do
-                               #s.append(" {n.super_init_calls[i]}")
-                               i += 1
-                       end
-                       i += 1
-                       #s.append(" {start_prop}")
-
-                       while n.explicit_super_init_calls[j] != start_prop do
-                               j += 1
-                       end
-                       j += 1
-               end
-               var stop_prop: nullable MMMethod = null
-               if j < n.explicit_super_init_calls.length then
-                       stop_prop = n.explicit_super_init_calls[j]
-               end
-               var l = n.super_init_calls.length
-               #s.append(" [")
-               while i < l do
-                       var p = n.super_init_calls[i]
-                       if p == stop_prop then break
-                       var cargs = new Array[String]
-                       if p.signature.arity == 0 then
-                               cargs.add(cfc.varname(nmc.method_params[0]))
-                       else
-                               for va in nmc.method_params.as(not null) do
-                                       cargs.add(cfc.varname(va))
-                               end
-                       end
-                       #s.append(" {p}")
-                       p.compile_stmt_call(self, cargs)
-                       i += 1
-               end
-               #s.append(" ]")
-               #while i < l do
-               #       s.append(" {n.super_init_calls[i]}")
-               #       i += 1
-               #end
-               #if stop_prop != null then s.append(" (stop at {stop_prop})")
-               #n.printl("implicit calls in {n.method}: {s}") 
-       end
-end
-
-# A C function currently written
-class CFunctionContext
-       readable var _visitor: CompilerVisitor
-
-       # Next available variable number
-       var _variable_index: Int = 0
-
-       # Total number of variable
-       var _variable_index_max: Int = 0
-
-       # Association between nit variable and the corrsponding c variable index
-       var _varindexes: Map[Variable, Int] = new HashMap[Variable, Int]
-
-       # Are we currenlty in a closure definition?
-       readable writable var _closure: nullable NitMethodContext = null
-
-       # Return the cvariable of a Nit variable
-       fun varname(v: Variable): String
-       do
-               if v isa ClosureVariable then
-                       return closure_variable(_varindexes[v])
-               else
-                       return variable(_varindexes[v])
-               end
-       end
-
-       # Return the next available variable
-       fun get_var(comment: nullable String): String
-       do
-               var v = variable(_variable_index)
-               _variable_index = _variable_index + 1
-               if _variable_index > _variable_index_max then
-                       _variable_index_max = _variable_index 
-               end
-               if comment != null then
-                       visitor.add_instr("/* Register {v}: {comment} */")
-               end
-               return v
-       end
-
-       fun register_variable(v: Variable): String
-       do
-               _varindexes[v] = _variable_index
-               var s = get_var("Local variable")
-               return s
-       end
-
-       # Next available closure variable number
-       var _closurevariable_index: Int = 0
-
-       fun register_closurevariable(v: ClosureVariable): String
-       do
-               var s = "closurevariable[{_closurevariable_index}]"
-               _varindexes[v] = _closurevariable_index
-               _closurevariable_index += 1
-               if _closure != null then
-                       return "(closctx->{s})"
-               else
-                       return s
-               end
-       end
-
-       # Return the ith cvariable
-       protected fun variable(i: Int): String
-       do
-               if closure != null then
-                       var vn = once new Array[String]
-                       if vn.length <= i then
-                               for j in [vn.length..i] do
-                                       vn[j] = "(closctx->variable[{j}])"
-                               end
-                       end
-                       return vn[i]
-               else
-                       var vn = once new Array[String]
-                       if vn.length <= i then
-                               for j in [vn.length..i] do
-                                       vn[j] = "variable[{j}]"
-                               end
-                       end
-                       return vn[i]
-               end
-       end
-
-       # Return the ith closurevariable
-       protected fun closure_variable(i: Int): String
-       do
-               if closure != null then
-                       return "(closctx->closurevariable[{i}])"
-               else
-                       return "closurevariable[{i}]"
-               end
-       end
-
-       # Is s a valid variable
-       protected fun is_valid_variable(s: String): Bool
-       do
-               for i in [0.._variable_index[ do
-                       if s == variable(i) then return true
-               end
-               return false
-       end
-
-       # Mark the variable available
-       fun free_var(v: String)
-       do
-               # FIXME: So ugly..
-               if v == variable(_variable_index-1) then
-                       _variable_index = _variable_index - 1
-               end
-       end
-
-       # Generate the local variable declarations
-       # To use at the end of the C function once all variables are known
-       fun generate_var_decls
-       do
-               if _variable_index_max > 0 then
-                       visitor.add_decl("val_t variable[{_variable_index_max}];")
-               else
-                       visitor.add_decl("val_t *variable = NULL;")
-               end
-               if _closurevariable_index > 0 then
-                       visitor.add_decl("struct WBT_ *closurevariable[{_closurevariable_index}];")
-               else
-                       visitor.add_decl("struct WBT_ **closurevariable = NULL;")
-               end
-       end
-
-       init(v: CompilerVisitor) do _visitor = v
-end
-
-# A Nit method currenlty compiled
-class NitMethodContext
-       # Current method compiled
-       readable var _method: nullable MMMethod
-
-       # Association between parameters and the corresponding variables
-       readable writable var _method_params: nullable Array[ParamVariable]
-
-       # Where a nit return must branch
-       readable writable var _return_label: nullable String
-
-       # Where a nit break must branch
-       readable writable var _break_label: nullable String
-
-       # Where a nit continue must branch
-       readable writable var _continue_label: nullable String
-
-       # Variable where a functionnal nit return must store its value
-       readable writable var _return_value: nullable String
-
-       # Variable where a functionnal nit break must store its value
-       readable writable var _break_value: nullable String
-
-       # Variable where a functionnal nit continue must store its value
-       readable writable var _continue_value: nullable String
-
-       init(method: nullable MMMethod)
-       do
-               _method = method
-       end
-end
-
-###############################################################################
-
-redef class ClosureVariable
-       readable writable var _ctypename: nullable String
-end
-
-redef class MMAttribute
-       # Compile a read acces on selffor a given reciever.
-       fun compile_isset(v: CompilerVisitor, n: PNode, recv: String): String
-       do
-               return "TAG_Bool({global.attr_access}({recv})!=NIT_NULL) /* isset {local_class}::{name}*/"
-       end
-
-       # Compile a read acces on selffor a given reciever.
-       fun compile_read_access(v: CompilerVisitor, n: PNode, recv: String): String
-       do
-               var res = "{global.attr_access}({recv}) /*{local_class}::{name}*/"
-               if not signature.return_type.is_nullable then
-                       res = v.ensure_var(res, "{local_class}::{name}")
-                       v.add_instr("if ({res} == NIT_NULL) \{ fprintf(stderr, \"Uninitialized attribute %s\", \"{name}\"); {v.printf_locate_error(n)} nit_exit(1); } /* implicit isset */;")
-               end
-               return res
-       end
-
-       # Compile a write acces on selffor a given reciever.
-       fun compile_write_access(v: CompilerVisitor, n: nullable PNode, recv: String, value: String)
-       do
-               v.add_instr("{global.attr_access}({recv}) /*{local_class}::{name}*/ = {value};")
-       end
-end
-
-redef class MMLocalProperty
-       # Compile the property as a C property
-       fun compile_property_to_c(v: CompilerVisitor) do end
-end
-
-redef class MMMethod
-       # Compile as an expression.
-       # require that signature.return_type != null
-       fun compile_expr_call(v: CompilerVisitor, cargs: Array[String]): String
-       do
-               assert signature.return_type != null
-               var s = intern_compile_call(v, cargs)
-               assert s != null
-               return s
-       end
-
-       # Compile as a statement.
-       # require that signature.return_type == null
-       fun compile_stmt_call(v: CompilerVisitor, cargs: Array[String])
-       do
-               assert signature.return_type == null
-               var s = intern_compile_call(v, cargs)
-               assert s == null
-       end
-
-       # Compile a call on self for given arguments
-       # Most calls are compiled with a table access,
-       # primitive calles are inlined
-       # == and != are guarded and possibly inlined
-       private fun intern_compile_call(v: CompilerVisitor, cargs: Array[String]): nullable String
-       do
-               var i = self
-               if i isa MMSrcMethod then
-                       if i isa MMMethSrcMethod and i.node isa AInternMethPropdef or 
-                               (i.local_class.name == (once "Array".to_symbol) and name == (once "[]".to_symbol))
-                       then
-                               var e = i.do_compile_inside(v, cargs)
-                               return e
-                       end
-               end
-               var ee = once "==".to_symbol
-               var ne = once "!=".to_symbol
-               if name == ne then
-                       var eqp = signature.recv.local_class.select_method(ee)
-                       var eqcall = eqp.compile_expr_call(v, cargs)
-                       return "TAG_Bool(!UNTAG_Bool({eqcall}))"
-               end
-               if global.is_init then
-                       cargs = cargs.to_a
-                       cargs.add("init_table /*YYY*/")
-               end
-
-               var m = "{global.meth_call}({cargs[0]})"
-               var vcall = "{m}({cargs.join(", ")}) /*{local_class}::{name}*/"
-               if name == ee then
-                       vcall = "UNTAG_Bool({vcall})"
-                       var obj = once "Object".to_symbol
-                       if i.local_class.name == obj then
-                               vcall = "(({m}=={i.cname})?(IS_EQUAL_NN({cargs[0]},{cargs[1]})):({vcall}))"
-                       end
-                       vcall = "TAG_Bool(({cargs.first} == {cargs[1]}) || (({cargs.first} != NIT_NULL) && {vcall}))"
-               end
-               if signature.return_type != null then
-                       return vcall
-               else
-                       v.add_instr(vcall + ";")
-                       return null
-               end
-       end
-
-       # Compile a call on self for given arguments and given closures
-       fun compile_call_and_closures(v: CompilerVisitor, cargs: Array[String], clos_defs: nullable Array[PClosureDef]): nullable String
-       do
-               var ve: String
-               var arity = 0
-               if clos_defs != null then arity = clos_defs.length
-
-               # Prepare result value.
-               # In case of procedure, the return value is still used to intercept breaks
-               var old_bv = v.nmc.break_value
-               ve = v.cfc.get_var("Closure return value and escape marker")
-               v.nmc.break_value = ve
-
-               # Compile closure to c function
-               var realcargs = new Array[String] # Args to pass to the C function call
-               var closcns = new Array[String] # Closure C structure names
-               realcargs.add_all(cargs)
-               for i in [0..arity[ do
-                       var cn = clos_defs[i].compile_closure(v, closure_cname(i))
-                       closcns.add(cn)
-                       realcargs.add(cn)
-               end
-               for i in [arity..signature.closures.length[ do
-                       realcargs.add("NULL")
-               end
-
-               v.nmc.break_value = old_bv
-
-               # Call
-               var e = intern_compile_call(v, realcargs)
-               if e != null then
-                       v.add_assignment(ve, e)
-                       e = ve
-               end
-
-               # Intercept returns and breaks
-               for i in [0..arity[ do
-                       # A break or a return is intercepted
-                       v.add_instr("if ({closcns[i]}->has_broke != NULL) \{")
-                       v.indent
-                       # A passtrought break or a return is intercepted: go the the next closure
-                       v.add_instr("if ({closcns[i]}->has_broke != &({ve})) \{")
-                       v.indent
-                       if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = {closcns[i]}->has_broke; closctx->broke_value = {closcns[i]}->broke_value;")
-                       v.add_instr("goto {v.nmc.return_label};")
-                       v.unindent
-                       # A direct break is interpected
-                       if e != null then
-                               # overwrite the returned value in a function
-                               v.add_instr("\} else {ve} = {closcns[i]}->broke_value;")
-                       else
-                               # Do nothing in a procedure
-                               v.add_instr("\}")
-                       end
-                       v.unindent
-                       v.add_instr("\}")
-               end
-               return e
-       end
-
-       # Compile a call as constructor with given args
-       fun compile_constructor_call(v: CompilerVisitor, recvtype: MMType, cargs: Array[String]): String
-       do
-               return "NEW_{recvtype.local_class}_{global.intro.cname}({cargs.join(", ")}) /*new {recvtype}*/"
-       end
-
-       # Compile a call as call-next-method on self with given args
-       fun compile_super_call(v: CompilerVisitor, cargs: Array[String]): String
-       do
-               if global.is_init then cargs.add("init_table")
-               var m = "{super_meth_call}({cargs[0]})"
-               var vcall = "{m}({cargs.join(", ")}) /*super {local_class}::{name}*/"
-               return vcall
-       end
-
-       # Cname of the i-th closure C struct type
-       protected fun closure_cname(i: Int): String
-       do
-               return "FWBT_{cname}_{i}"
-       end
-
-       # Compile and declare the signature to C
-       protected fun decl_csignature(v: CompilerVisitor, args: Array[String]): String
-       do
-               var params = new Array[String]
-               params.add("val_t {args[0]}")
-               for i in [0..signature.arity[ do
-                       var p = "val_t {args[i+1]}"
-                       params.add(p)
-               end
-
-               var first_closure_index = signature.arity + 1 # Wich parameter is the first closure
-               for i in [0..signature.closures.length[ do
-                       var closcn = closure_cname(i)
-                       var cs = signature.closures[i].signature # Closure signature
-                       var subparams = new Array[String] # Parameters of the closure
-                       subparams.add("struct WBT_ *")
-                       for j in [0..cs.arity[ do
-                               var p = "val_t"
-                               subparams.add(p)
-                       end
-                       var r = "void"
-                       if cs.return_type != null then r = "val_t"
-                       params.add("struct WBT_ *{args[first_closure_index+i]}")
-                       v.add_decl("typedef {r} (*{closcn})({subparams.join(", ")});")
-               end
-
-               if global.is_init then
-                       params.add("int* init_table")
-               end
-
-               var ret: String
-               if signature.return_type != null then
-                       ret = "val_t"
-               else
-                       ret = "void"
-               end
-
-               var p = params.join(", ")
-               var s = "{ret} {cname}({p})"
-               v.add_decl("typedef {ret} (* {cname}_t)({p});")
-               v.add_decl(s + ";")
-               return s
-       end
-
-       redef fun compile_property_to_c(v)
-       do
-               v.cfc = new CFunctionContext(v)
-
-               var args = new Array[String]
-               args.add(" self")
-               for i in [0..signature.arity[ do
-                       args.add(" param{i}")
-               end
-               for i in [0..signature.closures.length[ do
-                       args.add(" wd{i}")
-               end
-               var cs = decl_csignature(v, args)
-               v.add_decl("#define LOCATE_{cname} \"{full_name}\"")
-
-               v.add_instr("{cs} \{")
-               v.indent
-               var ctx_old = v.ctx
-               v.ctx = new CContext
-
-               v.out_contexts.clear
-
-               var itpos: nullable String = null
-               if global.is_init then
-                       itpos = "itpos{v.new_number}"
-                       v.add_decl("int {itpos} = VAL2OBJ(self)->vft[{local_class.global.init_table_pos_id}].i;")
-                       v.add_instr("if (init_table[{itpos}]) return;")
-               end
-
-               var ln = 0
-               var s = self
-               if s.node != null then ln = s.node.location.line_start
-               v.add_decl("struct trace_t trace = \{NULL, NULL, {ln}, LOCATE_{cname}};")
-               v.add_instr("trace.prev = tracehead; tracehead = &trace;")
-               v.add_instr("trace.file = LOCATE_{module.name};")
-
-               var s = do_compile_inside(v, args)
-
-               if itpos != null then
-                       v.add_instr("init_table[{itpos}] = 1;")
-               end
-
-               v.add_instr("tracehead = trace.prev;")
-               if s == null then
-                       v.add_instr("return;")
-               else
-                       v.add_instr("return {s};")
-               end
-
-               v.cfc.generate_var_decls
-
-               ctx_old.append(v.ctx)
-               v.ctx = ctx_old
-               v.unindent
-               v.add_instr("}")
-
-               for ctx in v.out_contexts do v.ctx.merge(ctx)
-       end
-
-       # Compile the method body inline
-       fun do_compile_inside(v: CompilerVisitor, params: Array[String]): nullable String is abstract
-end
-
-redef class MMReadImplementationMethod
-       redef fun do_compile_inside(v, params)
-       do
-               return node.prop.compile_read_access(v, node, params[0])
-       end
-end
-
-redef class MMWriteImplementationMethod
-       redef fun do_compile_inside(v, params)
-       do
-               node.prop.compile_write_access(v, node, params[0], params[1])
-               return null
-       end
-end
-
-redef class MMMethSrcMethod
-       redef fun do_compile_inside(v, params)
-       do
-               return node.do_compile_inside(v, self, params)
-       end
-end
-
-redef class MMImplicitInit
-       redef fun do_compile_inside(v, params)
-       do
-               var f = params.length - unassigned_attributes.length
-               var recv = params.first
-               for sp in super_inits do
-                       assert sp isa MMMethod
-                       var args_recv = [recv]
-                       if sp == super_init then
-                               var args = new Array[String].with_capacity(f)
-                               args.add(recv)
-                               for i in [1..f[ do
-                                       args.add(params[i])
-                               end
-                               sp.compile_stmt_call(v, args)
-                       else
-                               sp.compile_stmt_call(v, args_recv)
-                       end
-               end
-               for i in [f..params.length[ do
-                       var attribute = unassigned_attributes[i-f]
-                       attribute.compile_write_access(v, null, recv, params[i])
-               end
-               return null
-       end
-end
-
-redef class MMType
-       # Compile a subtype check to self
-       # Return a NIT Bool
-       fun compile_cast(v: CompilerVisitor, recv: String, fromtype: MMType): String
-       do
-               # Fixme: handle formaltypes
-               var g = local_class.global
-               var s = ""
-               if fromtype.is_nullable then
-                       if self.is_nullable then
-                               s = "({recv}==NIT_NULL) || "
-                       else
-                               s = "({recv}!=NIT_NULL) && "
-                       end
-               else
-                       # FIXME This is used to not break code without the nullable KW
-                       s = "({recv}==NIT_NULL) || "
-               end
-               return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {self}*/"
-       end
-
-       # Compile a cast assertion
-       fun compile_type_check(v: CompilerVisitor, recv: String, n: PNode, fromtype: MMType)
-       do
-               # Fixme: handle formaltypes
-               var g = local_class.global
-               var s = ""
-               if fromtype.is_nullable then
-                       if self.is_nullable then
-                               s = "({recv}!=NIT_NULL) && "
-                       else
-                               s = "({recv}==NIT_NULL) || "
-                       end
-               else
-                       # FIXME This is used to not break code without the nullable KW
-                       s = "({recv}!=NIT_NULL) && "
-               end
-               v.add_instr("if ({s}!VAL_ISA({recv}, {g.color_id}, {g.id_id})) \{ fprintf(stderr, \"Cast failed\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;")
-       end
-
-       # Compile a notnull cast assertion
-       fun compile_notnull_check(v: CompilerVisitor, recv: String, n: PNode)
-       do
-               if is_nullable then
-                       v.add_instr("if (({recv}==NIT_NULL)) \{ fprintf(stderr, \"Cast failed\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;")
-               end
-       end
-end
-
-###############################################################################
-
-redef class AMethPropdef
-       # Compile the method body
-       fun do_compile_inside(v: CompilerVisitor, method: MMMethod, params: Array[String]): nullable String is abstract
-end
-
-redef class PSignature
-       fun compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String]) is abstract
-end
-
-redef class ASignature
-       redef fun compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String])
-       do
-               for ap in n_params do
-                       var cname = v.cfc.register_variable(ap.variable)
-                       v.nmc.method_params.add(ap.variable)
-                       var orig_type = orig_sig[ap.position]
-                       if not orig_type < ap.variable.stype.as(not null) then
-                               # FIXME: do not test always
-                               # FIXME: handle formal types
-                               v.add_instr("/* check if p<{ap.variable.stype} with p:{orig_type} */")
-                               ap.variable.stype.compile_type_check(v, params[ap.position], ap, orig_type)
-                       end
-                       v.add_assignment(cname, params[ap.position])
-               end
-               for i in [0..n_closure_decls.length[ do
-                       var wd = n_closure_decls[i]
-                       var cname = v.cfc.register_closurevariable(wd.variable)
-                       wd.variable.ctypename = v.nmc.method.closure_cname(i)
-                       v.add_assignment(cname, "{params[orig_sig.arity + i]}")
-               end
-       end
-end
-
-redef class AConcreteMethPropdef
-       redef fun do_compile_inside(v, method, params)
-       do
-               var old_nmc = v.nmc
-               v.nmc = new NitMethodContext(method)
-
-               var selfcname = v.cfc.register_variable(self_var)
-               v.add_assignment(selfcname, params[0])
-               params.shift
-               v.nmc.method_params = [self_var]
-
-               var orig_meth: MMLocalProperty = method.global.intro
-               var orig_sig = orig_meth.signature_for(method.signature.recv)
-               if n_signature != null then
-                       n_signature.compile_parameters(v, orig_sig, params)
-               end
-
-               v.nmc.return_label = "return_label{v.new_number}"
-               v.nmc.return_value = v.cfc.get_var("Method return value and escape marker")
-               if self isa AConcreteInitPropdef then
-                       v.invoke_super_init_calls_after(null)
-               end
-               v.compile_stmt(n_block)
-               v.add_instr("{v.nmc.return_label}: while(false);")
-
-               var ret: nullable String = null
-               if method.signature.return_type != null then
-                       ret = v.nmc.return_value
-               end
-
-               v.nmc = old_nmc
-               return ret
-       end
-end
-
-redef class ADeferredMethPropdef
-       redef fun do_compile_inside(v, method, params)
-       do
-               v.add_instr("fprintf(stderr, \"Deferred method called\");")
-               v.add_instr(v.printf_locate_error(self))
-               v.add_instr("nit_exit(1);")
-               if method.signature.return_type != null then
-                       return("NIT_NULL")
-               else
-                       return null
-               end
-       end
-end
-
-redef class AExternMethPropdef
-       redef fun do_compile_inside(v, method, params)
-       do
-               var ename = "{method.module.name}_{method.local_class.name}_{method.local_class.name}_{method.name}_{method.signature.arity}"
-               if n_extern != null then
-                       ename = n_extern.text
-                       ename = ename.substring(1, ename.length-2)
-               end
-               var sig = method.signature
-               if params.length != sig.arity + 1 then
-                       printl("par:{params.length} sig:{sig.arity}")
-               end
-               var args = new Array[String]
-               args.add(sig.recv.unboxtype(params[0]))
-               for i in [0..sig.arity[ do
-                       args.add(sig[i].unboxtype(params[i+1]))
-               end
-               var s = "{ename}({args.join(", ")})"
-               if sig.return_type != null then
-                       return sig.return_type.boxtype(s)
-               else
-                       v.add_instr("{s};")
-                       return null
-               end
-       end
-end
-
-redef class AInternMethPropdef
-       redef fun do_compile_inside(v, method, p)
-       do
-               var c = method.local_class.name
-               var n = method.name
-               var s: nullable String = null
-               if c == once "Int".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "{p[0]}"
-                       else if n == once "unary -".to_symbol then
-                               s = "TAG_Int(-UNTAG_Int({p[0]}))"
-                       else if n == once "output".to_symbol then
-                               v.add_instr("printf(\"%ld\\n\", UNTAG_Int({p[0]}));")
-                       else if n == once "ascii".to_symbol then
-                               s = "TAG_Char(UNTAG_Int({p[0]}))"
-                       else if n == once "succ".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})+1)"
-                       else if n == once "prec".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})-1)"
-                       else if n == once "to_f".to_symbol then
-                               s = "BOX_Float((float)UNTAG_Int({p[0]}))"
-                       else if n == once "+".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})+UNTAG_Int({p[1]}))" 
-                       else if n == once "-".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})-UNTAG_Int({p[1]}))" 
-                       else if n == once "*".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})*UNTAG_Int({p[1]}))" 
-                       else if n == once "/".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})/UNTAG_Int({p[1]}))" 
-                       else if n == once "%".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})%UNTAG_Int({p[1]}))" 
-                       else if n == once "<".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int({p[0]})<UNTAG_Int({p[1]}))" 
-                       else if n == once ">".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int({p[0]})>UNTAG_Int({p[1]}))" 
-                       else if n == once "<=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int({p[0]})<=UNTAG_Int({p[1]}))" 
-                       else if n == once ">=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Int({p[0]})>=UNTAG_Int({p[1]}))" 
-                       else if n == once "lshift".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})<<UNTAG_Int({p[1]}))" 
-                       else if n == once "rshift".to_symbol then
-                               s = "TAG_Int(UNTAG_Int({p[0]})>>UNTAG_Int({p[1]}))"
-                       else if n == once "==".to_symbol then
-                               s = "TAG_Bool(({p[0]})==({p[1]}))" 
-                       else if n == once "!=".to_symbol then
-                               s = "TAG_Bool(({p[0]})!=({p[1]}))" 
-                       end
-               else if c == once "Float".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int((bigint)UNBOX_Float({p[0]}))"
-                       else if n == once "unary -".to_symbol then
-                               s = "BOX_Float(-UNBOX_Float({p[0]}))"
-                       else if n == once "output".to_symbol then
-                               v.add_instr("printf(\"%f\\n\", UNBOX_Float({p[0]}));")
-                       else if n == once "to_i".to_symbol then
-                               s = "TAG_Int((bigint)UNBOX_Float({p[0]}))"
-                       else if n == once "+".to_symbol then
-                               s = "BOX_Float(UNBOX_Float({p[0]})+UNBOX_Float({p[1]}))" 
-                       else if n == once "-".to_symbol then
-                               s = "BOX_Float(UNBOX_Float({p[0]})-UNBOX_Float({p[1]}))" 
-                       else if n == once "*".to_symbol then
-                               s = "BOX_Float(UNBOX_Float({p[0]})*UNBOX_Float({p[1]}))" 
-                       else if n == once "/".to_symbol then
-                               s = "BOX_Float(UNBOX_Float({p[0]})/UNBOX_Float({p[1]}))" 
-                       else if n == once "<".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float({p[0]})<UNBOX_Float({p[1]}))" 
-                       else if n == once ">".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float({p[0]})>UNBOX_Float({p[1]}))" 
-                       else if n == once "<=".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float({p[0]})<=UNBOX_Float({p[1]}))" 
-                       else if n == once ">=".to_symbol then
-                               s = "TAG_Bool(UNBOX_Float({p[0]})>=UNBOX_Float({p[1]}))" 
-                       end
-               else if c == once "Char".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(UNTAG_Char({p[0]}))"
-                       else if n == once "unary -".to_symbol then
-                               s = "TAG_Char(-UNTAG_Char({p[0]}))"
-                       else if n == once "output".to_symbol then
-                               v.add_instr("printf(\"%c\", (unsigned char)UNTAG_Char({p[0]}));")
-                       else if n == once "ascii".to_symbol then
-                               s = "TAG_Int((unsigned char)UNTAG_Char({p[0]}))"
-                       else if n == once "succ".to_symbol then
-                               s = "TAG_Char(UNTAG_Char({p[0]})+1)"
-                       else if n == once "prec".to_symbol then
-                               s = "TAG_Char(UNTAG_Char({p[0]})-1)"
-                       else if n == once "to_i".to_symbol then
-                               s = "TAG_Int(UNTAG_Char({p[0]})-'0')"
-                       else if n == once "+".to_symbol then
-                               s = "TAG_Char(UNTAG_Char({p[0]})+UNTAG_Char({p[1]}))" 
-                       else if n == once "-".to_symbol then
-                               s = "TAG_Char(UNTAG_Char({p[0]})-UNTAG_Char({p[1]}))" 
-                       else if n == once "*".to_symbol then
-                               s = "TAG_Char(UNTAG_Char({p[0]})*UNTAG_Char({p[1]}))" 
-                       else if n == once "/".to_symbol then
-                               s = "TAG_Char(UNTAG_Char({p[0]})/UNTAG_Char({p[1]}))" 
-                       else if n == once "%".to_symbol then
-                               s = "TAG_Char(UNTAG_Char({p[0]})%UNTAG_Char({p[1]}))" 
-                       else if n == once "<".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char({p[0]})<UNTAG_Char({p[1]}))" 
-                       else if n == once ">".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char({p[0]})>UNTAG_Char({p[1]}))" 
-                       else if n == once "<=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char({p[0]})<=UNTAG_Char({p[1]}))" 
-                       else if n == once ">=".to_symbol then
-                               s = "TAG_Bool(UNTAG_Char({p[0]})>=UNTAG_Char({p[1]}))" 
-                       else if n == once "==".to_symbol then
-                               s = "TAG_Bool(({p[0]})==({p[1]}))" 
-                       else if n == once "!=".to_symbol then
-                               s = "TAG_Bool(({p[0]})!=({p[1]}))" 
-                       end
-               else if c == once "Bool".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(UNTAG_Bool({p[0]}))"
-                       else if n == once "unary -".to_symbol then
-                               s = "TAG_Bool(-UNTAG_Bool({p[0]}))"
-                       else if n == once "output".to_symbol then
-                               v.add_instr("(void)printf(UNTAG_Bool({p[0]})?\"true\\n\":\"false\\n\");")
-                       else if n == once "ascii".to_symbol then
-                               s = "TAG_Bool(UNTAG_Bool({p[0]}))"
-                       else if n == once "to_i".to_symbol then
-                               s = "TAG_Int(UNTAG_Bool({p[0]}))"
-                       else if n == once "==".to_symbol then
-                               s = "TAG_Bool(({p[0]})==({p[1]}))" 
-                       else if n == once "!=".to_symbol then
-                               s = "TAG_Bool(({p[0]})!=({p[1]}))" 
-                       end
-               else if c == once "NativeArray".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(UNBOX_NativeArray({p[0]}))"
-                       else if n == once "[]".to_symbol then
-                               s = "UNBOX_NativeArray({p[0]})[UNTAG_Int({p[1]})]"
-                       else if n == once "[]=".to_symbol then
-                               v.add_instr("UNBOX_NativeArray({p[0]})[UNTAG_Int({p[1]})]={p[2]};")
-                       else if n == once "copy_to".to_symbol then
-                               v.add_instr("(void)memcpy(UNBOX_NativeArray({p[1]}), UNBOX_NativeArray({p[0]}), UNTAG_Int({p[2]})*sizeof(val_t));")
-                       end     
-               else if c == once "NativeString".to_symbol then
-                       if n == once "object_id".to_symbol then
-                               s = "TAG_Int(UNBOX_NativeString({p[0]}))"
-                       else if n == once "atoi".to_symbol then
-                               s = "TAG_Int(atoi(UNBOX_NativeString({p[0]})))"
-                       else if n == once "[]".to_symbol then
-                               s = "TAG_Char(UNBOX_NativeString({p[0]})[UNTAG_Int({p[1]})])"
-                       else if n == once "[]=".to_symbol then
-                               v.add_instr("UNBOX_NativeString({p[0]})[UNTAG_Int({p[1]})]=UNTAG_Char({p[2]});")
-                       else if n == once "copy_to".to_symbol then
-                               v.add_instr("(void)memcpy(UNBOX_NativeString({p[1]})+UNTAG_Int({p[4]}), UNBOX_NativeString({p[0]})+UNTAG_Int({p[3]}), UNTAG_Int({p[2]}));")
-                       end
-               else if n == once "object_id".to_symbol then
-                       s = "TAG_Int((bigint){p[0]})"
-               else if n == once "sys".to_symbol then
-                       s = "(G_sys)"
-               else if n == once "is_same_type".to_symbol then
-                       s = "TAG_Bool((VAL2VFT({p[0]})==VAL2VFT({p[1]})))"
-               else if n == once "exit".to_symbol then
-                       v.add_instr("exit(UNTAG_Int({p[1]}));")
-               else if n == once "calloc_array".to_symbol then
-                       s = "BOX_NativeArray((val_t*)malloc((UNTAG_Int({p[1]}) * sizeof(val_t))))"
-               else if n == once "calloc_string".to_symbol then
-                       s = "BOX_NativeString((char*)malloc((UNTAG_Int({p[1]}) * sizeof(char))))"
-
-               else
-                       stderr.write("{location}: Fatal error: unknown intern method {method.full_name}.\n")
-                       exit(1)
-               end
-               if method.signature.return_type != null and s == null then
-                       s = "NIT_NULL /*stub*/"
-               end
-               return s
-       end
-end
-
-###############################################################################
-
-redef class PExpr
-       # Compile the node as an expression
-       # Only the visitor should call it
-       fun compile_expr(v: CompilerVisitor): String is abstract
-
-       # Prepare a call of node as a statement
-       # Only the visitor should call it
-       # It's used for local variable managment
-       fun prepare_compile_stmt(v: CompilerVisitor) do end
-
-       # Compile the node as a statement
-       # Only the visitor should call it
-       fun compile_stmt(v: CompilerVisitor) do printl("Error!")
-end
-
-redef class ABlockExpr
-       redef fun compile_stmt(v)
-       do
-               for n in n_expr do
-                       v.compile_stmt(n)
-               end
-       end
-end
-
-redef class AVardeclExpr
-       redef fun prepare_compile_stmt(v)
-       do
-               v.cfc.register_variable(variable)
-       end
-
-       redef fun compile_stmt(v)
-       do
-               var cname = v.cfc.varname(variable)
-               if n_expr == null then
-                       v.add_instr("/*{cname} is variable {variable.name}*/")
-               else
-                       var e = v.compile_expr(n_expr.as(not null))
-                       v.add_assignment(cname, e)
-               end
-       end
-end
-
-redef class AReturnExpr
-       redef fun compile_stmt(v)
-       do
-               if n_expr != null then
-                       var e = v.compile_expr(n_expr.as(not null))
-                       v.add_assignment(v.nmc.return_value.as(not null), e)
-               end
-               if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = &({v.nmc.return_value});")
-               v.add_instr("goto {v.nmc.return_label};")
-       end
-end
-
-redef class ABreakExpr
-       redef fun compile_stmt(v)
-       do
-               if n_expr != null then
-                       var e = v.compile_expr(n_expr.as(not null))
-                       v.add_assignment(v.nmc.break_value.as(not null), e)
-               end
-               if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = &({v.nmc.break_value}); closctx->broke_value = *closctx->has_broke;")
-               v.add_instr("goto {v.nmc.break_label};")
-       end
-end
-
-redef class AContinueExpr
-       redef fun compile_stmt(v)
-       do
-               if n_expr != null then
-                       var e = v.compile_expr(n_expr.as(not null))
-                       v.add_assignment(v.nmc.continue_value.as(not null), e)
-               end
-               v.add_instr("goto {v.nmc.continue_label};")
-       end
-end
-
-redef class AAbortExpr
-       redef fun compile_stmt(v)
-       do
-               v.add_instr("fprintf(stderr, \"Aborted\"); {v.printf_locate_error(self)} nit_exit(1);")
-       end
-end
-
-redef class ADoExpr
-       redef fun compile_stmt(v)
-       do
-               v.compile_stmt(n_block)
-       end
-end
-
-redef class AIfExpr
-       redef fun compile_stmt(v)
-       do
-               var e = v.compile_expr(n_expr)
-               v.add_instr("if (UNTAG_Bool({e})) \{ /*if*/")
-               v.cfc.free_var(e)
-               if n_then != null then
-                       v.indent
-                       v.compile_stmt(n_then)
-                       v.unindent
-               end
-               if n_else != null then
-                       v.add_instr("} else \{ /*if*/")
-                       v.indent
-                       v.compile_stmt(n_else)
-                       v.unindent
-               end
-               v.add_instr("}")
-       end
-end
-
-redef class AIfexprExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               v.add_instr("if (UNTAG_Bool({e})) \{ /*if*/")
-               v.cfc.free_var(e)
-               v.indent
-               var e = v.ensure_var(v.compile_expr(n_then), "Then value")
-               v.unindent
-               v.add_instr("} else \{ /*if*/")
-               v.cfc.free_var(e)
-               v.indent
-               var e2 = v.ensure_var(v.compile_expr(n_else), "Else value")
-               v.add_assignment(e, e2)
-               v.unindent
-               v.add_instr("}")
-               return e
-       end
-end
-
-class AControlableBlock
-special PExpr
-       fun compile_inside_block(v: CompilerVisitor) is abstract
-       redef fun compile_stmt(v)
-       do
-               var old_break_label = v.nmc.break_label
-               var old_continue_label = v.nmc.continue_label
-               var id = v.new_number
-               v.nmc.break_label = "break_{id}"
-               v.nmc.continue_label = "continue_{id}"
-
-               compile_inside_block(v)
-
-
-               v.nmc.break_label = old_break_label
-               v.nmc.continue_label = old_continue_label
-       end
-end
-
-redef class AWhileExpr
-special AControlableBlock
-       redef fun compile_inside_block(v)
-       do
-               v.add_instr("while (true) \{ /*while*/")
-               v.indent
-               var e = v.compile_expr(n_expr)
-               v.add_instr("if (!UNTAG_Bool({e})) break; /* while*/")
-               v.cfc.free_var(e)
-               v.compile_stmt(n_block)
-               v.add_instr("{v.nmc.continue_label}: while(0);")
-               v.unindent
-               v.add_instr("}")
-               v.add_instr("{v.nmc.break_label}: while(0);")
-       end
-end
-
-redef class AForExpr
-special AControlableBlock
-       redef fun compile_inside_block(v)
-       do
-               var e = v.compile_expr(n_expr)
-               var ittype = meth_iterator.signature.return_type
-               v.cfc.free_var(e)
-               var iter = v.cfc.get_var("For iterator")
-               v.add_assignment(iter, meth_iterator.compile_expr_call(v, [e]))
-               v.add_instr("while (true) \{ /*for*/")
-               v.indent
-               var ok = v.cfc.get_var("For 'is_ok' result")
-               v.add_assignment(ok, meth_is_ok.compile_expr_call(v, [iter]))
-               v.add_instr("if (!UNTAG_Bool({ok})) break; /*for*/")
-               v.cfc.free_var(ok)
-               var e = meth_item.compile_expr_call(v, [iter])
-               e = v.ensure_var(e, "For item")
-               var cname = v.cfc.register_variable(variable)
-               v.add_assignment(cname, e)
-               v.compile_stmt(n_block)
-               v.add_instr("{v.nmc.continue_label}: while(0);")
-               meth_next.compile_stmt_call(v, [iter])
-               v.unindent
-               v.add_instr("}")
-               v.add_instr("{v.nmc.break_label}: while(0);")
-       end
-end
-
-redef class AAssertExpr
-       redef fun compile_stmt(v)
-       do
-               var e = v.compile_expr(n_expr)
-               var s = ""
-               if n_id != null then
-                       s = " '{n_id.text}' "
-               end
-               v.add_instr("if (!UNTAG_Bool({e})) \{ fprintf(stderr, \"Assert%s failed\", \"{s}\"); {v.printf_locate_error(self)} nit_exit(1);}")
-       end
-end
-
-redef class AVarExpr
-       redef fun compile_expr(v)
-       do
-               return " {v.cfc.varname(variable)} /*{variable.name}*/"
-       end
-end
-
-redef class AVarAssignExpr
-       redef fun compile_stmt(v)
-       do
-               var e = v.compile_expr(n_value)
-               v.add_assignment(v.cfc.varname(variable), "{e} /*{variable.name}=*/")
-       end
-end
-
-redef class AVarReassignExpr
-       redef fun compile_stmt(v)
-       do
-               var e1 = v.cfc.varname(variable)
-               var e2 = v.compile_expr(n_value)
-               var e3 = assign_method.compile_expr_call(v, [e1, e2])
-               v.add_assignment(v.cfc.varname(variable), "{e3} /*{variable.name}*/")
-       end
-end
-
-redef class ASelfExpr
-       redef fun compile_expr(v)
-       do
-               return v.cfc.varname(v.nmc.method_params[0])
-       end
-end
-
-redef class AOrExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.ensure_var(v.compile_expr(n_expr), "Left 'or' operand")
-               v.add_instr("if (!UNTAG_Bool({e})) \{ /* or */")
-               v.cfc.free_var(e)
-               v.indent
-               var e2 = v.compile_expr(n_expr2)
-               v.add_assignment(e, e2)
-               v.unindent
-               v.add_instr("}")
-               return e
-       end
-end
-
-redef class AAndExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.ensure_var(v.compile_expr(n_expr), "Left 'and' operand")
-               v.add_instr("if (UNTAG_Bool({e})) \{ /* and */")
-               v.cfc.free_var(e)
-               v.indent
-               var e2 = v.compile_expr(n_expr2)
-               v.add_assignment(e, e2)
-               v.unindent
-               v.add_instr("}")
-               return e
-       end
-end
-
-redef class ANotExpr
-       redef fun compile_expr(v)
-       do
-               return " TAG_Bool(!UNTAG_Bool({v.compile_expr(n_expr)}))"
-       end
-end
-
-redef class AEeExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               var e2 = v.compile_expr(n_expr2)
-               return "TAG_Bool(IS_EQUAL_NN({e},{e2}))"
-       end
-end
-
-redef class AIsaExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               return n_type.stype.compile_cast(v, e, n_expr.stype)
-       end
-end
-
-redef class AAsCastExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               n_type.stype.compile_type_check(v, e, self, n_expr.stype)
-               return e
-       end
-end
-
-redef class AAsNotnullExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               n_expr.stype.compile_notnull_check(v, e, self)
-               return e
-       end
-end
-
-redef class ATrueExpr
-       redef fun compile_expr(v)
-       do
-               return " TAG_Bool(true)"
-       end
-end
-
-redef class AFalseExpr
-       redef fun compile_expr(v)
-       do
-               return " TAG_Bool(false)"
-       end
-end
-
-redef class AIntExpr
-       redef fun compile_expr(v)
-       do
-               return " TAG_Int({n_number.text})"
-       end
-end
-
-redef class AFloatExpr
-       redef fun compile_expr(v)
-       do
-               return "BOX_Float({n_float.text})"
-       end
-end
-
-redef class ACharExpr
-       redef fun compile_expr(v)
-       do
-               return " TAG_Char({n_char.text})"
-       end
-end
-
-redef class AStringFormExpr
-       redef fun compile_expr(v)
-       do
-               compute_string_info
-               var i = v.new_number
-               var cvar = v.cfc.get_var("Once String constant")
-               v.add_decl("static val_t once_value_{i} = NIT_NULL; /* Once value for string {cvar}*/")
-               v.add_instr("if (once_value_{i} != NIT_NULL) {cvar} = once_value_{i};")
-               v.add_instr("else \{")
-               v.indent
-               v.cfc.free_var(cvar)
-               var e = meth_with_native.compile_constructor_call(v, stype, ["BOX_NativeString(\"{_cstring}\")", "TAG_Int({_cstring_length})"])
-               v.add_assignment(cvar, e)
-               v.add_instr("once_value_{i} = {cvar};")
-               v.unindent
-               v.add_instr("}")
-               return cvar
-       end
-
-       # The raw string value
-       protected fun string_text: String is abstract
-
-       # The string in a C native format
-       protected var _cstring: nullable String
-
-       # The string length in bytes
-       protected var _cstring_length: nullable Int
-
-       # Compute _cstring and _cstring_length using string_text
-       protected fun compute_string_info
-       do
-               var len = 0
-               var str = string_text
-               var res = new Buffer
-               var i = 0
-               while i < str.length do
-                       var c = str[i]
-                       if c == '\\' then
-                               i = i + 1
-                               var c2 = str[i]
-                               if c2 != '{' and c2 != '}' then
-                                       res.add(c)
-                               end
-                               c = c2
-                       end
-                       len = len + 1
-                       res.add(c)
-                       i = i + 1
-               end
-               _cstring = res.to_s
-               _cstring_length = len
-       end
-end
-
-redef class AStringExpr
-       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AStartStringExpr
-       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AMidStringExpr
-       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-redef class AEndStringExpr
-       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
-end
-
-redef class ASuperstringExpr
-       redef fun compile_expr(v)
-       do
-               var array = meth_with_capacity.compile_constructor_call(v, atype, ["TAG_Int({n_exprs.length})"])
-               array = v.ensure_var(array, "Array (for super-string)")
-
-               for ne in n_exprs do
-                       var e = v.ensure_var(v.compile_expr(ne), "super-string element")
-                       if ne.stype != stype then
-                               v.cfc.free_var(e)
-                               e = meth_to_s.compile_expr_call(v, [e])
-                       end
-                       v.cfc.free_var(e)
-                       meth_add.compile_stmt_call(v, [array, e])
-               end
-
-               return meth_to_s.compile_expr_call(v, [array])
-       end
-end
-
-redef class ANullExpr
-       redef fun compile_expr(v)
-       do
-               return " NIT_NULL /*null*/"
-       end
-end
-
-redef class AArrayExpr
-       redef fun compile_expr(v)
-       do
-               var recv = meth_with_capacity.compile_constructor_call(v, stype, ["TAG_Int({n_exprs.length})"])
-               recv = v.ensure_var(recv, "Literal array")
-
-               for ne in n_exprs do
-                       var e = v.compile_expr(ne)
-                       meth_add.compile_stmt_call(v, [recv, e])
-               end
-               return recv
-       end
-end
-
-redef class ARangeExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               var e2 = v.compile_expr(n_expr2)
-               return meth_init.compile_constructor_call(v, stype, [e, e2])
-       end
-end
-
-redef class ASuperExpr
-       redef fun compile_stmt(v)
-       do
-               var e = intern_compile_call(v)
-               if e != null then
-                       v.add_instr(e + ";")
-               end
-       end
-
-       redef fun compile_expr(v)
-       do
-               var e = intern_compile_call(v)
-               assert e != null
-               return e
-       end
-
-       private fun intern_compile_call(v: CompilerVisitor): nullable String
-       do
-               var arity = v.nmc.method_params.length - 1
-               if init_in_superclass != null then
-                       arity = init_in_superclass.signature.arity
-               end
-               var args = new Array[String].with_capacity(arity + 1)
-               args.add(v.cfc.varname(v.nmc.method_params[0]))
-               if n_args.length != arity then
-                       for i in [0..arity[ do
-                               args.add(v.cfc.varname(v.nmc.method_params[i + 1]))
-                       end
-               else
-                       for na in n_args do
-                               args.add(v.compile_expr(na))
-                       end
-               end
-               #return "{prop.cname}({args.join(", ")}) /*super {prop.local_class}::{prop.name}*/"
-               if init_in_superclass != null then
-                       return init_in_superclass.intern_compile_call(v, args)
-               else
-                       return prop.compile_super_call(v, args)
-               end
-       end
-end
-
-redef class AAttrExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               return prop.compile_read_access(v, n_id, e)
-       end
-end
-
-redef class AAttrAssignExpr
-       redef fun compile_stmt(v)
-       do
-               var e = v.compile_expr(n_expr)
-               var e2 = v.compile_expr(n_value)
-               prop.compile_write_access(v, n_id, e, e2)
-       end
-end
-redef class AAttrReassignExpr
-       redef fun compile_stmt(v)
-       do
-               var e1 = v.compile_expr(n_expr)
-               var e2 = prop.compile_read_access(v, n_id, e1)
-               var e3 = v.compile_expr(n_value)
-               var e4 = assign_method.compile_expr_call(v, [e2, e3])
-               prop.compile_write_access(v, n_id, e1, e4)
-       end
-end
-
-redef class AIssetAttrExpr
-       redef fun compile_expr(v)
-       do
-               var e = v.compile_expr(n_expr)
-               return prop.compile_isset(v, n_id, e)
-       end
-end
-
-redef class AAbsAbsSendExpr
-       # Compile each argument and add them to the array
-       fun compile_arguments_in(v: CompilerVisitor, cargs: Array[String])
-       do
-               for a in arguments do
-                       cargs.add(v.compile_expr(a))
-               end
-       end
-
-end
-
-redef class ASendExpr
-       private fun intern_compile_call(v: CompilerVisitor): nullable String
-       do
-               var recv = v.compile_expr(n_expr)
-               var cargs = new Array[String]
-               cargs.add(recv)
-               compile_arguments_in(v, cargs)
-
-               var e: nullable String
-               if prop_signature.closures.is_empty then
-                       e = prop.intern_compile_call(v, cargs)
-               else
-                       e = prop.compile_call_and_closures(v, cargs, closure_defs)
-               end
-
-               if prop.global.is_init then
-                       v.invoke_super_init_calls_after(prop)
-               end
-               return e
-       end
-
-       redef fun compile_expr(v)
-       do
-               var e = intern_compile_call(v)
-               assert e != null
-               return e
-       end
-
-       redef fun compile_stmt(v)
-       do
-               var e = intern_compile_call(v)
-               if e != null then
-                       v.add_instr(e + ";")
-               end
-       end
-end
-
-redef class ASendReassignExpr
-       redef fun compile_expr(v) do abort
-
-       redef fun compile_stmt(v)
-       do
-               var recv = v.compile_expr(n_expr)
-               var cargs = new Array[String]
-               cargs.add(recv)
-               compile_arguments_in(v, cargs)
-
-               var e2 = read_prop.compile_expr_call(v, cargs)
-               var e3 = v.compile_expr(n_value)
-               var e4 = assign_method.compile_expr_call(v, [e2, e3])
-               cargs.add(e4)
-               prop.compile_stmt_call(v, cargs)
-       end
-end
-
-redef class ANewExpr
-       redef fun compile_expr(v)
-       do
-               var cargs = new Array[String]
-               compile_arguments_in(v, cargs)
-               return prop.compile_constructor_call(v, stype, cargs)
-       end
-
-       redef fun compile_stmt(v) do abort
-end
-
-redef class PClosureDef
-       # Compile the closure definition as a function in v.out_contexts
-       # Return the cname of the function
-       fun compile_closure(v: CompilerVisitor, closcn: String): String is abstract
-
-       # Compile the closure definition inside the current C function.
-       fun do_compile_inside(v: CompilerVisitor, params: nullable Array[String]): nullable String is abstract
-end
-
-redef class AClosureDef
-       # The cname of the function
-       readable var _cname: nullable String
-
-       redef fun compile_closure(v, closcn)
-       do
-               var ctx_old = v.ctx
-               v.ctx = new CContext
-               v.out_contexts.add(v.ctx)
-
-               var cfc_old = v.cfc.closure
-               v.cfc.closure = v.nmc
-
-               var old_rv = v.nmc.return_value
-               var old_bv = v.nmc.break_value
-               if cfc_old == null then
-                       v.nmc.return_value = "closctx->{old_rv}"
-                       v.nmc.break_value = "closctx->{old_bv}"
-               end
-
-               var cname = "OC_{v.nmc.method.cname}_{v.out_contexts.length}"
-               _cname = cname
-               var args = new Array[String]
-               for i in [0..closure.signature.arity[ do
-                       args.add(" param{i}")
-               end
-
-               var cs = decl_csignature(v, args, closcn)
-
-               v.add_instr("{cs} \{")
-               v.indent
-               var ctx_old2 = v.ctx
-               v.ctx = new CContext
-
-               v.add_decl("struct trace_t trace = \{NULL, NULL, {location.line_start}, LOCATE_{v.nmc.method.cname}};")
-               v.add_instr("trace.prev = tracehead; tracehead = &trace;")
-               
-               v.add_instr("trace.file = LOCATE_{v.module.name};")
-               var s = do_compile_inside(v, args)
-
-               v.add_instr("{v.nmc.return_label}:")
-               v.add_instr("tracehead = trace.prev;")
-               if s == null then
-                       v.add_instr("return;")
-               else
-                       v.add_instr("return {s};")
-               end
-
-               ctx_old2.append(v.ctx)
-               v.ctx = ctx_old2
-               v.unindent
-               v.add_instr("}")
-               v.ctx = ctx_old
-
-               v.cfc.closure = cfc_old
-               v.nmc.return_value = old_rv
-               v.nmc.break_value = old_bv
-
-               # Build closure
-               var closcnv = "wbclos{v.new_number}"
-               v.add_decl("struct WBT_ {closcnv};")
-               v.add_instr("{closcnv}.fun = (fun_t){cname};")
-               v.add_instr("{closcnv}.has_broke = NULL;")
-               if cfc_old != null then 
-                       v.add_instr("{closcnv}.variable = closctx->variable;")
-                       v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;")
-               else
-                       v.add_instr("{closcnv}.variable = variable;")
-                       v.add_instr("{closcnv}.closurevariable = closurevariable;")
-               end
-
-               return "(&{closcnv})"
-       end
-
-       protected fun decl_csignature(v: CompilerVisitor, args: Array[String], closcn: String): String
-       do
-               var params = new Array[String]
-               params.add("struct WBT_ *closctx")
-               for i in [0..closure.signature.arity[ do
-                       var p = "val_t {args[i]}"
-                       params.add(p)
-               end
-               var ret: String
-               if closure.signature.return_type != null then
-                       ret = "val_t"
-               else
-                       ret = "void"
-               end
-               var p = params.join(", ")
-               var s = "{ret} {cname}({p})"
-               v.add_decl("typedef {ret} (* {cname}_t)({p});")
-               v.add_decl(s + ";")
-               return s
-       end
-
-       redef fun do_compile_inside(v, params)
-       do
-               for i in [0..variables.length[ do
-                       var vacname = v.cfc.register_variable(variables[i])
-                       v.add_assignment(vacname, params[i])
-               end
-
-               var old_cv = v.nmc.continue_value
-               var old_cl = v.nmc.continue_label
-               var old_bl = v.nmc.break_label
-
-               v.nmc.continue_value = v.cfc.get_var("Continue value and escape marker")
-               v.nmc.continue_label = "continue_label{v.new_number}"
-               v.nmc.break_label = v.nmc.return_label
-
-               v.compile_stmt(n_expr)
-
-               v.add_instr("{v.nmc.continue_label}: while(false);")
-
-               var ret: nullable String = null
-               if closure.signature.return_type != null then ret = v.nmc.continue_value
-
-               v.nmc.continue_value = old_cv
-               v.nmc.continue_label = old_cl
-               v.nmc.break_label = old_bl
-
-               return ret
-       end
-end
-
-redef class PClosureDecl
-       fun do_compile_inside(v: CompilerVisitor, params: Array[String]): nullable String is abstract
-end
-redef class AClosureDecl
-       redef fun do_compile_inside(v, params)
-       do
-               n_signature.compile_parameters(v, variable.closure.signature, params)
-
-               var old_cv = v.nmc.continue_value
-               var old_cl = v.nmc.continue_label
-               var old_bl = v.nmc.break_label
-
-               v.nmc.continue_value = v.cfc.get_var("Continue value and escape marker")
-               v.nmc.continue_label = "continue_label{v.new_number}"
-               v.nmc.break_label = v.nmc.return_label
-
-               v.compile_stmt(n_expr)
-
-               v.add_instr("{v.nmc.continue_label}: while(false);")
-
-               var ret: nullable String = null
-               if variable.closure.signature.return_type != null then ret = v.nmc.continue_value
-
-               v.nmc.continue_value = old_cv
-               v.nmc.continue_label = old_cl
-               v.nmc.break_label = old_bl
-
-               return ret
-       end
-end
-
-redef class AClosureCallExpr
-       fun intern_compile_call(v: CompilerVisitor): nullable String
-       do
-               var cargs = new Array[String]
-               compile_arguments_in(v, cargs)
-               var va: nullable String = null
-               if variable.closure.signature.return_type != null then va = v.cfc.get_var("Closure call result value")
-
-               if variable.closure.is_optional then
-                       v.add_instr("if({v.cfc.varname(variable)}==NULL) \{")
-                       v.indent
-                       var n = variable.decl
-                       assert n isa AClosureDecl
-                       var s = n.do_compile_inside(v, cargs)
-                       if s != null then v.add_assignment(va.as(not null), s)
-                       v.unindent
-                       v.add_instr("} else \{")
-                       v.indent
-               end
-
-               var ivar = v.cfc.varname(variable)
-               var cargs2 = [ivar]
-               cargs2.append(cargs)
-               var s = "(({variable.ctypename})({ivar}->fun))({cargs2.join(", ")}) /* Invoke closure {variable} */"
-               if va != null then
-                       v.add_assignment(va, s)
-               else
-                       v.add_instr("{s};")
-               end
-               v.add_instr("if ({ivar}->has_broke) \{")
-               v.indent
-               if n_closure_defs.length == 1 then do
-                       n_closure_defs.first.do_compile_inside(v, null)
-               end
-               if v.cfc.closure == v.nmc then v.add_instr("if ({ivar}->has_broke) \{ closctx->has_broke = {ivar}->has_broke; closctx->broke_value = {ivar}->broke_value;\}")
-               v.add_instr("goto {v.nmc.return_label};")
-               v.unindent
-               v.add_instr("\}")
-
-               if variable.closure.is_optional then
-                       v.unindent
-                       v.add_instr("\}")
-               end
-               return va
-       end
-
-       redef fun compile_expr(v)
-       do
-               var e = intern_compile_call(v)
-               assert e != null
-               return e
-       end
-
-       redef fun compile_stmt(v)
-       do
-               var e = intern_compile_call(v)
-               if e != null then
-                       v.add_instr(e + ";")
-               end
-       end
-end
-
-redef class AProxyExpr
-       redef fun compile_expr(v)
-       do
-               return v.compile_expr(n_expr)
-       end
-end
-
-redef class AOnceExpr
-       redef fun compile_expr(v)
-       do
-               var i = v.new_number
-               var cvar = v.cfc.get_var("Once expression result")
-               v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value for {cvar}*/")
-               v.add_instr("if (once_bool_{i}) {cvar} = once_value_{i};")
-               v.add_instr("else \{")
-               v.indent
-               v.cfc.free_var(cvar)
-               var e = v.compile_expr(n_expr)
-               v.add_assignment(cvar, e)
-               v.add_instr("once_value_{i} = {cvar};")
-               v.add_instr("once_bool_{i} = true;")
-               v.unindent
-               v.add_instr("}")
-               return cvar
-       end
-end
diff --git a/src/icode/icode.nit b/src/icode/icode.nit
new file mode 100644 (file)
index 0000000..ec6ddbf
--- /dev/null
@@ -0,0 +1,20 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Nit intermediate code representation
+import icode_base
+import icode_tools
+import icode_builder
diff --git a/src/icode/icode_base.nit b/src/icode/icode_base.nit
new file mode 100644 (file)
index 0000000..f46ff5b
--- /dev/null
@@ -0,0 +1,399 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Base classes for Nit intermediate code representation
+package icode_base
+
+import metamodel
+import mmloader
+
+## UTILITY CLASSES ##
+
+# Register store local variable and intermediate results
+class IRegister
+       # The static type
+       readable var _stype: MMType
+       init(s: MMType)
+       do
+               _stype = s
+       end
+end
+
+# A Closure declaration
+class IClosureDecl
+       # The associated closure definition
+       readable var _closure: MMClosure
+
+       # The default implementation
+       readable writable var _default: nullable IRoutine
+
+       init(c: MMClosure) do _closure = c
+end
+
+# A routine is a sequence of icodes with entry iregisters (params) and an exit iregister (result)
+class IRoutine
+       # The parameters of the routine
+       readable var _params: IndexedCollection[IRegister]
+
+       # The closure declared
+       readable writable var _closure_decls: nullable IndexedCollection[IClosureDecl] = null
+
+       # The result of the routine
+       readable var _result: nullable IRegister
+
+       # The sequence of icode
+       readable var _body: ISeq = new ISeq
+
+       # The location of the iroutine (if any)
+       readable writable var _location: nullable Location = null
+
+       init(p: IndexedCollection[IRegister], r: nullable IRegister)
+       do
+               _params = p.to_a
+               _result = r
+       end
+end
+
+# A closure definition in a iroutine body
+class IClosureDef
+special IRoutine
+       init(p: Array[IRegister], r: nullable IRegister)
+       do
+               super(p, r)
+       end
+end
+
+## INTERMEDIATE CODE ##
+
+# The root of the intermediate code representation
+abstract class ICode
+       # The number of registers used by the icode
+       fun arity: Int is abstract
+
+       # The result of the icode (if any)
+       readable writable var _result: nullable IRegister = null
+
+       # The location of the icode (if any)
+       readable writable var _location: nullable Location = null
+end
+
+# An icode that uses no registers (no args)
+abstract class ICode0
+special ICode
+       redef fun arity do return 0
+end
+
+# An icode that uses a single register (1 arg)
+abstract class ICode1
+special ICode
+       redef fun arity do return 1
+
+       # The single argument
+       readable var _expr: IRegister
+
+       init(e: IRegister) do _expr = e
+end
+
+# An icode that uses two single registers (2 args)
+abstract class ICode2
+special ICode
+       redef fun arity do return 2
+
+       # The first argument
+       readable var _expr1: IRegister
+
+       # The second argument
+       readable var _expr2: IRegister
+
+       init(e1, e2: IRegister)
+       do
+               _expr1 = e1
+               _expr2 = e2
+       end
+end
+
+# An icode that uses a variable number of registers (n args) and a variable number of closure definitions
+abstract class ICodeN
+special ICode
+       redef fun arity do return _exprs.length
+
+       # All arguments
+       readable var _exprs: IndexedCollection[IRegister]
+
+       # All closure definition
+       readable writable var _closure_defs: nullable IndexedCollection[nullable IClosureDef]
+
+       init(e: nullable IndexedCollection[IRegister])
+       do
+               if e == null then
+                       _exprs = new Array[IRegister]
+               else
+                       _exprs = e
+               end
+       end
+end
+
+#################################################
+
+# A linear sequence of ICode
+class ISeq
+special ICode0
+       # The sequence of icode
+       readable var _icodes: List[ICode] = new List[ICode]
+       init do end
+end
+
+# An infinite loop of ICode
+# Use IEscape to exit
+class ILoop
+special ISeq
+       init do end
+end
+
+# A Condidianal if-then-else statement
+# expr is the condition
+class IIf
+special ICode1
+       # The 'then' sequence of icode
+       readable var _then_seq: ISeq = new ISeq
+       # The 'else' sequence of icode
+       readable var _else_seq: ISeq = new ISeq
+       init(e: IRegister) do super
+end
+
+# Escape to to end of a parent sequence
+class IEscape
+special ICode0
+       # The seqeuence to escape
+       # The control flow continues at the next icode after the sequence
+       readable var _seq: ISeq
+       init(seq: ISeq) do _seq = seq
+end
+
+# An abort statement
+class IAbort
+special ICode0
+       # The reason the abort occured
+       # tests.first is the format
+       readable var _texts: Array[String]
+       # The local property that has the abort (if any)
+       readable var _property_location: nullable MMLocalProperty
+       # The module that has the abort
+       readable var _module_location: MMModule
+       init(t: Array[String], pl: nullable MMLocalProperty, ml: MMModule)
+       do
+               _texts = t
+               _property_location = pl
+               _module_location = ml
+       end
+end
+
+#################################################
+
+# The root of all method invocations
+abstract class IAbsCall
+special ICodeN
+       # The called method
+       readable var _property: MMMethod
+
+       init(p: MMMethod, e: IndexedCollection[IRegister])
+       do
+               super(e)
+               _property = p
+       end
+end
+
+# A simple procedure or function call
+# exprs.first is the reciever, other are arguments
+class ICall
+special IAbsCall
+       init(p, e) do super
+end
+
+# A super method call
+# exprs.first is the reciever, other are arguments
+class ISuper
+special IAbsCall
+       init(p, e) do super
+end
+
+# An instantiation
+# no reciever, all exprs are arguments
+class INew
+special ICall
+       # The type to instantiate
+       readable var _stype: MMType
+       init(t: MMType, p: MMMethod, a: IndexedCollection[IRegister])
+       do
+               super(p, a)
+               _stype = t
+       end
+end
+
+# A closure call
+# exprs are the arguments
+class IClosCall
+special ICodeN
+       # The called closure
+       readable var _closure_decl: IClosureDecl
+
+       # The !break sequence (if any)
+       readable writable var _break_seq: nullable ISeq = null
+
+       init(c: IClosureDecl, e: IndexedCollection[IRegister])
+       do
+               super(e)
+               _closure_decl = c
+       end
+end
+
+# A native C code
+# Mainly used to implements things that do not have a specific ICode yet
+# expr are the arguments
+class INative
+special ICodeN
+       # The native C code
+       # Special character sequence '@@@' will be substitued in order with the arguments
+       readable var _code: String
+
+       init(c: String, e: nullable IndexedCollection[IRegister])
+       do
+               super(e)
+               _code = c
+       end
+end
+
+# A register assigment
+# expr is the assigned value
+# result is the register assigned
+class IMove
+special ICode1
+       init(r: IRegister, e: IRegister)
+       do
+               super(e)
+               _result = r
+       end
+end
+
+# An attribute read access
+# expr is the reciever
+class IAttrRead
+special ICode1
+       # The accessed attribute
+       readable var _property: MMAttribute
+
+       init(p: MMAttribute, r: IRegister)
+       do
+               super(r)
+               _property = p
+       end
+end
+
+# An attribute assignment
+# expr1 is the receiver, expr2 is the assigned value
+class IAttrWrite
+special ICode2
+       # The accessed attribute
+       readable var _property: MMAttribute
+
+       init(p: MMAttribute, r: IRegister, v: IRegister)
+       do
+               super(r, v)
+               _property = p
+       end
+end
+
+
+# An attribute is_set check
+# expr is the reciever
+class IAttrIsset
+special ICode1
+       # The accessed attribute
+       readable var _property: MMAttribute
+
+       init(p: MMAttribute, r: IRegister)
+       do
+               super(r)
+               _property = p
+       end
+end
+
+# A type check
+# expr is the expression checked
+class ITypeCheck
+special ICode1
+       # The static type checkes to
+       readable var _stype: MMType
+
+       init(e: IRegister, t: MMType)
+       do
+               super(e)
+               _stype = t
+       end
+end
+
+# The 'is' operator
+# expr1 and expr2 are both operands
+class IIs
+special ICode2
+       init(e1, e2: IRegister)
+       do
+               super
+       end
+end
+
+# The unary 'not' operation
+# expr is the operand
+class INot
+special ICode1
+       init(e: IRegister)
+       do
+               super
+       end
+end
+
+# Evaluate body once them return the same value again and again
+# if result is not null, then it must also be assigned in the body
+class IOnce
+special ICode0
+       readable var _body: ISeq = new ISeq
+       init do end
+end
+
+# Is a closure given as a parameter?
+class IHasClos
+special ICode0
+       # The called closure
+       readable var _closure_decl: IClosureDecl
+
+       init(c: IClosureDecl)
+       do
+               _closure_decl = c
+       end
+end
+
+#################################################
+
+redef class MMAttribute
+       # The attached initialisation iroutine if any
+       # To call between the allocate-instance and the initialize-instance
+       fun iroutine: nullable IRoutine is abstract
+end
+
+redef class MMMethod
+       # The attached body iroutine if any
+       fun iroutine: nullable IRoutine is abstract
+end
diff --git a/src/icode/icode_builder.nit b/src/icode/icode_builder.nit
new file mode 100644 (file)
index 0000000..01cdde9
--- /dev/null
@@ -0,0 +1,242 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Help generation of icode
+package icode_builder
+
+import icode_base
+
+# Helps to generate icodes in a iroutine
+class ICodeBuilder
+       # Add a new statment in the current icode sequence
+       # Can be used with expression if the result is ignored of if the result is already set
+       fun stmt(s: ICode)
+       do
+               s.location = _current_location
+               _seq.icodes.add(s)
+       end
+
+       # Add a new expression in the current icode sequence and return a new associated result register
+       fun expr(e: ICode, s: MMType): IRegister
+       do
+               stmt(e)
+               assert e.result == null
+               var reg = new_register(s)
+               e.result = reg
+               return reg
+       end
+
+       # Add an assignement (IMove) in the current icode sequence
+       fun add_assignment(reg: IRegister, v: IRegister)
+       do
+               stmt(new IMove(reg, v))
+       end
+
+       # Check that the reciever e is not null (IIs + IAbort)
+       fun add_null_reciever_check(e: IRegister)
+       do
+               var nul = new_register(module.type_none)
+               var c = expr(new IIs(e, nul), module.type_bool)
+               var iif = new IIf(c)
+               stmt(iif)
+               var old_seq = seq
+               seq = iif.then_seq
+               add_abort("Reciever is null")
+               seq = old_seq
+       end
+
+       # Add a type cast (ITypeCheck + IAbort) in the current icode sequence
+       fun add_type_cast(e: IRegister, stype: MMType)
+       do
+               var c = expr(new ITypeCheck(e, stype), module.type_bool)
+               var iif = new IIf(c)
+               stmt(iif)
+               var old_seq = seq
+               seq = iif.else_seq
+               add_abort("Cast failed")
+               seq = old_seq
+       end
+
+       # Add an attr check (IAttrIsset + IAbort) in the current icode sequence
+       fun add_attr_check(prop: MMAttribute, e: IRegister)
+       do
+               if not prop.signature.return_type.is_nullable then
+                       var cond = expr(new IAttrIsset(prop, e), module.type_bool)
+                       var iif = new IIf(cond)
+                       stmt(iif)
+                       var seq_old = seq
+                       seq = iif.else_seq
+                       add_abort("Uninitialized attribute %s", prop.name.to_s)
+                       seq = seq_old
+               end
+       end
+
+       # Add an IAttrRead guarded by an add_attr_check in the current icode sequence
+       fun add_attr_read(prop: MMAttribute, e: IRegister): IRegister
+       do
+               add_attr_check(prop, e)
+               return expr(new IAttrRead(prop, e), prop.signature.return_type.as(not null))
+       end
+
+       # Add a localized IAbort
+       fun add_abort(s: String...)
+       do
+               stmt(new IAbort(s, method, module))
+       end
+
+       # Add an assigment to the iroutine return value
+       # Beware, no jump is generated
+       fun add_return_value(reg: IRegister)
+       do
+               add_assignment(iroutine.result.as(not null), reg)
+       end
+
+       # Add an ICall with possible simple inlining in the current icode sequence
+       fun add_call(prop: MMMethod, args: Array[IRegister], closcns: nullable Array[nullable IClosureDef]): nullable IRegister
+       do
+               var ee = once "==".to_symbol
+
+               # Inline "x!=y" as "not x==y"
+               var ne = once "!=".to_symbol
+               if prop.name == ne then
+                       var eqp = prop.signature.recv.local_class.select_method(ee)
+                       var eqcall = add_call(eqp, args, closcns).as(not null)
+                       return expr(new INot(eqcall), module.type_bool)
+               end
+
+               # TODO: Inline x==y as "x is y or (x != null and (== is not the Object one) and x.==(y))"
+               # inline "x==y" as "x is y or x != null and x.==(y)"
+               var icall = new ICall(prop, args)
+               icall.closure_defs = closcns
+               if prop.name == ee then
+                       # Prepare the result
+                       var reg = new_register(module.type_bool)
+                       # "x is y"
+                       var cond = expr(new IIs(args[0], args[1]), module.type_bool)
+                       var iif = new IIf(cond)
+                       stmt(iif)
+                       var seq_old = seq
+                       seq = iif.then_seq
+                       add_assignment(reg, cond)
+                       # "or"
+                       seq = iif.else_seq
+                       # Do the "x != null" part iff x is nullable
+                       if args[0].stype.is_nullable then
+                               var nul = new_register(module.type_none)
+                               cond = expr(new IIs(args[0], nul), module.type_bool)
+                               iif = new IIf(cond)
+                               stmt(iif)
+                               seq = iif.then_seq
+                               add_assignment(reg, expr(new INative("TAG_Bool(false)", null), module.type_bool))
+                               seq = iif.else_seq
+                       end
+                       # "x.==(y)"
+                       add_assignment(reg, expr(icall, module.type_bool))
+                       seq = seq_old
+                       return reg
+               end
+
+               var rtype = prop.signature.return_type
+               if rtype != null then
+                       return expr(icall, rtype)
+               else
+                       stmt(icall)
+                       return null
+               end
+       end
+
+       # Get a new register
+       fun new_register(s: MMType): IRegister
+       do
+               return new IRegister(s)
+       end
+
+       # The module where classes and types are extracted
+       readable var _module: MMModule
+
+       # The current iroutine build
+       readable var _iroutine: IRoutine
+
+       # The current sequence of icodes
+       readable writable var _seq: ISeq
+
+       # The method associated to the iroutine (if any)
+       readable var _method: nullable MMMethod
+
+       init(module: MMModule, r: IRoutine, m: nullable MMMethod)
+       do
+               _module = module
+               _current_location = r.location
+               _iroutine = r
+               _seq = r.body
+               _method = m
+       end
+
+       # New ICodes are set to this location
+       readable writable var _current_location: nullable Location = null
+end
+
+redef class MMSignature
+       # Create an empty IRoutine that match the signature
+       fun generate_empty_iroutine: IRoutine
+       do
+               var args = new Array[IRegister]
+               args.add(new IRegister(recv)) # Self
+               for i in [0..arity[ do
+                       args.add(new IRegister(self[i]))
+               end
+               var res: nullable IRegister = null
+               var rtype = return_type
+               if rtype != null then
+                       res = new IRegister(rtype)
+               end
+               var iroutine = new IRoutine(args, res)
+               var clos: nullable Array[IClosureDecl] = null
+               if not closures.is_empty then
+                       clos = new Array[IClosureDecl]
+                       for c in closures do
+                               clos.add(new IClosureDecl(c))
+                       end
+                       iroutine.closure_decls = clos
+               end
+               return iroutine
+       end
+
+       # Create an empty IClosureDef that match the signature
+       fun generate_empty_iclosuredef: IClosureDef
+       do
+               var args = new Array[IRegister]
+               for i in [0..arity[ do
+                       args.add(new IRegister(self[i]))
+               end
+               var res: nullable IRegister = null
+               var rtype = return_type
+               if rtype != null then
+                       res = new IRegister(rtype)
+               end
+               var iroutine = new IClosureDef(args, res)
+               var clos: nullable Array[IClosureDecl] = null
+               if not closures.is_empty then
+                       clos = new Array[IClosureDecl]
+                       for c in closures do
+                               clos.add(new IClosureDecl(c))
+                       end
+                       iroutine.closure_decls = clos
+               end
+               return iroutine
+       end
+end
+
diff --git a/src/icode/icode_tools.nit b/src/icode/icode_tools.nit
new file mode 100644 (file)
index 0000000..efdc0b7
--- /dev/null
@@ -0,0 +1,328 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Tools to manipulate intermediace nit code representation
+import icode_base
+
+# A simple visitor to visit icode structures
+class ICodeVisitor
+       # Called when a iregister is read in a icode
+       fun visit_iregister_read(ic: ICode, r: IRegister) do end
+
+       # Called when a iregister is wrote in a icode
+       fun visit_iregister_write(ic: ICode, r: IRegister) do end
+
+       # The current icode iterator.
+       # Can be used to insert_before, used to change the item or deleted
+       readable var _current_icode: nullable ListIterator[ICode] = null
+
+       # Called when a icode is visited
+       # Automatically visits iregisters and sub-icodes
+       fun visit_icode(ic: nullable ICode)
+       do
+               if ic == null then return
+               if ic isa ISeq then
+                       var old_icode = _current_icode
+                       var cur = ic.icodes.iterator
+                       while cur.is_ok do
+                               _current_icode = cur
+                               var ic2 = cur.item
+                               visit_icode(ic2)
+                               cur.next
+                       end
+                       _current_icode = old_icode
+               else if ic isa IIf then
+                       visit_iregister_read(ic, ic.expr)
+                       visit_icode(ic.then_seq)
+                       visit_icode(ic.else_seq)
+               else if ic isa IOnce then
+                       visit_icode(ic.body)
+               else if ic isa ICode1 then
+                       visit_iregister_read(ic, ic.expr)
+               else if ic isa ICode2 then
+                       visit_iregister_read(ic, ic.expr1)
+                       visit_iregister_read(ic, ic.expr2)
+               else if ic isa ICodeN then
+                       for e in ic.exprs do
+                               visit_iregister_read(ic, e)
+                       end
+                       var closdefs = ic.closure_defs
+                       if ic isa IClosCall then
+                               visit_icode(ic.break_seq)
+                       end
+                       if closdefs != null then
+                               visit_closure_defs(closdefs)
+                       end
+               end
+               var r = ic.result
+               if r != null then visit_iregister_write(ic, r)
+       end
+
+       # Called when closure definitions are visited
+       # Automatically visits each closure definition
+       fun visit_closure_defs(closdefs: Collection[nullable IClosureDef])
+       do
+               for e in closdefs do
+                       if e != null then
+                               visit_iroutine(e)
+                       end
+               end
+       end
+
+       # Called when an iroutine is visited
+       # Automatically visits the body
+       # Warning: parameters of result registers are not visited
+       fun visit_iroutine(ir: IRoutine)
+       do
+               visit_icode(ir.body)
+       end
+end
+
+redef class IRoutine
+       # Inline an iroutine in an icode sequence
+       fun inline_in_seq(seq: ISeq, args: IndexedCollection[IRegister]): nullable IRegister
+       do
+               var d = new ICodeDupContext
+               if args.length != params.length then print "{args.length} != {params.length}"
+               assert args.length == params.length
+               for i in [0..args.length[ do
+                       # FIXME The following assumes that params are readonly.
+                       # The alternative is safe but add one move :/
+                       d._registers[params[i]] = args[i]
+                       #seq.icodes.add(new IMove(d.dup_ireg(params[i]), args[i]))
+               end
+               seq.icodes.add(body.dup_with(d))
+               var r = result
+               if r != null then r = d.dup_ireg(r)
+               return r
+       end
+end
+
+# This class stores reference to allow correct duplication of icodes
+private class ICodeDupContext
+       # Duplicate one register
+       # Subsequent invocation will return the same register
+       fun dup_ireg(r: IRegister): IRegister
+       do
+               var rs = _registers
+               if rs.has_key(r) then
+                       return rs[r]
+               else
+                       var r2 = new IRegister(r.stype)
+                       rs[r] = r2
+                       return r2
+               end
+       end
+
+       # Duplicate a bunch of registers
+       # Subsequent invocation will return the same registers
+       fun dup_iregs(regs: IndexedCollection[IRegister]): IndexedCollection[IRegister]
+       do
+               var a = new Array[IRegister].with_capacity(regs.length)
+               for r in regs do
+                       a.add(dup_ireg(r))
+               end
+               return a
+       end
+
+       # The associoation between old_seq and new_seq
+       # Directly used by the IEscape
+       var _seqs: Map[ISeq, ISeq] = new HashMap[ISeq, ISeq]
+
+       # The assocation between old_ireg and new_ireg
+       # Used by dup_ireg
+       var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
+end
+
+redef class ICode
+       # Duplicate the current icode (generic part)
+       private fun dup_with(d: ICodeDupContext): ICode
+       do
+               var c = inner_dup_with(d)
+               var r = result
+               if r != null then c.result = d.dup_ireg(r)
+               c.location = location
+               return c
+       end
+
+       # Duplicate the current icode (specific part)
+       private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
+end
+
+redef class ISeq
+       redef fun inner_dup_with(d)
+       do
+               var c2 = new ISeq
+               dup_seq_to(d, c2)
+               return c2
+       end
+
+       # Duplicate each icode and store them in dest
+       # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
+       private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
+       do
+               d._seqs[self] = dest
+               for c in icodes do
+                       dest.icodes.add(c.dup_with(d))
+               end
+       end
+end
+
+redef class ILoop
+       redef fun inner_dup_with(d)
+       do
+               var c2 = new ILoop
+               dup_seq_to(d, c2)
+               return c2
+       end
+end
+
+redef class IIf
+       redef fun inner_dup_with(d)
+       do
+               var c2 = new IIf(d.dup_ireg(expr))
+               then_seq.dup_seq_to(d, c2.then_seq)
+               else_seq.dup_seq_to(d, c2.else_seq)
+               return c2
+       end
+end
+
+redef class IEscape
+       redef fun inner_dup_with(d)
+       do
+               if d._seqs.has_key(seq) then
+                       # Jump to a duplicated sequence
+                       return new IEscape(d._seqs[seq])
+               else
+                       # Jump to an englobing unduplicated sequence
+                       return new IEscape(seq)
+               end
+       end
+end
+
+redef class IAbort
+       redef fun inner_dup_with(d)
+       do
+               return new IAbort(texts, property_location, module_location)
+       end
+end
+
+redef class ICall
+       redef fun inner_dup_with(d)
+       do
+               return new ICall(property, d.dup_iregs(exprs))
+       end
+end
+
+redef class ISuper
+       redef fun inner_dup_with(d)
+       do
+               return new ISuper(property, d.dup_iregs(exprs))
+       end
+end
+
+redef class INew
+       redef fun inner_dup_with(d)
+       do
+               return new INew(stype, property, d.dup_iregs(exprs))
+       end
+end
+
+redef class IClosCall
+       redef fun inner_dup_with(d)
+       do
+               var c2 = new IClosCall(closure_decl, d.dup_iregs(exprs))
+               var bs = break_seq
+               if bs != null then
+                       var bs2 = new ISeq
+                       c2.break_seq = bs2
+                       bs.dup_seq_to(d, bs2)
+               end
+               return c2
+       end
+end
+
+redef class INative
+       redef fun inner_dup_with(d)
+       do
+               return new INative(code, d.dup_iregs(exprs))
+       end
+end
+
+redef class IMove
+       redef fun inner_dup_with(d)
+       do
+               return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr))
+       end
+end
+
+redef class IAttrRead
+       redef fun inner_dup_with(d)
+       do
+               return new IAttrRead(property, d.dup_ireg(expr))
+       end
+end
+
+redef class IAttrWrite
+       redef fun inner_dup_with(d)
+       do
+               return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2))
+       end
+end
+
+redef class IAttrIsset
+       redef fun inner_dup_with(d)
+       do
+               return new IAttrIsset(property, d.dup_ireg(expr))
+       end
+end
+
+redef class ITypeCheck
+       redef fun inner_dup_with(d)
+       do
+               return new ITypeCheck(d.dup_ireg(expr), stype)
+       end
+end
+
+redef class IIs
+       redef fun inner_dup_with(d)
+       do
+               return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2))
+       end
+end
+
+redef class INot
+       redef fun inner_dup_with(d)
+       do
+               return new INot(d.dup_ireg(expr))
+       end
+end
+
+redef class IOnce
+       redef fun inner_dup_with(d)
+       do
+               var c2 = new IOnce
+               body.dup_seq_to(d, c2.body)
+               return c2
+       end
+end
+
+redef class IHasClos
+       redef fun inner_dup_with(d)
+       do
+               return new IHasClos(closure_decl)
+       end
+end
index fa9a4d8..541e3e5 100644 (file)
@@ -484,4 +484,10 @@ end
 redef class MMModule
        # The type of null
        readable var _type_none: MMTypeNone = new MMTypeNone(self)
+
+       # The type of bool
+       fun type_bool: MMType
+       do
+               return class_by_name(once ("Bool".to_symbol)).get_type
+       end
 end
index 6e7657f..eef3c56 100644 (file)
@@ -18,7 +18,9 @@
 package nitc
 
 import abstracttool
+import analysis
 private import compiling
+private import syntax
 
 # The main class of the nitcompiler program
 class NitCompiler
@@ -31,11 +33,12 @@ special AbstractCompiler
        readable var _opt_bindir: OptionString = new OptionString("NIT tools directory", "--bindir")
        readable var _opt_compdir: OptionString = new OptionString("Intermediate compilation directory", "--compdir")
        readable var _opt_extension_prefix: OptionString = new OptionString("Append prefix to file extension", "-p", "--extension-prefix")
+       readable var _opt_dump: OptionBool = new OptionBool("Dump intermediate code", "--dump")
 
        init
        do
                super("nitc")
-               option_context.add_option(opt_output, opt_boost, opt_no_cc, opt_global, opt_clibdir, opt_bindir, opt_compdir, opt_extension_prefix)
+               option_context.add_option(opt_output, opt_boost, opt_no_cc, opt_global, opt_clibdir, opt_bindir, opt_compdir, opt_extension_prefix, opt_dump)
        end
 
        redef fun process_options
@@ -90,8 +93,36 @@ special AbstractCompiler
                end
        end
 
+       fun dump_intermediate_code(mods: Collection[MMModule])
+       do
+               for mod in mods do
+                       for c in mod.local_classes do
+                               if not c isa MMConcreteClass then continue
+                               for p in c.local_local_properties do
+                                       var routine: nullable IRoutine = null
+                                       if p isa MMAttribute then
+                                               routine = p.iroutine
+                                       else if p isa MMMethod then
+                                               routine = p.iroutine
+                                       end
+                                       if routine == null then continue
+                                       print "**** Property {p.full_name} ****"
+                                       var icd = new ICodeDumper
+                                       routine.dump(icd)
+                                       print "**** OPTIMIZE {p.full_name} ****"
+                                       routine.optimize
+                                       icd = new ICodeDumper
+                                       routine.dump(icd)
+                               end
+                       end
+               end
+       end
+
        redef fun perform_work(mods)
        do
+               if opt_dump.value then
+                       dump_intermediate_code(mods)
+               end
                for mod in mods do
                        mod.compile_prog_to_c(self)
                end
diff --git a/src/syntax/icode_generation.nit b/src/syntax/icode_generation.nit
new file mode 100644 (file)
index 0000000..719eba8
--- /dev/null
@@ -0,0 +1,1403 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2009 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.
+
+# Things needed by typing.nit to generate intermediate code from AST
+package icode_generation
+
+import icode
+import syntax_base
+private import typing
+private import primitive_info
+
+# An AST2ICode context stores the currently built icode informations
+class A2IContext
+special ICodeBuilder
+       redef fun stmt(s: ICode)
+       do
+               if _current_node != null then
+                       current_location = _current_node.location
+               else if visitor.current_node != null then
+                       current_location = visitor.current_node.location
+               end
+               super
+       end
+
+       # Prepare a new array of length item
+       fun add_new_array(stype: MMType, length: Int): IRegister
+       do
+               var prop = visitor.get_method(stype, once "with_capacity".to_symbol)
+               var ni = expr(new INative("TAG_Int({length})", null), visitor.type_int)
+               return expr(new INew(stype, prop, [ni]), stype)
+       end
+
+       # Add an array add
+       fun add_call_array_add(recv, item: IRegister)
+       do
+               var stype = recv.stype
+               var prop = visitor.get_method(stype, once "add".to_symbol)
+               stmt(new ICall(prop, [recv, item]))
+       end
+
+       # Get the iregister associated with a variable
+       # Or assign one if none exists
+       fun variable(v: Variable): IRegister
+       do
+               if _variables.has_key(v) then
+                       return _variables[v]
+               else
+                       var reg = new_register(v.stype.as(not null))
+                       _variables[v] = reg
+                       return reg
+               end
+       end
+
+       # Current registered variable
+       var _variables: HashMap[Variable, IRegister] = new HashMap[Variable, IRegister]
+
+       # Current registered closurevariables
+       readable var _closurevariables: HashMap[ClosureVariable, IClosureDecl] = new HashMap[ClosureVariable, IClosureDecl]
+
+       # The current syntax visitor
+       readable var _visitor: AbsSyntaxVisitor
+
+       # Where a nit return must branch
+       readable writable var _return_seq: nullable ISeq
+
+       # Register where a functionnal nit return must store its value
+       readable writable var _return_value: nullable IRegister
+
+       init(visitor: AbsSyntaxVisitor, r: IRoutine, m: nullable MMMethod)
+       do
+               super(visitor.module, r, m)
+               _visitor = visitor
+               _return_seq = r.body
+               _return_value = r.result
+       end
+
+       # Insert implicit super init calls
+       fun invoke_super_init_calls_after(start_prop: nullable MMMethod)
+       do
+               var p = method
+               assert p isa MMSrcMethod
+               var n = p.node
+               assert n isa AConcreteInitPropdef
+
+               if n.super_init_calls.is_empty then return
+               var i = 0
+               var j = 0
+               if start_prop != null then
+                       while n.super_init_calls[i] != start_prop do
+                               i += 1
+                       end
+                       i += 1
+
+                       while n.explicit_super_init_calls[j] != start_prop do
+                               j += 1
+                       end
+                       j += 1
+               end
+               var stop_prop: nullable MMMethod = null
+               if j < n.explicit_super_init_calls.length then
+                       stop_prop = n.explicit_super_init_calls[j]
+               end
+               var l = n.super_init_calls.length
+               while i < l do
+                       var p = n.super_init_calls[i]
+                       if p == stop_prop then break
+                       var cargs = new Array[IRegister]
+                       if p.signature.arity == 0 then
+                               cargs.add(iroutine.params.first)
+                       else
+                               for va in iroutine.params do
+                                       cargs.add(va)
+                               end
+                       end
+                       stmt(new ICall(p, cargs))
+                       i += 1
+               end
+       end
+
+       # The current  PExpr
+       var _current_node: nullable PExpr = null
+
+       # Generate icode in the current sequence from a statement
+       fun generate_stmt(n: nullable PExpr)
+       do
+               if n == null then return
+               var old = _current_node
+               _current_node = n
+               n.generate_icode(self)
+               _current_node = old
+       end
+
+       # Generate icode in the current sequence from an expression
+       fun generate_expr(n: PExpr): IRegister
+       do
+               var old = _current_node
+               _current_node = n
+               var reg = n.generate_icode(self).as(not null)
+               _current_node = old
+               return reg
+       end
+end
+
+redef class EscapableBlock
+       # Where a nit break must branch
+       readable writable var _break_seq: nullable ISeq
+
+       # Where a nit continue must branch
+       readable writable var _continue_seq: nullable ISeq
+
+       # Register where a functionnal nit break must store its value
+       readable writable var _break_value: nullable IRegister
+
+       # Register where a functionnal nit continue must store its value
+       readable writable var _continue_value: nullable IRegister
+end
+
+redef class MMSrcModule
+       # Generate icode for method bodies
+       fun generate_icode(tc: ToolContext)
+       do
+               var v = new A2IVisitor(tc, self)
+               for c in src_local_classes do
+                       for p in c.src_local_properties do
+                               if p isa MMSrcMethod then
+                                       p.generate_iroutine(v)
+                               else if p isa MMSrcAttribute then
+                                       p.generate_iroutine(v)
+                               end
+                       end
+               end
+       end
+end
+
+redef class MMSrcAttribute
+       redef readable writable var _iroutine: nullable IRoutine
+
+       # Generate the initialization iroutine
+       fun generate_iroutine(visitor: A2IVisitor)
+       do
+               if node.n_expr != null then
+                       var iroutine = signature.generate_empty_iroutine
+                       iroutine.location = node.location
+                       var v = new A2IContext(visitor, iroutine, null)
+                       visitor.icode_ctx = v
+                       visitor.enter_visit(node)
+                       visitor.icode_ctx = null
+                       _iroutine = iroutine
+               end
+       end
+end
+
+redef class MMSrcMethod
+       redef readable writable var _iroutine: nullable IRoutine
+
+       # Generate the body iroutine
+       fun generate_iroutine(visitor: A2IVisitor)
+       do
+               var iroutine = signature.generate_empty_iroutine
+               if node != null then
+                       iroutine.location = node.location
+               end
+               var v = new A2IContext(visitor, iroutine, self)
+               visitor.icode_ctx = v
+               inner_generate_iroutine(v)
+               visitor.icode_ctx = null
+               _iroutine = iroutine
+       end
+
+       # Generate the body iroutine (specific part)
+       fun inner_generate_iroutine(v: A2IContext) is abstract
+end
+
+redef class MMReadImplementationMethod
+       redef fun inner_generate_iroutine(v)
+       do
+               var e = v.add_attr_read(node.prop, v.iroutine.params.first)
+               v.add_return_value(e)
+       end
+end
+
+redef class MMWriteImplementationMethod
+       redef fun inner_generate_iroutine(v)
+       do
+               var params = v.iroutine.params
+               v.stmt(new IAttrWrite(node.prop, params[0], params[1]))
+       end
+end
+
+redef class MMMethSrcMethod
+       redef fun inner_generate_iroutine(v)
+       do
+               v.visitor.enter_visit(node)
+       end
+end
+
+redef class MMImplicitInit
+       redef fun inner_generate_iroutine(v)
+       do
+               var params = v.iroutine.params
+               var f = params.length - unassigned_attributes.length
+               var recv = params.first
+               for sp in super_inits do
+                       assert sp isa MMMethod
+                       var args_recv = [recv]
+                       if sp == super_init then
+                               var args = new Array[IRegister].with_capacity(f)
+                               args.add(recv)
+                               for i in [1..f[ do
+                                       args.add(params[i])
+                               end
+                               v.stmt(new ICall(sp, args))
+                       else
+                               v.stmt(new ICall(sp, args_recv))
+                       end
+               end
+               for i in [f..params.length[ do
+                       var attribute = unassigned_attributes[i-f]
+                       v.stmt(new IAttrWrite(attribute, recv, params[i]))
+               end
+       end
+end
+
+class A2IVisitor
+special AbsSyntaxVisitor
+       writable var _icode_ctx: nullable A2IContext
+       fun icode_ctx: A2IContext do return _icode_ctx.as(not null)
+       redef fun visit(n) do n.accept_icode_generation(self)
+       init(tc, m) do super
+end
+
+
+###############################################################################
+
+redef class PNode
+       fun accept_icode_generation(v: A2IVisitor) do accept_abs_syntax_visitor(v) end
+end
+
+redef class AAttrPropdef
+       redef fun accept_icode_generation(vv)
+       do
+               var v = vv.icode_ctx
+               v.stmt(new IMove(v.variable(self_var), v.iroutine.params.first))
+               super
+               var ne = n_expr
+               if ne != null then
+                       v.stmt(new IMove(v.iroutine.result.as(not null), v.generate_expr(ne)))
+               end
+       end
+end
+
+redef class AMethPropdef
+       redef fun accept_icode_generation(vv)
+       do
+               super
+               fill_iroutine(vv.icode_ctx, method)
+       end
+
+       # Compile the method body common preambule (before specific body stuff if any)
+       fun fill_iroutine(v: A2IContext, method: MMSrcMethod) is abstract
+end
+
+redef class PSignature
+       fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: IndexedCollection[IRegister], closdecls: nullable IndexedCollection[IClosureDecl]) is abstract
+end
+
+redef class ASignature
+       redef fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: IndexedCollection[IRegister], closdecls: nullable IndexedCollection[IClosureDecl])
+       do
+               for ap in n_params do
+                       var reg = v.variable(ap.variable)
+                       var orig_type = orig_sig[ap.position]
+                       var apst = ap.variable.stype.as(not null)
+                       if not orig_type < apst then
+                               v.add_type_cast(params[ap.position], apst)
+                       end
+                       v.stmt(new IMove(reg, params[ap.position]))
+               end
+               for i in [0..n_closure_decls.length[ do
+                       var wd = n_closure_decls[i]
+                       v.closurevariables[wd.variable] = closdecls[i]
+               end
+       end
+end
+
+redef class AClosureDecl
+       redef fun accept_icode_generation(vv)
+       do
+               var v = vv.icode_ctx
+               var iclos = variable.closure.signature.generate_empty_iclosuredef
+               var old_seq = v.seq
+               v.seq = iclos.body
+               escapable.continue_seq = iclos.body
+               escapable.continue_value = iclos.result
+               n_signature.fill_iroutine_parameters(v, variable.closure.signature, iclos.params, null)
+
+               if n_expr != null then
+                       v.generate_stmt(n_expr)
+                       v.iroutine.closure_decls[position].default = iclos
+               end
+               v.seq = old_seq
+       end
+end
+
+redef class AConcreteMethPropdef
+       redef fun fill_iroutine(v, method)
+       do
+               var params = v.iroutine.params.to_a
+               var selfreg = v.variable(self_var)
+               v.stmt(new IMove(selfreg, params[0]))
+               params.shift
+
+               var orig_meth: MMLocalProperty = method.global.intro
+               var orig_sig = orig_meth.signature_for(method.signature.recv)
+               if n_signature != null then
+                       n_signature.fill_iroutine_parameters(v, orig_sig, params, v.iroutine.closure_decls)
+               end
+
+               if self isa AConcreteInitPropdef then
+                       v.invoke_super_init_calls_after(null)
+               end
+
+               if n_block != null then
+                       v.generate_stmt(n_block)
+               end
+       end
+end
+
+redef class ADeferredMethPropdef
+       redef fun fill_iroutine(v, method)
+       do
+               v.add_abort("Deferred method called")
+       end
+end
+
+redef class AExternMethPropdef
+       redef fun fill_iroutine(v, method)
+       do
+               var params = v.iroutine.params
+               var ename = "{method.module.name}_{method.local_class.name}_{method.local_class.name}_{method.name}_{method.signature.arity}"
+               if n_extern != null then
+                       ename = n_extern.text
+                       ename = ename.substring(1, ename.length-2)
+               end
+               var sig = method.signature
+               assert params.length == sig.arity + 1
+               var args = new Array[String]
+               args.add(sig.recv.unboxtype("@@@"))
+               for i in [0..sig.arity[ do
+                       args.add(sig[i].unboxtype("@@@"))
+               end
+               var s = "{ename}({args.join(", ")})"
+               var rtype = sig.return_type
+               if rtype != null then
+                       s = rtype.boxtype(s)
+                       v.add_return_value(v.expr(new INative(s, params), rtype))
+               else
+                       v.stmt(new INative(s, params))
+               end
+       end
+end
+
+redef class AInternMethPropdef
+       redef fun fill_iroutine(v, method)
+       do
+               var p = v.iroutine.params.to_a
+               var c = method.local_class.name
+               var n = method.name
+               var s: nullable String = null
+               if c == once "Int".to_symbol then
+                       if n == once "object_id".to_symbol then
+                               s = "@@@"
+                       else if n == once "unary -".to_symbol then
+                               s = "TAG_Int(-UNTAG_Int(@@@))"
+                       else if n == once "output".to_symbol then
+                               s = "printf(\"%ld\\n\", UNTAG_Int(@@@));"
+                       else if n == once "ascii".to_symbol then
+                               s = "TAG_Char(UNTAG_Int(@@@))"
+                       else if n == once "succ".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)+1)"
+                       else if n == once "prec".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)-1)"
+                       else if n == once "to_f".to_symbol then
+                               s = "BOX_Float((float)UNTAG_Int(@@@))"
+                       else if n == once "+".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)+UNTAG_Int(@@@))"
+                       else if n == once "-".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)-UNTAG_Int(@@@))"
+                       else if n == once "*".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)*UNTAG_Int(@@@))"
+                       else if n == once "/".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)/UNTAG_Int(@@@))"
+                       else if n == once "%".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)%UNTAG_Int(@@@))"
+                       else if n == once "<".to_symbol then
+                               s = "TAG_Bool(UNTAG_Int(@@@)<UNTAG_Int(@@@))"
+                       else if n == once ">".to_symbol then
+                               s = "TAG_Bool(UNTAG_Int(@@@)>UNTAG_Int(@@@))"
+                       else if n == once "<=".to_symbol then
+                               s = "TAG_Bool(UNTAG_Int(@@@)<=UNTAG_Int(@@@))"
+                       else if n == once ">=".to_symbol then
+                               s = "TAG_Bool(UNTAG_Int(@@@)>=UNTAG_Int(@@@))"
+                       else if n == once "lshift".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)<<UNTAG_Int(@@@))"
+                       else if n == once "rshift".to_symbol then
+                               s = "TAG_Int(UNTAG_Int(@@@)>>UNTAG_Int(@@@))"
+                       else if n == once "==".to_symbol then
+                               s = "TAG_Bool((@@@)==(@@@))"
+                       else if n == once "!=".to_symbol then
+                               s = "TAG_Bool((@@@)!=(@@@))"
+                       end
+               else if c == once "Float".to_symbol then
+                       if n == once "object_id".to_symbol then
+                               s = "TAG_Int((bigint)UNBOX_Float(@@@))"
+                       else if n == once "unary -".to_symbol then
+                               s = "BOX_Float(-UNBOX_Float(@@@))"
+                       else if n == once "output".to_symbol then
+                               s = "printf(\"%f\\n\", UNBOX_Float(@@@));"
+                       else if n == once "to_i".to_symbol then
+                               s = "TAG_Int((bigint)UNBOX_Float(@@@))"
+                       else if n == once "+".to_symbol then
+                               s = "BOX_Float(UNBOX_Float(@@@)+UNBOX_Float(@@@))"
+                       else if n == once "-".to_symbol then
+                               s = "BOX_Float(UNBOX_Float(@@@)-UNBOX_Float(@@@))"
+                       else if n == once "*".to_symbol then
+                               s = "BOX_Float(UNBOX_Float(@@@)*UNBOX_Float(@@@))"
+                       else if n == once "/".to_symbol then
+                               s = "BOX_Float(UNBOX_Float(@@@)/UNBOX_Float(@@@))"
+                       else if n == once "<".to_symbol then
+                               s = "TAG_Bool(UNBOX_Float(@@@)<UNBOX_Float(@@@))"
+                       else if n == once ">".to_symbol then
+                               s = "TAG_Bool(UNBOX_Float(@@@)>UNBOX_Float(@@@))"
+                       else if n == once "<=".to_symbol then
+                               s = "TAG_Bool(UNBOX_Float(@@@)<=UNBOX_Float(@@@))"
+                       else if n == once ">=".to_symbol then
+                               s = "TAG_Bool(UNBOX_Float(@@@)>=UNBOX_Float(@@@))"
+                       end
+               else if c == once "Char".to_symbol then
+                       if n == once "object_id".to_symbol then
+                               s = "TAG_Int(UNTAG_Char(@@@))"
+                       else if n == once "unary -".to_symbol then
+                               s = "TAG_Char(-UNTAG_Char(@@@))"
+                       else if n == once "output".to_symbol then
+                               s = "printf(\"%c\", (unsigned char)UNTAG_Char(@@@));"
+                       else if n == once "ascii".to_symbol then
+                               s = "TAG_Int((unsigned char)UNTAG_Char(@@@))"
+                       else if n == once "succ".to_symbol then
+                               s = "TAG_Char(UNTAG_Char(@@@)+1)"
+                       else if n == once "prec".to_symbol then
+                               s = "TAG_Char(UNTAG_Char(@@@)-1)"
+                       else if n == once "to_i".to_symbol then
+                               s = "TAG_Int(UNTAG_Char(@@@)-'0')"
+                       else if n == once "+".to_symbol then
+                               s = "TAG_Char(UNTAG_Char(@@@)+UNTAG_Char(@@@))"
+                       else if n == once "-".to_symbol then
+                               s = "TAG_Char(UNTAG_Char(@@@)-UNTAG_Char(@@@))"
+                       else if n == once "*".to_symbol then
+                               s = "TAG_Char(UNTAG_Char(@@@)*UNTAG_Char(@@@))"
+                       else if n == once "/".to_symbol then
+                               s = "TAG_Char(UNTAG_Char(@@@)/UNTAG_Char(@@@))"
+                       else if n == once "%".to_symbol then
+                               s = "TAG_Char(UNTAG_Char(@@@)%UNTAG_Char(@@@))"
+                       else if n == once "<".to_symbol then
+                               s = "TAG_Bool(UNTAG_Char(@@@)<UNTAG_Char(@@@))"
+                       else if n == once ">".to_symbol then
+                               s = "TAG_Bool(UNTAG_Char(@@@)>UNTAG_Char(@@@))"
+                       else if n == once "<=".to_symbol then
+                               s = "TAG_Bool(UNTAG_Char(@@@)<=UNTAG_Char(@@@))"
+                       else if n == once ">=".to_symbol then
+                               s = "TAG_Bool(UNTAG_Char(@@@)>=UNTAG_Char(@@@))"
+                       else if n == once "==".to_symbol then
+                               s = "TAG_Bool((@@@)==(@@@))"
+                       else if n == once "!=".to_symbol then
+                               s = "TAG_Bool((@@@)!=(@@@))"
+                       end
+               else if c == once "Bool".to_symbol then
+                       if n == once "object_id".to_symbol then
+                               s = "TAG_Int(UNTAG_Bool(@@@))"
+                       else if n == once "unary -".to_symbol then
+                               s = "TAG_Bool(-UNTAG_Bool(@@@))"
+                       else if n == once "output".to_symbol then
+                               s = "(void)printf(UNTAG_Bool(@@@)?\"true\\n\":\"false\\n\");"
+                       else if n == once "ascii".to_symbol then
+                               s = "TAG_Bool(UNTAG_Bool(@@@))"
+                       else if n == once "to_i".to_symbol then
+                               s = "TAG_Int(UNTAG_Bool(@@@))"
+                       else if n == once "==".to_symbol then
+                               s = "TAG_Bool((@@@)==(@@@))"
+                       else if n == once "!=".to_symbol then
+                               s = "TAG_Bool((@@@)!=(@@@))"
+                       end
+               else if c == once "NativeArray".to_symbol then
+                       if n == once "object_id".to_symbol then
+                               s = "TAG_Int(UNBOX_NativeArray(@@@))"
+                       else if n == once "[]".to_symbol then
+                               s = "UNBOX_NativeArray(@@@)[UNTAG_Int(@@@)]"
+                       else if n == once "[]=".to_symbol then
+                               s = "UNBOX_NativeArray(@@@)[UNTAG_Int(@@@)]=@@@;"
+                       else if n == once "copy_to".to_symbol then
+                               var t = p[0]
+                               p[0] = p[1]
+                               p[1] = t
+                               s = "(void)memcpy(UNBOX_NativeArray(@@@), UNBOX_NativeArray(@@@), UNTAG_Int(@@@)*sizeof(val_t));"
+                       end
+               else if c == once "NativeString".to_symbol then
+                       if n == once "object_id".to_symbol then
+                               s = "TAG_Int(UNBOX_NativeString(@@@))"
+                       else if n == once "atoi".to_symbol then
+                               s = "TAG_Int(atoi(UNBOX_NativeString(@@@)))"
+                       else if n == once "[]".to_symbol then
+                               s = "TAG_Char(UNBOX_NativeString(@@@)[UNTAG_Int(@@@)])"
+                       else if n == once "[]=".to_symbol then
+                               s = "UNBOX_NativeString(@@@)[UNTAG_Int(@@@)]=UNTAG_Char(@@@);"
+                       else if n == once "copy_to".to_symbol then
+                               var t = p[0]
+                               p[0] = p[1]
+                               p[1] = p[4]
+                               p[4] = p[2]
+                               p[2] = t
+                               s = "(void)memcpy(UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNTAG_Int(@@@));"
+                       end
+               else if n == once "object_id".to_symbol then
+                       s = "TAG_Int((bigint)@@@)"
+               else if n == once "sys".to_symbol then
+                       s = "(G_sys)"
+               else if n == once "is_same_type".to_symbol then
+                       s = "TAG_Bool((VAL2VFT(@@@)==VAL2VFT(@@@)))"
+               else if n == once "exit".to_symbol then
+                       p[0] = p[1]
+                       s = "exit(UNTAG_Int(@@@));"
+               else if n == once "calloc_array".to_symbol then
+                       p[0] = p[1]
+                       s = "BOX_NativeArray((val_t*)malloc((UNTAG_Int(@@@) * sizeof(val_t))))"
+               else if n == once "calloc_string".to_symbol then
+                       p[0] = p[1]
+                       s = "BOX_NativeString((char*)malloc((UNTAG_Int(@@@) * sizeof(char))))"
+               end
+               if s == null then
+                       v.visitor.error(self, "Fatal error: unknown intern method {method.full_name}.")
+                       s = "NIT_NULL"
+               end
+               var rtype = method.signature.return_type
+               if rtype != null then
+                       v.add_return_value(v.expr(new INative(s, p), rtype))
+               else
+                       v.stmt(new INative(s, p))
+               end
+       end
+end
+
+###############################################################################
+
+redef class PExpr
+       redef fun accept_icode_generation(v) do end
+
+       # Generate icode sequence in the current A2IContext
+       # This method should not be called direclty: use generate_expr and generate_stmt from A2IContext instead
+       protected fun generate_icode(v: A2IContext): nullable IRegister is abstract
+end
+
+redef class AVardeclExpr
+       redef fun generate_icode(v)
+       do
+               var reg = v.variable(variable)
+               var ne = n_expr
+               if ne != null then
+                       v.add_assignment(reg, v.generate_expr(ne))
+               end
+               return null
+       end
+end
+
+redef class ABlockExpr
+       redef fun generate_icode(v)
+       do
+               for ne in n_expr do v.generate_stmt(ne)
+               return null
+       end
+end
+
+redef class ADoExpr
+       redef fun generate_icode(v)
+       do
+               v.generate_stmt(n_block)
+               return null
+       end
+end
+
+redef class AReturnExpr
+       redef fun generate_icode(v)
+       do
+               var ne = n_expr
+               if ne != null then
+                       v.add_assignment(v.return_value.as(not null), v.generate_expr(ne))
+               end
+               v.stmt(new IEscape(v.return_seq.as(not null)))
+               return null
+       end
+end
+
+redef class ABreakExpr
+       redef fun generate_icode(v)
+       do
+               var ne = n_expr
+               if ne != null then
+                       v.add_assignment(escapable.break_value.as(not null), v.generate_expr(ne))
+               end
+               v.stmt(new IEscape(escapable.break_seq.as(not null)))
+               return null
+       end
+end
+
+redef class AContinueExpr
+       redef fun generate_icode(v)
+       do
+               var ne = n_expr
+               if ne != null then
+                       v.add_assignment(escapable.continue_value.as(not null), v.generate_expr(ne))
+               end
+               v.stmt(new IEscape(escapable.continue_seq.as(not null)))
+               return null
+       end
+end
+
+redef class AAbortExpr
+       redef fun generate_icode(v)
+       do
+               v.add_abort("Aborted")
+               return null
+       end
+end
+
+redef class AIfExpr
+       redef fun generate_icode(v)
+       do
+               var iif = new IIf(v.generate_expr(n_expr))
+               v.stmt(iif)
+               var seq_old = v.seq
+
+               if n_then != null then
+                       v.seq = iif.then_seq
+                       v.generate_stmt(n_then)
+               end
+
+               if n_else != null then
+                       v.seq = iif.else_seq
+                       v.generate_stmt(n_else)
+               end
+
+               v.seq = seq_old
+               return null
+       end
+end
+
+redef class AWhileExpr
+       redef fun generate_icode(v)
+       do
+               var seq_old = v.seq
+               var iloop = new ILoop
+               v.stmt(iloop)
+               escapable.break_seq = iloop
+               v.seq = iloop
+
+               # Process condition
+               var iif = new IIf(v.generate_expr(n_expr))
+               v.stmt(iif)
+
+               # Process inside (condition is true)
+               if n_block != null then
+                       v.seq = iif.then_seq
+                       escapable.continue_seq = iif.then_seq
+                       v.generate_stmt(n_block)
+               end
+
+               # Process escape (condition is false)
+               v.seq = iif.else_seq
+               v.stmt(new IEscape(iloop))
+
+               v.seq = seq_old
+               return null
+       end
+end
+
+redef class AForExpr
+       redef fun generate_icode(v)
+       do
+               var expr_type = n_expr.stype
+
+               # Get iterator
+               var meth_iterator = v.visitor.get_method(expr_type, once "iterator".to_symbol)
+
+               var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null)
+               var ireg_iter = v.expr(new ICall(meth_iterator, [v.generate_expr(n_expr)]), iter_type)
+
+               # Enter loop
+               var seq_old = v.seq
+               var iloop = new ILoop
+               v.stmt(iloop)
+               escapable.break_seq = iloop
+               v.seq = iloop
+
+               # Condition evaluation
+               var meth_is_ok = v.visitor.get_method(iter_type, once ("is_ok".to_symbol))
+               var ireg_isok = v.expr(new ICall(meth_is_ok, [ireg_iter]), v.visitor.type_bool)
+               var iif = new IIf(ireg_isok)
+
+               # Process insite the loop (condition is true)
+               v.stmt(iif)
+               v.seq = iif.then_seq
+               escapable.continue_seq = iif.then_seq
+
+               # Automatic variable assignment
+               var meth_item = v.visitor.get_method(iter_type, once ("item".to_symbol))
+               var va_stype = variable.stype.as(not null)
+               var ireg_item = v.expr(new ICall(meth_item, [ireg_iter]), va_stype)
+               var ireg_va = v.variable(variable)
+               v.add_assignment(ireg_va, ireg_item)
+
+               # Body evaluation
+               v.generate_stmt(n_block)
+
+               # Exit contition (condition is false)
+               v.seq = iif.else_seq
+               v.stmt(new IEscape(iloop))
+
+               # Next step
+               var meth_next = v.visitor.get_method(iter_type, once ("next".to_symbol))
+               v.seq = iloop
+               v.stmt(new ICall(meth_next, [ireg_iter]))
+
+               v.seq = seq_old
+               return null
+       end
+end
+
+redef class AAssertExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               var iif = new IIf(e)
+               v.stmt(iif)
+               var seq_old = v.seq
+               v.seq = iif.else_seq
+               var id = n_id
+               if id == null then
+                       v.add_abort("Assert failed")
+               else
+                       v.add_abort("Assert %s  failed", id.to_s)
+               end
+               v.seq = seq_old
+               return null
+       end
+end
+
+redef class AVarExpr
+       redef fun generate_icode(v)
+       do
+               return v.variable(variable)
+       end
+end
+
+redef class AVarAssignExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_value)
+               v.add_assignment(v.variable(variable), e)
+               return null
+       end
+end
+
+redef class AVarReassignExpr
+       redef fun generate_icode(v)
+       do
+               var e1 = v.variable(variable)
+               var e2 = v.generate_expr(n_value)
+               var e3 = v.expr(new ICall(assign_method, [e1, e2]), assign_method.signature.return_type.as(not null))
+               v.add_assignment(e1, e3)
+               return null
+       end
+end
+
+redef class ASelfExpr
+       redef fun generate_icode(v)
+       do
+               return v.variable(variable)
+       end
+end
+
+redef class AIfexprExpr
+       redef fun generate_icode(v)
+       do
+               # Process condition
+               var iif = new IIf(v.generate_expr(n_expr))
+               v.stmt(iif)
+               var seq_old = v.seq
+
+               # Prepare result
+               var reg = v.new_register(stype)
+
+               # Process 'then'
+               v.seq = iif.then_seq
+               v.add_assignment(reg, v.generate_expr(n_then))
+
+               # Process 'else'
+               v.seq = iif.else_seq
+               v.add_assignment(reg, v.generate_expr(n_else))
+
+               v.seq = seq_old
+               return reg
+       end
+end
+
+redef class AEeExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               var e2 = v.generate_expr(n_expr2)
+               return v.expr(new IIs(e, e2), stype)
+       end
+end
+
+redef class AOrExpr
+       redef fun generate_icode(v)
+       do
+               # Prepare result
+               var reg = v.new_register(stype)
+
+               # Process left operand (in a if/then)
+               var iif = new IIf(v.generate_expr(n_expr))
+               v.stmt(iif)
+               var seq_old = v.seq
+               v.seq = iif.then_seq
+               v.add_assignment(reg, v.generate_expr(n_expr))
+
+               # Process right operand (in the else)
+               v.seq = iif.else_seq
+               v.add_assignment(reg, v.generate_expr(n_expr2))
+
+               v.seq = seq_old
+               return reg
+       end
+end
+
+redef class AAndExpr
+       redef fun generate_icode(v)
+       do
+               # Prepare result
+               var reg = v.new_register(stype)
+
+               # Process left operand (in a if/else)
+               var iif = new IIf(v.generate_expr(n_expr))
+               v.stmt(iif)
+               var seq_old = v.seq
+               v.seq = iif.else_seq
+               v.add_assignment(reg, v.generate_expr(n_expr))
+
+               # Process right operand (in the then)
+               v.seq = iif.then_seq
+               v.add_assignment(reg, v.generate_expr(n_expr2))
+
+               v.seq = seq_old
+               return reg
+       end
+end
+
+redef class ANotExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               return v.expr(new INot(e), stype)
+       end
+end
+
+redef class AIsaExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               return v.expr(new ITypeCheck(e, n_type.stype), stype)
+       end
+end
+
+redef class AAsCastExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               v.add_type_cast(e, stype)
+               return e
+       end
+end
+
+redef class AAsNotnullExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               v.add_type_cast(e, stype)
+               return e
+       end
+end
+
+redef class ATrueExpr
+       redef fun generate_icode(v)
+       do
+               return v.expr(new INative("TAG_Bool(true)", null), stype)
+       end
+end
+
+redef class AFalseExpr
+       redef fun generate_icode(v)
+       do
+               return v.expr(new INative("TAG_Bool(false)", null), stype)
+       end
+end
+
+redef class AIntExpr
+       redef fun generate_icode(v)
+       do
+               return v.expr(new INative("TAG_Int({n_number.text})", null), stype)
+       end
+end
+
+redef class AFloatExpr
+       redef fun generate_icode(v)
+       do
+               return v.expr(new INative("BOX_Float({n_float.text})", null), stype)
+       end
+end
+
+redef class ACharExpr
+       redef fun generate_icode(v)
+       do
+               return v.expr(new INative("TAG_Char({n_char.text})", null), stype)
+       end
+end
+
+redef class AStringFormExpr
+       redef fun generate_icode(v)
+       do
+               compute_string_infos
+               var old_seq = v.seq
+               var ionce = new IOnce
+               var reg = v.expr(ionce, stype)
+               v.seq = ionce.body
+               var ns = v.expr(new INative("BOX_NativeString(\"{_cstring}\")", null), v.visitor.type_nativestring)
+               var ni = v.expr(new INative("TAG_Int({_cstring_length})", null), v.visitor.type_int)
+               var prop = v.visitor.get_method(stype, once "with_native".to_symbol)
+               var e = v.expr(new INew(stype, prop, [ns, ni]), stype)
+               v.add_assignment(reg, e)
+               v.seq = old_seq
+               return reg
+       end
+
+       # The raw string value
+       protected fun string_text: String is abstract
+
+       # The string in a C native format
+       protected var _cstring: nullable String
+
+       # The string length in bytes
+       protected var _cstring_length: nullable Int
+
+       # Compute _cstring and _cstring_length using string_text
+       protected fun compute_string_infos
+       do
+               var len = 0
+               var str = string_text
+               var res = new Buffer
+               var i = 0
+               while i < str.length do
+                       var c = str[i]
+                       if c == '\\' then
+                               i = i + 1
+                               var c2 = str[i]
+                               if c2 != '{' and c2 != '}' then
+                                       res.add(c)
+                               end
+                               c = c2
+                       end
+                       len = len + 1
+                       res.add(c)
+                       i = i + 1
+               end
+               _cstring = res.to_s
+               _cstring_length = len
+       end
+end
+
+redef class AStringExpr
+       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+redef class AStartStringExpr
+       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+redef class AMidStringExpr
+       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+redef class AEndStringExpr
+       redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2)
+end
+
+redef class ASuperstringExpr
+       redef fun generate_icode(v)
+       do
+               var array = v.add_new_array(atype, n_exprs.length)
+               var prop_to_s = v.visitor.get_method(v.visitor.type_object, once "to_s".to_symbol)
+               for ne in n_exprs do
+                       var e = v.generate_expr(ne)
+                       if ne.stype != stype then
+                               e = v.expr(new ICall(prop_to_s, [e]), stype)
+                       end
+                       v.add_call_array_add(array, e)
+               end
+               return v.expr(new ICall(prop_to_s, [array]), stype)
+       end
+end
+
+redef class ANullExpr
+       redef fun generate_icode(v)
+       do
+               return v.new_register(stype)
+       end
+end
+
+redef class AArrayExpr
+       redef fun generate_icode(v)
+       do
+               var recv = v.add_new_array(stype, n_exprs.length)
+               for ne in n_exprs do
+                       var e = v.generate_expr(ne)
+                       v.add_call_array_add(recv, e)
+               end
+               return recv
+       end
+end
+
+redef class ACrangeExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               var e2 = v.generate_expr(n_expr2)
+               var prop = v.visitor.get_method(stype, once "init".to_symbol)
+               return v.expr(new INew(stype, prop, [e, e2]), stype)
+       end
+end
+
+redef class AOrangeExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               var e2 = v.generate_expr(n_expr2)
+               var prop = v.visitor.get_method(stype, once "without_last".to_symbol)
+               return v.expr(new INew(stype, prop, [e, e2]), stype)
+       end
+end
+
+redef class ASuperExpr
+       redef fun generate_icode(v)
+       do
+               var arity = v.iroutine.params.length - 1
+               if init_in_superclass != null then
+                       arity = init_in_superclass.signature.arity
+               end
+               var args = new Array[IRegister].with_capacity(arity + 1)
+               args.add(v.iroutine.params[0])
+               if n_args.length != arity then
+                       for i in [0..arity[ do
+                               args.add(v.iroutine.params[i + 1])
+                       end
+               else
+                       for na in n_args do
+                               args.add(v.generate_expr(na))
+                       end
+               end
+               var p = init_in_superclass
+               if p != null then
+                       var rtype = p.signature.return_type
+                       if rtype != null then
+                               return v.expr(new ICall(p, args), rtype)
+                       else
+                               v.stmt(new ICall(p, args))
+                               return null
+                       end
+               else
+                       var p = prop
+                       var rtype = p.signature.return_type
+                       if rtype == null then
+                               v.stmt(new ISuper(p, args))
+                               return null
+                       else
+                               return v.expr(new ISuper(p, args), rtype)
+                       end
+               end
+       end
+end
+
+redef class AAttrExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               return v.add_attr_read(prop, e)
+       end
+end
+
+redef class AAttrAssignExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               var e2 = v.generate_expr(n_value)
+               v.stmt(new IAttrWrite(prop, e, e2))
+               return null
+       end
+end
+redef class AAttrReassignExpr
+       redef fun generate_icode(v)
+       do
+               var e1 = v.generate_expr(n_expr)
+               var e2 = v.expr(new IAttrRead(prop, e1), attr_type)
+               var e3 = v.generate_expr(n_value)
+               var e4 = v.expr(new ICall(assign_method, [e2, e3]), attr_type)
+               v.stmt(new IAttrWrite(prop, e1, e4))
+               return null
+       end
+end
+
+redef class AIssetAttrExpr
+       redef fun generate_icode(v)
+       do
+               var e = v.generate_expr(n_expr)
+               return v.expr(new IAttrIsset(prop, e), stype)
+       end
+end
+
+redef class AAbsAbsSendExpr
+       # Compile each argument and add them to the array
+       fun generate_icode_for_arguments_in(v: A2IContext, args: Array[IRegister])
+       do
+               for a in arguments do
+                       args.add(v.generate_expr(a))
+               end
+       end
+end
+
+redef class ASendExpr
+       redef fun generate_icode(v)
+       do
+               var recv = v.generate_expr(n_expr)
+               var args = new Array[IRegister]
+               args.add(recv)
+               generate_icode_for_arguments_in(v, args)
+               var prop = prop
+               var r: nullable IRegister = null # The full result of the send (raw call + breaks)
+               var r2: nullable IRegister # The raw result of the call
+
+               # Prepare closures
+               var seq_old = v.seq
+               var closcns: nullable Array[nullable IClosureDef] = null
+               if not prop_signature.closures.is_empty then
+                       var rtype = prop_signature.return_type
+                       if rtype != null then
+                               r = v.new_register(rtype)
+                       end
+                       var seq = new ISeq
+                       v.stmt(seq)
+                       v.seq = seq
+                       closcns = new Array[nullable IClosureDef]
+                       var cdarity = 0
+                       if closure_defs != null then cdarity = closure_defs.length
+                       for i in [0..cdarity[ do
+                               closure_defs[i].escapable.break_seq = seq
+                               closure_defs[i].escapable.break_value = r
+                               var cn = closure_defs[i].generate_iclosuredef(v)
+                               closcns.add(cn)
+                       end
+                       for i in [cdarity..prop_signature.closures.length[ do
+                               closcns.add(null)
+                       end
+               end
+
+               r2 = v.add_call(prop, args, closcns)
+
+               # Closure work
+               if not prop_signature.closures.is_empty then
+                       if r != null and r2 != null then v.add_assignment(r, r2)
+                       v.seq = seq_old
+               else
+                       r = r2
+               end
+
+               if prop.global.is_init then
+                       v.invoke_super_init_calls_after(prop)
+               end
+               return r
+       end
+end
+
+redef class ASendReassignExpr
+       redef fun generate_icode(v)
+       do
+               var recv = v.generate_expr(n_expr)
+               var args = new Array[IRegister]
+               args.add(recv)
+               generate_icode_for_arguments_in(v, args)
+
+               var e2 = v.expr(new ICall(read_prop, args), read_prop.signature.return_type.as(not null))
+               var e3 = v.generate_expr(n_value)
+               var e4 = v.expr(new ICall(assign_method, [e2, e3]), assign_method.signature.return_type.as(not null))
+               var args2 = args.to_a
+               args2.add(e4)
+               v.stmt(new ICall(prop, args2))
+               return null
+       end
+end
+
+redef class ANewExpr
+       redef fun generate_icode(v)
+       do
+               var args = new Array[IRegister]
+               generate_icode_for_arguments_in(v, args)
+               return v.expr(new INew(stype, prop, args), stype)
+       end
+end
+
+redef class AProxyExpr
+       redef fun generate_icode(v)
+       do
+               return v.generate_expr(n_expr)
+       end
+end
+
+redef class AOnceExpr
+       redef fun generate_icode(v)
+       do
+               var ionce = new IOnce
+               var reg = v.expr(ionce, stype)
+               var old_seq = v.seq
+               v.seq = ionce.body
+
+               var e = v.generate_expr(n_expr)
+               v.add_assignment(reg, e)
+
+               v.seq = old_seq
+               return reg
+       end
+end
+
+
+redef class PClosureDef
+       var _iclosure_def: nullable IClosureDef
+
+       fun generate_iclosuredef(v: A2IContext): IClosureDef is abstract
+end
+
+redef class AClosureDef
+       redef fun generate_iclosuredef(v)
+       do
+               # Prepare signature
+               var args = new Array[IRegister]
+               var sig = closure.signature
+               for i in [0..sig.arity[ do
+                       args.add(v.new_register(sig[i]))
+               end
+               var ret: nullable IRegister = null
+               var rtype = sig.return_type
+               if rtype != null then
+                       ret = v.new_register(rtype)
+               end
+
+               var iclos = new IClosureDef(args, ret)
+               iclos.location = location
+
+               # Prepare env
+               var seq_old = v.seq
+               v.seq = iclos.body
+               escapable.continue_seq = iclos.body
+               escapable.continue_value = iclos.result
+
+               # Assign parameters
+               for i in [0..variables.length[ do
+                       var res = v.variable(variables[i])
+                       v.add_assignment(res, iclos.params[i])
+               end
+
+               v.generate_stmt(n_expr)
+
+               v.seq = seq_old
+               _iclosure_def = iclos
+               return iclos
+       end
+end
+
+redef class AClosureCallExpr
+       redef fun generate_icode(v)
+       do
+               # Geneate arguments
+               var args = new Array[IRegister]
+               generate_icode_for_arguments_in(v, args)
+
+               # Prepare icall
+               var closdecl = v.closurevariables[variable]
+               var icall = new IClosCall(closdecl, args)
+               var seq_old = v.seq
+
+               # Fill break of ical
+               if n_closure_defs.length == 1 then do
+                       var iseq = new ISeq
+                       icall.break_seq = iseq
+                       v.seq = iseq
+                       v.generate_stmt(n_closure_defs.first.as(AClosureDef).n_expr)
+                       v.seq = seq_old
+               end
+
+               # Prepare in case of default block
+               var iif: nullable IIf = null # The iif of default block
+               var closdecl_default = closdecl.default # The default (if any)
+               if closdecl_default != null then
+                       iif = new IIf(v.expr(new IHasClos(closdecl), v.visitor.type_bool))
+                       v.stmt(iif)
+                       v.seq = iif.then_seq
+               end
+
+               # Add the icall
+               var r2: nullable IRegister = null # the result of the icall
+               var rtype = variable.closure.signature.return_type
+               if rtype == null then
+                       v.stmt(icall)
+               else
+                       r2 = v.expr(icall, rtype)
+               end
+
+               # Process the case of default block
+               var r: nullable IRegister = null # the real result
+               if closdecl_default != null then
+                       assert iif != null
+                       if r2 != null then
+                               assert rtype != null
+                               r = v.new_register(rtype)
+                               v.add_assignment(r, r2)
+                       end
+                       v.seq = iif.else_seq
+                       var r3 = closdecl_default.inline_in_seq(iif.else_seq, args)
+                       if r != null then
+                               assert r3 != null
+                               v.add_assignment(r, r3)
+                       end
+                       v.seq = seq_old
+               else
+                       r = r2
+               end
+               return r
+       end
+end
index 8f38abd..68a1ebb 100644 (file)
@@ -21,6 +21,7 @@ package syntax
 import mmloader
 import mmbuilder
 import typing
+import icode_generation
 
 # Loader of nit source files
 class SrcModuleLoader
@@ -72,6 +73,9 @@ redef class MMSrcModule
 
                do_typing(tc)
                tc.check_errors
+
+               generate_icode(tc)
+               tc.check_errors
        end
 end
 
index cc626c1..5d9c0a8 100644 (file)
@@ -783,26 +783,10 @@ redef class AAttrFormExpr
        fun attr_type: MMType is abstract
 end
 
-redef class AStringFormExpr
-       fun meth_with_native: MMMethod is abstract
-end
-
 redef class ASuperstringExpr
-       fun meth_with_capacity: MMMethod is abstract
-       fun meth_add: MMMethod is abstract
-       fun meth_to_s: MMMethod is abstract
        fun atype: MMType is abstract
 end
 
-redef class AArrayExpr
-       fun meth_with_capacity: MMMethod is abstract
-       fun meth_add: MMMethod is abstract
-end
-
-redef class ARangeExpr
-       fun meth_init: MMMethod is abstract
-end
-
 redef class AVardeclExpr
        # Assiociated local variable
        fun variable: VarVariable is abstract
@@ -812,34 +796,26 @@ end
 redef class AForExpr
        # Associated automatic local variable
        fun variable: AutoVariable is abstract
-       fun meth_iterator: MMMethod is abstract
-       fun meth_is_ok: MMMethod is abstract
-       fun meth_item: MMMethod is abstract
-       fun meth_next: MMMethod is abstract
 end
 
 redef class ASelfExpr
        # Associated local variable
        fun variable: ParamVariable is abstract
-       #readable writable var _variable: nullable ParamVariable
 end
 
 redef class AVarFormExpr
        # Associated local variable
        fun variable: Variable is abstract
-       #readable writable var _variable: nullable Variable
 end
 
 redef class AClosureCallExpr
 special AAbsAbsSendExpr
        # Associated closure variable
        fun variable: ClosureVariable is abstract
-       #readable writable var _variable: nullable ClosureVariable
 end
 
 redef class PClosureDef
        # Associated closure
-       #readable writable var _closure: nullable MMClosure
        fun closure: MMClosure is abstract
 
        # Automatic variables
index e2e6a2f..e89d9e2 100644 (file)
@@ -535,14 +535,6 @@ redef class AForExpr
        # The corresponding escapable block
        readable var _escapable: nullable EscapableBlock
 
-       var _meth_iterator: nullable MMMethod
-       redef fun meth_iterator: MMMethod do return _meth_iterator.as(not null)
-       var _meth_is_ok: nullable MMMethod
-       redef fun meth_is_ok: MMMethod do return _meth_is_ok.as(not null)
-       var _meth_item: nullable MMMethod
-       redef fun meth_item: MMMethod do return _meth_item.as(not null)
-       var _meth_next: nullable MMMethod
-       redef fun meth_next: MMMethod do return _meth_next.as(not null)
        redef fun accept_typing(v)
        do
                var escapable = new EscapableBlock(self)
@@ -563,30 +555,13 @@ redef class AForExpr
                if not v.check_conform_expr(n_expr, v.type_collection) then return
                var expr_type = n_expr.stype
 
-               _meth_iterator = expr_type.local_class.select_method(once ("iterator".to_symbol))
-               if _meth_iterator == null then
-                       v.error(self, "Error: Collection MUST have an iterate method")
-                       return
-               end
-               var iter_type = _meth_iterator.signature_for(expr_type).return_type.as(not null)
-               _meth_is_ok = iter_type.local_class.select_method(once ("is_ok".to_symbol))
-               if _meth_is_ok == null then
-                       v.error(self, "Error: {iter_type} MUST have an is_ok method")
-                       return
-               end
-               _meth_item = iter_type.local_class.select_method(once ("item".to_symbol))
-               if _meth_item == null then
-                       v.error(self, "Error: {iter_type} MUST have an item method")
-                       return
-               end
-               _meth_next = iter_type.local_class.select_method(once ("next".to_symbol))
-               if _meth_next == null then
-                       v.error(self, "Error: {iter_type} MUST have a next method")
-                       return
-               end
-               var t = _meth_item.signature_for(iter_type).return_type
-               if not n_expr.is_self then t = t.not_for_self
-               va.stype = t
+               # Get iterator
+               var meth_iterator = v.get_method(expr_type, once "iterator".to_symbol)
+               var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null)
+               var meth_item = v.get_method(iter_type, once ("item".to_symbol))
+               var va_stype = meth_item.signature_for(iter_type).return_type.as(not null)
+               if not n_expr.is_self then va_stype = va_stype.not_for_self
+               va.stype = va_stype
 
                # Body evaluation
                if n_block != null then v.enter_visit(n_block)
@@ -854,24 +829,14 @@ redef class ACharExpr
 end
 
 redef class AStringFormExpr
-       var _meth_with_native: nullable MMMethod
-       redef fun meth_with_native: MMMethod do return _meth_with_native.as(not null)
        redef fun after_typing(v)
        do
                _stype = v.type_string
                _is_typed = true
-               _meth_with_native = _stype.local_class.select_method(once "with_native".to_symbol)
-               if _meth_with_native == null then v.error(self, "{_stype} MUST have a with_native method.")
        end
 end
 
 redef class ASuperstringExpr
-       redef fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null)
-       var _meth_with_capacity: nullable MMMethod
-       redef fun meth_add: MMMethod do return _meth_add.as(not null)
-       var _meth_add: nullable MMMethod
-       redef fun meth_to_s: MMMethod do return _meth_to_s.as(not null)
-       var _meth_to_s: nullable MMMethod
        redef fun atype do return _atype.as(not null)
        var _atype: nullable MMType
        redef fun after_typing(v)
@@ -880,12 +845,6 @@ redef class ASuperstringExpr
                _stype = stype
                var atype = v.type_array(stype)
                _atype = atype
-               _meth_with_capacity = atype.local_class.select_method(once "with_capacity".to_symbol)
-               if _meth_with_capacity == null then v.error(self, "{_atype} MUST have a with_capacity method.")
-               _meth_add = atype.local_class.select_method(once "add".to_symbol)
-               if _meth_add == null then v.error(self, "{_atype} MUST have an add method.")
-               _meth_to_s = v.type_object.local_class.select_method(once "to_s".to_symbol)
-               if _meth_to_s == null then v.error(self, "Object MUST have a to_s method.")
                _is_typed = true
        end
 end
@@ -899,11 +858,6 @@ redef class ANullExpr
 end
 
 redef class AArrayExpr
-       redef fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null)
-       var _meth_with_capacity: nullable MMMethod
-       redef fun meth_add: MMMethod do return _meth_add.as(not null)
-       var _meth_add: nullable MMMethod
-
        redef fun after_typing(v)
        do
                var stype = v.check_conform_multiexpr(null, n_exprs)
@@ -913,19 +867,11 @@ redef class AArrayExpr
        private fun do_typing(v: TypingVisitor, element_type: MMType)
        do
                _stype = v.type_array(element_type)
-
-               _meth_with_capacity = _stype.local_class.select_method(once "with_capacity".to_symbol)
-               if _meth_with_capacity == null then v.error(self, "{_stype} MUST have a with_capacity method.")
-               _meth_add = _stype.local_class.select_method(once "add".to_symbol)
-               if _meth_add == null then v.error(self, "{_stype} MUST have an add method.")
-
                _is_typed = true
        end
 end
 
 redef class ARangeExpr
-       redef fun meth_init: MMMethod do return _meth_init.as(not null)
-       var _meth_init: nullable MMMethod
        redef fun after_typing(v)
        do
                if not v.check_expr(n_expr) or not v.check_expr(n_expr2) then return
@@ -944,24 +890,6 @@ redef class ARangeExpr
        end
 end
 
-redef class ACrangeExpr
-       redef fun after_typing(v)
-       do
-               super
-               if not is_typed then return
-               _meth_init = stype.local_class.select_method(once "init".to_symbol)
-       end
-end
-redef class AOrangeExpr
-       redef fun after_typing(v)
-       do
-               super
-               if not is_typed then return
-               _meth_init = stype.local_class.select_method(once "without_last".to_symbol)
-       end
-end
-
-
 redef class ASuperExpr
        redef readable var _init_in_superclass: nullable MMMethod
        redef fun after_typing(v)
index 6033ed3..113b78b 100644 (file)
@@ -7,7 +7,9 @@ false
 3
 true
 false
-Uninitialized attribute base_attr_isset_alt3::Foo::_a2 at base_attr_isset_alt3::Foo::init for Foo.
+Uninitialized attribute _a2 (alt/base_attr_isset_alt3.nit)
 ,---- Stack trace -- - -  -
+| check new Foo (alt/base_attr_isset_alt3.nit:0)
+| new Foo base_attr_isset_alt3::Foo::init (alt/base_attr_isset_alt3.nit:53)
 | base_attr_isset_alt3::Sys::main (alt/base_attr_isset_alt3.nit:100)
 `------------------- - -  -
index 514d4ae..15edaf3 100644 (file)
@@ -7,7 +7,9 @@ false
 3
 true
 false
-Uninitialized attribute base_attr_isset_alt4::Foo::_a2 at base_attr_isset_alt4::Foo::init for Baz.
+Uninitialized attribute _a2 (alt/base_attr_isset_alt4.nit)
 ,---- Stack trace -- - -  -
+| check new Baz (alt/base_attr_isset_alt4.nit:0)
+| new Baz base_attr_isset_alt4::Foo::init (alt/base_attr_isset_alt4.nit:53)
 | base_attr_isset_alt4::Sys::main (alt/base_attr_isset_alt4.nit:99)
 `------------------- - -  -
index ca81b10..c3800b8 100644 (file)
@@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_alt1::Foo::run (alt/base_attr_
 ,---- Stack trace -- - -  -
 | base_attr_nullable_alt1::Foo::run (alt/base_attr_nullable_alt1.nit:36)
 | base_attr_nullable_alt1::Foo::init (alt/base_attr_nullable_alt1.nit:48)
+| new Foo base_attr_nullable_alt1::Foo::init (alt/base_attr_nullable_alt1.nit:48)
 | base_attr_nullable_alt1::Sys::main (alt/base_attr_nullable_alt1.nit:81)
 `------------------- - -  -
index 999e869..428d2f8 100644 (file)
@@ -1,8 +1,9 @@
 1
-Uninitialized attribute _a2 (alt/base_attr_nullable_alt2.nit:35)
+Uninitialized attribute _a2 in base_attr_nullable_alt2::Foo::a2 (alt/base_attr_nullable_alt2.nit:35)
 ,---- Stack trace -- - -  -
 | base_attr_nullable_alt2::Foo::a2 (alt/base_attr_nullable_alt2.nit:35)
 | base_attr_nullable_alt2::Foo::run (alt/base_attr_nullable_alt2.nit:36)
 | base_attr_nullable_alt2::Foo::init (alt/base_attr_nullable_alt2.nit:48)
+| new Foo base_attr_nullable_alt2::Foo::init (alt/base_attr_nullable_alt2.nit:48)
 | base_attr_nullable_alt2::Sys::main (alt/base_attr_nullable_alt2.nit:81)
 `------------------- - -  -
index 0ee9fff..0888aad 100644 (file)
@@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_alt3::Bar::(base_attr_nullable
 ,---- Stack trace -- - -  -
 | base_attr_nullable_alt3::Bar::(base_attr_nullable_alt3::Foo::run) (alt/base_attr_nullable_alt3.nit:62)
 | base_attr_nullable_alt3::Bar::init (alt/base_attr_nullable_alt3.nit:69)
+| new Bar base_attr_nullable_alt3::Bar::init (alt/base_attr_nullable_alt3.nit:69)
 | base_attr_nullable_alt3::Sys::main (alt/base_attr_nullable_alt3.nit:81)
 `------------------- - -  -
index 2a4dd60..b387535 100644 (file)
@@ -3,5 +3,6 @@ Uninitialized attribute _a2 in base_attr_nullable_alt4::Foo::run_other (alt/base
 ,---- Stack trace -- - -  -
 | base_attr_nullable_alt4::Foo::run_other (alt/base_attr_nullable_alt4.nit:42)
 | base_attr_nullable_alt4::Bar::init (alt/base_attr_nullable_alt4.nit:69)
+| new Bar base_attr_nullable_alt4::Bar::init (alt/base_attr_nullable_alt4.nit:69)
 | base_attr_nullable_alt4::Sys::main (alt/base_attr_nullable_alt4.nit:81)
 `------------------- - -  -
index 07bfb3c..798d8d7 100644 (file)
@@ -4,5 +4,6 @@ Uninitialized attribute _a3 in base_attr_nullable_alt5::Bar::(base_attr_nullable
 ,---- Stack trace -- - -  -
 | base_attr_nullable_alt5::Bar::(base_attr_nullable_alt5::Foo::run) (alt/base_attr_nullable_alt5.nit:62)
 | base_attr_nullable_alt5::Bar::init (alt/base_attr_nullable_alt5.nit:69)
+| new Bar base_attr_nullable_alt5::Bar::init (alt/base_attr_nullable_alt5.nit:69)
 | base_attr_nullable_alt5::Sys::main (alt/base_attr_nullable_alt5.nit:81)
 `------------------- - -  -
index c8d5ccf..32184bb 100644 (file)
@@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_int_alt1::Foo::run (alt/base_a
 ,---- Stack trace -- - -  -
 | base_attr_nullable_int_alt1::Foo::run (alt/base_attr_nullable_int_alt1.nit:30)
 | base_attr_nullable_int_alt1::Foo::init (alt/base_attr_nullable_int_alt1.nit:42)
+| new Foo base_attr_nullable_int_alt1::Foo::init (alt/base_attr_nullable_int_alt1.nit:42)
 | base_attr_nullable_int_alt1::Sys::main (alt/base_attr_nullable_int_alt1.nit:75)
 `------------------- - -  -
index 61a7d03..42bbe46 100644 (file)
@@ -1,8 +1,9 @@
 1
-Uninitialized attribute _a2 (alt/base_attr_nullable_int_alt2.nit:29)
+Uninitialized attribute _a2 in base_attr_nullable_int_alt2::Foo::a2 (alt/base_attr_nullable_int_alt2.nit:29)
 ,---- Stack trace -- - -  -
 | base_attr_nullable_int_alt2::Foo::a2 (alt/base_attr_nullable_int_alt2.nit:29)
 | base_attr_nullable_int_alt2::Foo::run (alt/base_attr_nullable_int_alt2.nit:30)
 | base_attr_nullable_int_alt2::Foo::init (alt/base_attr_nullable_int_alt2.nit:42)
+| new Foo base_attr_nullable_int_alt2::Foo::init (alt/base_attr_nullable_int_alt2.nit:42)
 | base_attr_nullable_int_alt2::Sys::main (alt/base_attr_nullable_int_alt2.nit:75)
 `------------------- - -  -
index 7587dfd..40863a2 100644 (file)
@@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_int_alt3::Bar::(base_attr_null
 ,---- Stack trace -- - -  -
 | base_attr_nullable_int_alt3::Bar::(base_attr_nullable_int_alt3::Foo::run) (alt/base_attr_nullable_int_alt3.nit:56)
 | base_attr_nullable_int_alt3::Bar::init (alt/base_attr_nullable_int_alt3.nit:63)
+| new Bar base_attr_nullable_int_alt3::Bar::init (alt/base_attr_nullable_int_alt3.nit:63)
 | base_attr_nullable_int_alt3::Sys::main (alt/base_attr_nullable_int_alt3.nit:75)
 `------------------- - -  -
index 7880ac1..92447fc 100644 (file)
@@ -3,5 +3,6 @@ Uninitialized attribute _a2 in base_attr_nullable_int_alt4::Foo::run_other (alt/
 ,---- Stack trace -- - -  -
 | base_attr_nullable_int_alt4::Foo::run_other (alt/base_attr_nullable_int_alt4.nit:36)
 | base_attr_nullable_int_alt4::Bar::init (alt/base_attr_nullable_int_alt4.nit:63)
+| new Bar base_attr_nullable_int_alt4::Bar::init (alt/base_attr_nullable_int_alt4.nit:63)
 | base_attr_nullable_int_alt4::Sys::main (alt/base_attr_nullable_int_alt4.nit:75)
 `------------------- - -  -
index cbe4cde..f5e49d8 100644 (file)
@@ -4,5 +4,6 @@ Uninitialized attribute _a3 in base_attr_nullable_int_alt5::Bar::(base_attr_null
 ,---- Stack trace -- - -  -
 | base_attr_nullable_int_alt5::Bar::(base_attr_nullable_int_alt5::Foo::run) (alt/base_attr_nullable_int_alt5.nit:56)
 | base_attr_nullable_int_alt5::Bar::init (alt/base_attr_nullable_int_alt5.nit:63)
+| new Bar base_attr_nullable_int_alt5::Bar::init (alt/base_attr_nullable_int_alt5.nit:63)
 | base_attr_nullable_int_alt5::Sys::main (alt/base_attr_nullable_int_alt5.nit:75)
 `------------------- - -  -
index 843c7f0..a87fb02 100644 (file)
@@ -4,7 +4,7 @@ true
 
 true
 true
-false
+true
 true
 
 true