Merge: new `with` statement
[nit.git] / src / interpreter / naive_interpreter.nit
index 6cd0bd0..272219f 100644 (file)
@@ -20,10 +20,12 @@ module naive_interpreter
 import literal
 import semantize
 private import parser::tables
+import mixin
+import primitive_types
 
 redef class ToolContext
        # --discover-call-trace
-       var opt_discover_call_trace: OptionBool = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
+       var opt_discover_call_trace = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
 
        redef init
        do
@@ -57,7 +59,7 @@ class NaiveInterpreter
        # The modelbuilder that know the AST and its associations with the model
        var modelbuilder: ModelBuilder
 
-       # The main moduleof the program (used to lookup methoda
+       # The main module of the program (used to lookup method)
        var mainmodule: MModule
 
        # The command line arguments of the interpreted program
@@ -65,16 +67,18 @@ class NaiveInterpreter
        # arguments[1] is the first argument
        var arguments: Array[String]
 
-       var mainobj: nullable Instance
+       # The main Sys instance
+       var mainobj: nullable Instance is noinit
 
-       init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String])
+       init
        do
-               self.modelbuilder = modelbuilder
-               self.mainmodule = mainmodule
-               self.arguments = arguments
-               self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
-               self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
-               self.null_instance = new MutableInstance(mainmodule.model.null_type)
+               if mainmodule.model.get_mclasses_by_name("Bool") != null then
+                       self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
+                       init_instance_primitive(self.true_instance)
+                       self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
+                       init_instance_primitive(self.false_instance)
+               end
+               self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null)
        end
 
        # Starts the interpreter on the main module of a program
@@ -99,56 +103,40 @@ class NaiveInterpreter
        # Subtype test in the context of the mainmodule
        fun is_subtype(sub, sup: MType): Bool
        do
-               return sub.is_subtype(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType), sup)
+               return sub.is_subtype(self.mainmodule, current_receiver_class, sup)
        end
 
+       # Get a primitive method in the context of the main module
        fun force_get_primitive_method(name: String, recv: MType): MMethod
        do
                assert recv isa MClassType
-               return self.modelbuilder.force_get_primitive_method(self.frame.current_node, name, recv.mclass, self.mainmodule)
+               return self.modelbuilder.force_get_primitive_method(current_node, name, recv.mclass, self.mainmodule)
        end
 
        # Is a return executed?
        # Set this mark to skip the evaluation until the end of the specified method frame
-       var returnmark: nullable Frame = null
-
-       # Is a break executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_break`
-       var breakmark: nullable EscapeMark = null
+       var returnmark: nullable FRAME = null
 
-       # Is a continue executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_continue`
-       var continuemark: nullable EscapeMark = null
+       # Is a break or a continue executed?
+       # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
+       var escapemark: nullable EscapeMark = null
 
        # Is a return or a break or a continue executed?
        # Use this function to know if you must skip the evaluation of statements
-       fun is_escaping: Bool do return returnmark != null or breakmark != null or continuemark != null
+       fun is_escaping: Bool do return returnmark != null or escapemark != null
 
        # The value associated with the current return/break/continue, if any.
        # Set the value when you set a escapemark.
        # Read the value when you catch a mark or reach the end of a method
        var escapevalue: nullable Instance = null
 
-       # If there is a break and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no break or if `escapemark` is null then return false.
-       # Use this function to catch a potential break.
-       fun is_break(escapemark: nullable EscapeMark): Bool
-       do
-               if escapemark != null and self.breakmark == escapemark then
-                       self.breakmark = null
-                       return true
-               else
-                       return false
-               end
-       end
-
-       # If there is a continue and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no continue or if `escapemark` is null then return false.
-       # Use this function to catch a potential continue.
-       fun is_continue(escapemark: nullable EscapeMark): Bool
+       # If there is a break/continue and is associated with `escapemark`, then return true and clear the mark.
+       # If there is no break/continue or if `escapemark` is null then return false.
+       # Use this function to catch a potential break/continue.
+       fun is_escape(escapemark: nullable EscapeMark): Bool
        do
-               if escapemark != null and self.continuemark == escapemark then
-                       self.continuemark = null
+               if escapemark != null and self.escapemark == escapemark then
+                       self.escapemark = null
                        return true
                else
                        return false
@@ -185,14 +173,20 @@ class NaiveInterpreter
        # If `n` cannot be evaluated, then aborts.
        fun stmt(n: nullable AExpr)
        do
-               if n != null then
-                       var frame = self.frame
-                       var old = frame.current_node
-                       frame.current_node = n
-                       #n.debug("Execute stmt")
-                       n.stmt(self)
-                       frame.current_node = old
+               if n == null then return
+
+               if n.comprehension != null then
+                       var comprehension = frame.comprehension.as(not null)
+                       var i = expr(n)
+                       if i != null then comprehension.add(i)
+                       return
                end
+
+               var frame = self.frame
+               var old = frame.current_node
+               frame.current_node = n
+               n.stmt(self)
+               frame.current_node = old
        end
 
        # Map used to store values of nodes that must be evaluated once in the system (`AOnceExpr`)
@@ -207,62 +201,98 @@ class NaiveInterpreter
        # Return the integer instance associated with `val`.
        fun int_instance(val: Int): Instance
        do
-               var ic = self.mainmodule.get_primitive_class("Int")
-               return new PrimitiveInstance[Int](ic.mclass_type, val)
+               var t = mainmodule.int_type
+               var instance = new PrimitiveInstance[Int](t, val)
+               init_instance_primitive(instance)
+               return instance
        end
 
        # Return the char instance associated with `val`.
        fun char_instance(val: Char): Instance
        do
-               var ic = self.mainmodule.get_primitive_class("Char")
-               return new PrimitiveInstance[Char](ic.mclass_type, val)
+               var t = mainmodule.char_type
+               var instance = new PrimitiveInstance[Char](t, val)
+               init_instance_primitive(instance)
+               return instance
        end
 
        # Return the float instance associated with `val`.
        fun float_instance(val: Float): Instance
        do
-               var ic = self.mainmodule.get_primitive_class("Float")
-               return new PrimitiveInstance[Float](ic.mclass_type, val)
+               var t = mainmodule.float_type
+               var instance = new PrimitiveInstance[Float](t, val)
+               init_instance_primitive(instance)
+               return instance
        end
 
-       # The unique intance of the `true` value.
-       var true_instance: Instance
+       # The unique instance of the `true` value.
+       var true_instance: Instance is noinit
 
-       # The unique intance of the `false` value.
-       var false_instance: Instance
+       # The unique instance of the `false` value.
+       var false_instance: Instance is noinit
 
-       # The unique intance of the `null` value.
-       var null_instance: Instance
+       # The unique instance of the `null` value.
+       var null_instance: Instance is noinit
 
        # Return a new array made of `values`.
        # The dynamic type of the result is Array[elttype].
        fun array_instance(values: Array[Instance], elttype: MType): Instance
        do
                assert not elttype.need_anchor
-               var nat = new PrimitiveInstance[Array[Instance]](self.mainmodule.get_primitive_class("NativeArray").get_mtype([elttype]), values)
-               var mtype = self.mainmodule.get_primitive_class("Array").get_mtype([elttype])
+               var nat = new PrimitiveInstance[Array[Instance]](mainmodule.native_array_type(elttype), values)
+               init_instance_primitive(nat)
+               var mtype = mainmodule.array_type(elttype)
                var res = new MutableInstance(mtype)
                self.init_instance(res)
                self.send(self.force_get_primitive_method("with_native", mtype), [res, nat, self.int_instance(values.length)])
                return res
        end
 
+       # Return a instance associated to a primitive class
+       # Current primitive classes are `Int`, `Bool`, and `String`
+       fun value_instance(object: Object): Instance
+       do
+               if object isa Int then
+                       return int_instance(object)
+               else if object isa Bool then
+                       return bool_instance(object)
+               else if object isa String then
+                       return string_instance(object)
+               else
+                       abort
+               end
+       end
+
        # Return a new native string initialized with `txt`
        fun native_string_instance(txt: String): Instance
        do
                var val = new FlatBuffer.from(txt)
                val.add('\0')
-               var ic = self.mainmodule.get_primitive_class("NativeString")
-               return new PrimitiveInstance[Buffer](ic.mclass_type, val)
+               var t = mainmodule.native_string_type
+               var instance = new PrimitiveInstance[Buffer](t, val)
+               init_instance_primitive(instance)
+               return instance
+       end
+
+       # Return a new String instance for `txt`
+       fun string_instance(txt: String): Instance
+       do
+               var nat = native_string_instance(txt)
+               var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.length)])
+               assert res != null
+               return res
        end
 
+       # The virtual type of the frames used in the execution engine
+       type FRAME: Frame
+
        # The current frame used to store local variables of the current method executed
-       fun frame: Frame do return frames.first
+       fun frame: FRAME do return frames.first
 
        # The stack of all frames. The first one is the current one.
-       var frames: List[Frame] = new List[Frame]
+       var frames = new List[FRAME]
 
-       # Return a stack stace. One line per function
+       # Return a stack trace. One line per function
        fun stack_trace: String
        do
                var b = new FlatBuffer
@@ -274,13 +304,36 @@ class NaiveInterpreter
                return b.to_s
        end
 
+       # The current node, used to print errors, debug and stack-traces
+       fun current_node: nullable ANode
+       do
+               if frames.is_empty then return null
+               return frames.first.current_node
+       end
+
+       # The dynamic type of the current `self`
+       fun current_receiver_class: MClassType
+       do
+               return frames.first.arguments.first.mtype.as(MClassType)
+       end
+
+       # Initialize the environment for a call and return a new Frame
+       # *`node` The AST node
+       # *`mpropdef` The corresponding mpropdef
+       # *`args` Arguments of the call
+       fun new_frame(node: ANode, mpropdef: MPropDef, args: Array[Instance]): FRAME
+       do
+               return new InterpreterFrame(node, mpropdef, args)
+       end
+
        # Exit the program with a message
        fun fatal(message: String)
        do
-               if frames.is_empty then
+               var node = current_node
+               if node == null then
                        print message
                else
-                       self.frame.current_node.fatal(self, message)
+                       node.fatal(self, message)
                end
                exit(1)
        end
@@ -288,76 +341,80 @@ class NaiveInterpreter
        # Debug on the current node
        fun debug(message: String)
        do
-               if frames.is_empty then
+               var node = current_node
+               if node == null then
                        print message
                else
-                       self.frame.current_node.debug(message)
+                       node.debug(message)
                end
        end
 
        # Retrieve the value of the variable in the current frame
        fun read_variable(v: Variable): Instance
        do
-               var f = frames.first
+               var f = frames.first.as(InterpreterFrame)
                return f.map[v]
        end
 
        # Assign the value of the variable in the current frame
        fun write_variable(v: Variable, value: Instance)
        do
-               var f = frames.first
+               var f = frames.first.as(InterpreterFrame)
                f.map[v] = value
        end
 
-       # Store known method, used to trace methods as thez are reached
+       # Store known methods, used to trace methods as they are reached
        var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]
 
-       # Common code for calls to injected methods and normal methods
-       fun call_commons(mpropdef: MMethodDef, args: Array[Instance]): Array[Instance]
+       # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
+       # This method is used to manage varargs in signatures and returns the real array
+       # of instances to use in the call.
+       # Return `null` if one of the evaluation of the arguments return null.
+       fun varargize(mpropdef: MMethodDef, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
        do
-               var vararg_rank = mpropdef.msignature.vararg_rank
-               if vararg_rank >= 0 then
-                       assert args.length >= mpropdef.msignature.arity + 1 # because of self
-                       var rawargs = args
-                       args = new Array[Instance]
+               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               var res = new Array[Instance]
+               res.add(recv)
 
-                       args.add(rawargs.first) # recv
+               if args.is_empty then return res
 
-                       for i in [0..vararg_rank[ do
-                               args.add(rawargs[i+1])
-                       end
-
-                       var vararg_lastrank = vararg_rank + rawargs.length-1-mpropdef.msignature.arity
-                       var vararg = new Array[Instance]
-                       for i in [vararg_rank..vararg_lastrank] do
-                               vararg.add(rawargs[i+1])
-                       end
-                       # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis
-                       var elttype = mpropdef.msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, args.first.mtype.as(MClassType))
-                       args.add(self.array_instance(vararg, elttype))
+               var vararg_rank = msignature.vararg_rank
+               var vararg_len = args.length - msignature.arity
+               if vararg_len < 0 then vararg_len = 0
 
-                       for i in [vararg_lastrank+1..rawargs.length-1[ do
-                               args.add(rawargs[i+1])
+               for i in [0..msignature.arity[ do
+                       if i == vararg_rank then
+                               var ne = args[i]
+                               if ne isa AVarargExpr then
+                                       var e = self.expr(ne.n_expr)
+                                       if e == null then return null
+                                       res.add(e)
+                                       continue
+                               end
+                               var vararg = new Array[Instance]
+                               for j in [vararg_rank..vararg_rank+vararg_len] do
+                                       var e = self.expr(args[j])
+                                       if e == null then return null
+                                       vararg.add(e)
+                               end
+                               var elttype = msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
+                               res.add(self.array_instance(vararg, elttype))
+                       else
+                               var j = i
+                               if i > vararg_rank then j += vararg_len
+                               var e = self.expr(args[j])
+                               if e == null then return null
+                               res.add(e)
                        end
                end
-               return args
+               return res
        end
 
        # Execute `mpropdef` for a `args` (where `args[0]` is the receiver).
-       # Return a falue if `mpropdef` is a function, or null if it is a procedure.
-       # The call is direct/static. There is no message-seding/late-binding.
+       # Return a value if `mpropdef` is a function, or null if it is a procedure.
+       # The call is direct/static. There is no message-sending/late-binding.
        fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
        do
-               args = call_commons(mpropdef, args)
-               return call_without_varargs(mpropdef, args)
-       end
-
-       # Common code to call and this function
-       #
-       # Call only executes the variadic part, this avoids
-       # double encapsulation of variadic parameters into an Array
-       fun call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
-       do
                if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then
                        self.discover_call_trace.add mpropdef
                        self.debug("Discovered {mpropdef}")
@@ -365,22 +422,35 @@ class NaiveInterpreter
                assert args.length == mpropdef.msignature.arity + 1 else debug("Invalid arity for {mpropdef}. {args.length} arguments given.")
 
                # Look for the AST node that implements the property
-               var mproperty = mpropdef.mproperty
-               if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then
-                       var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef]
-                       self.parameter_check(npropdef, mpropdef, args)
-                       return npropdef.call(self, mpropdef, args)
-               else if mproperty.name == "init" then
-                       var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef]
-                       self.parameter_check(nclassdef, mpropdef, args)
-                       return nclassdef.call(self, mpropdef, args)
+               var val = mpropdef.constant_value
+
+               var node = modelbuilder.mpropdef2node(mpropdef)
+               if mpropdef.is_abstract then
+                       if node != null then
+                               self.frames.unshift new_frame(node, mpropdef, args)
+                       end
+                       fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`")
+                       abort
+               end
+
+               if node isa APropdef then
+                       self.parameter_check(node, mpropdef, args)
+                       return node.call(self, mpropdef, args)
+               else if node isa AClassdef then
+                       self.parameter_check(node, mpropdef, args)
+                       return node.call(self, mpropdef, args)
+               else if node != null then
+                       fatal("Fatal Error: method {mpropdef} associated to unexpected AST node {node.location}")
+                       abort
+               else if val != null then
+                       return value_instance(val)
                else
                        fatal("Fatal Error: method {mpropdef} not found in the AST")
                        abort
                end
        end
 
-       # Generate type checks in the C code to check covariant parameters
+       # Execute type checks of covariant parameters
        fun parameter_check(node: ANode, mpropdef: MMethodDef, args: Array[Instance])
        do
                var msignature = mpropdef.msignature
@@ -392,6 +462,8 @@ class NaiveInterpreter
                        var origmtype =  mpropdef.mproperty.intro.msignature.mparameters[i].mtype
                        if not origmtype.need_anchor then continue
 
+                       #print "{mpropdef}: {mpropdef.mproperty.intro.msignature.mparameters[i]}"
+
                        # get the parameter type
                        var mtype = msignature.mparameters[i].mtype
                        var anchor = args.first.mtype.as(MClassType)
@@ -406,7 +478,7 @@ class NaiveInterpreter
        fun send_commons(mproperty: MMethod, args: Array[Instance], mtype: MType): nullable Instance
        do
                if mtype isa MNullType then
-                       if mproperty.name == "==" then
+                       if mproperty.name == "==" or mproperty.name == "is_same_instance" then
                                return self.bool_instance(args[0] == args[1])
                        else if mproperty.name == "!=" then
                                return self.bool_instance(args[0] != args[1])
@@ -418,7 +490,7 @@ class NaiveInterpreter
        end
 
        # Execute a full `callsite` for given `args`
-       # Use this method, instead of `send` to execute and control the aditionnal behavior of the call-sites
+       # Use this method, instead of `send` to execute and control the additional behavior of the call-sites
        fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance
        do
                var initializers = callsite.mpropdef.initializers
@@ -435,7 +507,7 @@ class NaiveInterpreter
                                        self.send(p, args)
                                else if p isa MAttribute then
                                        assert recv isa MutableInstance
-                                       recv.attributes[p] = arguments[i]
+                                       write_attribute(p, recv, arguments[i])
                                        i += 1
                                else abort
                        end
@@ -447,8 +519,8 @@ class NaiveInterpreter
        end
 
        # Execute `mproperty` for a `args` (where `args[0]` is the receiver).
-       # Return a falue if `mproperty` is a function, or null if it is a procedure.
-       # The call is polimotphic. There is a message-seding/late-bindng according to te receiver (args[0]).
+       # Return a value if `mproperty` is a function, or null if it is a procedure.
+       # The call is polymorphic. There is a message-sending/late-binding according to the receiver (args[0]).
        fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance
        do
                var recv = args.first
@@ -495,19 +567,14 @@ class NaiveInterpreter
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
-                       var n = self.modelbuilder.mclassdef2nclassdef[cd]
-                       for npropdef in n.n_propdefs do
-                               if npropdef isa AAttrPropdef then
-                                       res.add(npropdef)
-                               end
-                       end
+                       res.add_all(modelbuilder.collect_attr_propdef(cd))
                end
 
                cache[mtype] = res
                return res
        end
 
-       var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
+       private var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
 
        # Fill the initial values of the newly created instance `recv`.
        # `recv.mtype` is used to know what must be filled.
@@ -518,10 +585,13 @@ class NaiveInterpreter
                end
        end
 
-       # This function determine the correct type according the reciever of the current definition (self).
+       # A hook to initialize a `PrimitiveInstance`
+       fun init_instance_primitive(recv: Instance) do end
+
+       # This function determines the correct type according to the receiver of the current propdef (self).
        fun unanchor_type(mtype: MType): MType
        do
-               return mtype.anchor_to(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType))
+               return mtype.anchor_to(self.mainmodule, current_receiver_class)
        end
 
        # Placebo instance used to mark internal error result when `null` already have a meaning.
@@ -556,7 +626,7 @@ abstract class Instance
 
        # The real value encapsulated if the instance is primitive.
        # Else aborts.
-       fun val: Object do abort
+       fun val: nullable Object do abort
 end
 
 # A instance with attribute (standards objects)
@@ -569,18 +639,12 @@ end
 
 # Special instance to handle primitives values (int, bool, etc.)
 # The trick it just to encapsulate the <<real>> value
-class PrimitiveInstance[E: Object]
+class PrimitiveInstance[E]
        super Instance
 
        # The real value encapsulated
        redef var val: E
 
-       init(mtype: MType, val: E)
-       do
-               super(mtype)
-               self.val = val
-       end
-
        redef fun is_true
        do
                if val == true then return true
@@ -590,17 +654,17 @@ class PrimitiveInstance[E: Object]
 
        redef fun ==(o)
        do
-               if not o isa PrimitiveInstance[Object] then return false
+               if not o isa PrimitiveInstance[nullable Object] then return false
                return self.val == o.val
        end
 
        redef fun eq_is(o)
        do
-               if not o isa PrimitiveInstance[Object] then return false
+               if not o isa PrimitiveInstance[nullable Object] then return false
                return self.val.is_same_instance(o.val)
        end
 
-       redef fun to_s do return "{mtype}#{val.object_id}({val})"
+       redef fun to_s do return "{mtype}#{val.object_id}({val or else "null"})"
 
        redef fun to_i do return val.as(Int)
 
@@ -608,7 +672,7 @@ class PrimitiveInstance[E: Object]
 end
 
 # Information about local variables in a running method
-class Frame
+abstract class Frame
        # The current visited node
        # The node is stored by frame to keep a stack trace
        var current_node: ANode
@@ -617,6 +681,14 @@ class Frame
        var mpropdef: MPropDef
        # Arguments of the method (the first is the receiver)
        var arguments: Array[Instance]
+       # Indicate if the expression has an array comprehension form
+       var comprehension: nullable Array[Instance] = null
+end
+
+# Implementation of a Frame with a Hashmap to store local variables
+class InterpreterFrame
+       super Frame
+
        # Mapping between a variable and the current value
        private var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
 end
@@ -651,7 +723,7 @@ redef class AMethPropdef
 
        redef fun call(v, mpropdef, args)
        do
-               var f = new Frame(self, self.mpropdef.as(not null), args)
+               var f = v.new_frame(self, mpropdef, args)
                var res = call_commons(v, mpropdef, args, f)
                v.frames.shift
                if v.returnmark == f then
@@ -673,11 +745,6 @@ redef class AMethPropdef
                        v.write_variable(variable, arguments[i+1])
                end
 
-               if mpropdef.is_abstract then
-                       v.fatal("Abstract method `{mpropdef.mproperty.name}` called on `{arguments.first.mtype}`")
-                       abort
-               end
-
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
@@ -694,7 +761,7 @@ redef class AMethPropdef
                if auto_super_call then
                        # standard call-next-method
                        var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype)
-                       v.call_without_varargs(superpd, arguments)
+                       v.call(superpd, arguments)
                end
 
                if mpropdef.is_intern or mpropdef.is_extern then
@@ -743,17 +810,23 @@ redef class AMethPropdef
                        var txt = recv.mtype.to_s
                        return v.native_string_instance(txt)
                else if pname == "==" then
-                       # == is correclt redefined for instances
+                       # == is correctly redefined for instances
                        return v.bool_instance(args[0] == args[1])
                else if pname == "!=" then
                        return v.bool_instance(args[0] != args[1])
                else if pname == "is_same_type" then
                        return v.bool_instance(args[0].mtype == args[1].mtype)
                else if pname == "is_same_instance" then
-                       return v.bool_instance(args[1] != null and args[0].eq_is(args[1]))
+                       return v.bool_instance(args[0].eq_is(args[1]))
                else if pname == "exit" then
                        exit(args[1].to_i)
                        abort
+               else if pname == "buffer_mode_full" then
+                       return v.int_instance(sys.buffer_mode_full)
+               else if pname == "buffer_mode_line" then
+                       return v.int_instance(sys.buffer_mode_line)
+               else if pname == "buffer_mode_none" then
+                       return v.int_instance(sys.buffer_mode_none)
                else if pname == "sys" then
                        return v.mainobj
                else if cname == "Int" then
@@ -799,8 +872,15 @@ redef class AMethPropdef
                                return v.int_instance(args[0].to_i.bin_xor(args[1].to_i))
                        else if pname == "bin_not" then
                                return v.int_instance(args[0].to_i.bin_not)
+                       else if pname == "int_to_s_len" then
+                               return v.int_instance(recvval.to_s.length)
                        else if pname == "native_int_to_s" then
-                               return v.native_string_instance(recvval.to_s)
+                               var s = recvval.to_s
+                               var srecv = args[1].val.as(Buffer)
+                               srecv.clear
+                               srecv.append(s)
+                               srecv.add('\0')
+                               return null
                        else if pname == "strerror_ext" then
                                return v.native_string_instance(recvval.strerror)
                        end
@@ -875,9 +955,11 @@ redef class AMethPropdef
                                return v.bool_instance(args[0].to_f.is_nan)
                        else if pname == "is_inf_extern" then
                                return v.bool_instance(args[0].to_f.is_inf != 0)
+                       else if pname == "round" then
+                               return v.float_instance(args[0].to_f.round)
                        end
                else if cname == "NativeString" then
-                       if pname == "init" then
+                       if pname == "new" then
                                return v.native_string_instance("!" * args[1].to_i)
                        end
                        var recvval = args.first.val.as(Buffer)
@@ -903,13 +985,13 @@ redef class AMethPropdef
                                if fromval < 0 then
                                        debug("Illegal access on {recvval} for element {fromval}/{recvval.length}")
                                end
-                               if fromval + lenval >= recvval.length then
+                               if fromval + lenval > recvval.length then
                                        debug("Illegal access on {recvval} for element {fromval}+{lenval}/{recvval.length}")
                                end
                                if toval < 0 then
                                        debug("Illegal access on {destval} for element {toval}/{destval.length}")
                                end
-                               if toval + lenval >= destval.length then
+                               if toval + lenval > destval.length then
                                        debug("Illegal access on {destval} for element {toval}+{lenval}/{destval.length}")
                                end
                                recvval.as(FlatBuffer).copy(fromval, lenval, destval, toval)
@@ -934,13 +1016,26 @@ redef class AMethPropdef
                                return v.int_instance(res)
                        else if pname == "atof" then
                                return v.float_instance(recvval.to_f)
+                       else if pname == "fast_cstring" then
+                               var ns = recvval.to_cstring.to_s.substring_from(args[1].to_i)
+                               return v.native_string_instance(ns)
+                       end
+               else if cname == "String" then
+                       var cs = v.send(v.force_get_primitive_method("to_cstring", args.first.mtype), [args.first])
+                       var str = cs.val.to_s
+                       if pname == "files" then
+                               var res = new Array[Instance]
+                               for f in str.files do res.add v.string_instance(f)
+                               return v.array_instance(res, v.mainmodule.string_type)
                        end
                else if pname == "calloc_string" then
                        return v.native_string_instance("!" * args[1].to_i)
                else if cname == "NativeArray" then
-                       if pname == "init" then
+                       if pname == "new" then
                                var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
-                               return new PrimitiveInstance[Array[Instance]](args[0].mtype, val)
+                               var instance = new PrimitiveInstance[Array[Instance]](args[0].mtype, val)
+                               v.init_instance_primitive(instance)
+                               return instance
                        end
                        var recvval = args.first.val.as(Array[Instance])
                        if pname == "[]" then
@@ -954,46 +1049,57 @@ redef class AMethPropdef
                        else if pname == "length" then
                                return v.int_instance(recvval.length)
                        else if pname == "copy_to" then
-                               recvval.copy(0, args[2].to_i, args[1].val.as(Array[Instance]), 0)
+                               recvval.copy_to(0, args[2].to_i, args[1].val.as(Array[Instance]), 0)
                                return null
                        end
                else if cname == "NativeFile" then
                        if pname == "native_stdout" then
-                               return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdout)
+                               var inst = new PrimitiveNativeFile.native_stdout
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "native_stdin" then
-                               return new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdin)
+                               var inst = new PrimitiveNativeFile.native_stdin
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "native_stderr" then
-                               return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stderr)
+                               var inst = new PrimitiveNativeFile.native_stderr
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "io_open_read" then
                                var a1 = args[1].val.as(Buffer)
-                               return new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, new IFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_read(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        else if pname == "io_open_write" then
                                var a1 = args[1].val.as(Buffer)
-                               return new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, new OFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_write(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
+                               v.init_instance_primitive(instance)
+                               return instance
                        end
                        var recvval = args.first.val
                        if pname == "io_write" then
                                var a1 = args[1].val.as(Buffer)
-                               recvval.as(OStream).write(a1.substring(0, args[2].to_i).to_s)
-                               return args[2]
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_write(a1.to_cstring, args[2].to_i))
                        else if pname == "io_read" then
-                               var str = recvval.as(IStream).read(args[2].to_i)
                                var a1 = args[1].val.as(Buffer)
-                               new FlatBuffer.from(str).copy(0, str.length, a1.as(FlatBuffer), 0)
-                               return v.int_instance(str.length)
+                               var ns = new NativeString(a1.length)
+                               var len = recvval.as(PrimitiveNativeFile).io_read(ns, args[2].to_i)
+                               a1.clear
+                               a1.append(ns.to_s_with_length(len))
+                               return v.int_instance(len)
+                       else if pname == "flush" then
+                               recvval.as(PrimitiveNativeFile).flush
+                               return null
                        else if pname == "io_close" then
-                               recvval.as(IOS).close
-                               return v.int_instance(0)
-                       else if pname == "address_is_null" then
-                               return v.false_instance
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_close)
+                       else if pname == "set_buffering_type" then
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).set_buffering_type(args[1].to_i, args[2].to_i))
                        end
-               else if pname == "calloc_array" then
-                       var recvtype = args.first.mtype.as(MClassType)
-                       var mtype: MType
-                       mtype = recvtype.supertype_to(v.mainmodule, recvtype, v.mainmodule.get_primitive_class("ArrayCapable"))
-                       mtype = mtype.arguments.first
-                       var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
-                       return new PrimitiveInstance[Array[Instance]](v.mainmodule.get_primitive_class("NativeArray").get_mtype([mtype]), val)
                else if pname == "native_argc" then
                        return v.int_instance(v.arguments.length)
                else if pname == "native_argv" then
@@ -1006,6 +1112,9 @@ redef class AMethPropdef
                        return v.native_string_instance(txt)
                else if pname == "get_time" then
                        return v.int_instance(get_time)
+               else if pname == "srand" then
+                       srand
+                       return null
                else if pname == "srand_from" then
                        srand_from(args[1].to_i)
                        return null
@@ -1026,19 +1135,16 @@ redef class AMethPropdef
                else if pname == "errno" then
                        return v.int_instance(sys.errno)
                else if pname == "address_is_null" then
+                       var recv = args[0]
+                       if recv isa PrimitiveInstance[PrimitiveNativeFile] then
+                               return v.bool_instance(recv.val.address_is_null)
+                       end
                        return v.false_instance
                end
                return v.error_instance
        end
 end
 
-redef class AbstractArray[E]
-       fun copy(start: Int, len: Int, dest: AbstractArray[E], new_start: Int)
-       do
-               self.copy_to(start, len, dest, new_start)
-       end
-end
-
 redef class AAttrPropdef
        redef fun call(v, mpropdef, args)
        do
@@ -1048,7 +1154,8 @@ redef class AAttrPropdef
                if mpropdef == mreadpropdef then
                        assert args.length == 1
                        if not is_lazy or v.isset_attribute(attr, recv) then return v.read_attribute(attr, recv)
-                       return evaluate_expr(v, recv)
+                       var f = v.new_frame(self, mpropdef, args)
+                       return evaluate_expr(v, recv, f)
                else if mpropdef == mwritepropdef then
                        assert args.length == 2
                        v.write_attribute(attr, recv, args[1])
@@ -1062,27 +1169,42 @@ redef class AAttrPropdef
        private fun init_expr(v: NaiveInterpreter, recv: Instance)
        do
                if is_lazy then return
-               var nexpr = self.n_expr
-               if nexpr != null then
-                       evaluate_expr(v, recv)
+               if has_value then
+                       var f = v.new_frame(self, mpropdef.as(not null), [recv])
+                       evaluate_expr(v, recv, f)
                        return
                end
-               var mtype = self.mpropdef.static_mtype.as(not null)
+               var mpropdef = self.mpropdef
+               if mpropdef == null then return
+               var mtype = mpropdef.static_mtype.as(not null)
                mtype = mtype.anchor_to(v.mainmodule, recv.mtype.as(MClassType))
                if mtype isa MNullableType then
                        v.write_attribute(self.mpropdef.mproperty, recv, v.null_instance)
                end
        end
 
-       private fun evaluate_expr(v: NaiveInterpreter, recv: Instance): Instance
+       private fun evaluate_expr(v: NaiveInterpreter, recv: Instance, f: Frame): Instance
        do
                assert recv isa MutableInstance
-               var nexpr = self.n_expr
-               assert nexpr != null
-               var f = new Frame(self, self.mpropdef.as(not null), [recv])
                v.frames.unshift(f)
-               var val = v.expr(nexpr)
+
+               var val
+
+               var nexpr = self.n_expr
+               var nblock = self.n_block
+               if nexpr != null then
+                       val = v.expr(nexpr)
+               else if nblock != null then
+                       v.stmt(nblock)
+                       assert v.returnmark == f
+                       val = v.escapevalue
+                       v.returnmark = null
+                       v.escapevalue = null
+               else
+                       abort
+               end
                assert val != null
+
                v.frames.shift
                assert not v.is_escaping
                v.write_attribute(self.mpropdef.mproperty, recv, val)
@@ -1099,7 +1221,7 @@ redef class AClassdef
                        if not mpropdef.is_intro then
                                # standard call-next-method
                                var superpd = mpropdef.lookup_next_definition(v.mainmodule, args.first.mtype)
-                               v.call_without_varargs(superpd, args)
+                               v.call(superpd, args)
                        end
                        return null
                else
@@ -1113,7 +1235,7 @@ redef class AExpr
        # Return a possible value
        # NOTE: Do not call this method directly, but use `v.expr`
        # This method is here to be implemented by subclasses.
-       private fun expr(v: NaiveInterpreter): nullable Instance
+       protected fun expr(v: NaiveInterpreter): nullable Instance
        do
                fatal(v, "NOT YET IMPLEMENTED expr {class_name}")
                abort
@@ -1122,7 +1244,7 @@ redef class AExpr
        # Evaluate the node as a statement.
        # NOTE: Do not call this method directly, but use `v.stmt`
        # This method is here to be implemented by subclasses (no need to return something).
-       private fun stmt(v: NaiveInterpreter)
+       protected fun stmt(v: NaiveInterpreter)
        do
                expr(v)
        end
@@ -1151,14 +1273,16 @@ redef class ABlockExpr
 end
 
 redef class AVardeclExpr
-       redef fun stmt(v)
+       redef fun expr(v)
        do
                var ne = self.n_expr
                if ne != null then
                        var i = v.expr(ne)
-                       if i == null then return
+                       if i == null then return null
                        v.write_variable(self.variable.as(not null), i)
+                       return i
                end
+               return null
        end
 end
 
@@ -1199,7 +1323,7 @@ redef class ASelfExpr
        end
 end
 
-redef class AContinueExpr
+redef class AEscapeExpr
        redef fun stmt(v)
        do
                var ne = self.n_expr
@@ -1208,20 +1332,7 @@ redef class AContinueExpr
                        if i == null then return
                        v.escapevalue = i
                end
-               v.continuemark = self.escapemark
-       end
-end
-
-redef class ABreakExpr
-       redef fun stmt(v)
-       do
-               var ne = self.n_expr
-               if ne != null then
-                       var i = v.expr(ne)
-                       if i == null then return
-                       v.escapevalue = i
-               end
-               v.breakmark = self.escapemark
+               v.escapemark = self.escapemark
        end
 end
 
@@ -1287,7 +1398,7 @@ redef class ADoExpr
        redef fun stmt(v)
        do
                v.stmt(self.n_block)
-               v.is_break(self.escapemark) # Clear the break (if any)
+               v.is_escape(self.break_mark) # Clear the break (if any)
        end
 end
 
@@ -1299,8 +1410,8 @@ redef class AWhileExpr
                        if cond == null then return
                        if not cond.is_true then return
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@ -1311,8 +1422,8 @@ redef class ALoopExpr
        do
                loop
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@ -1330,7 +1441,7 @@ redef class AForExpr
                #self.debug("iter {iter}")
                loop
                        var isok = v.callsite(method_is_ok, [iter]).as(not null)
-                       if not isok.is_true then return
+                       if not isok.is_true then break
                        if self.variables.length == 1 then
                                var item = v.callsite(method_item, [iter]).as(not null)
                                #self.debug("item {item}")
@@ -1344,11 +1455,28 @@ redef class AForExpr
                                abort
                        end
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
-                       if v.is_escaping then return
+                       if v.is_escape(self.break_mark) then break
+                       v.is_escape(self.continue_mark) # Clear the break
+                       if v.is_escaping then break
                        v.callsite(method_next, [iter])
                end
+               var method_finish = self.method_finish
+               if method_finish != null then
+                       v.callsite(method_finish, [iter])
+               end
+       end
+end
+
+redef class AWithExpr
+       redef fun stmt(v)
+       do
+               var expr = v.expr(self.n_expr)
+               if expr == null then return
+
+               v.callsite(method_start, [expr])
+               v.stmt(self.n_block)
+               v.is_escape(self.break_mark) # Clear the break
+               v.callsite(method_finish, [expr])
        end
 end
 
@@ -1445,11 +1573,18 @@ redef class AArrayExpr
        redef fun expr(v)
        do
                var val = new Array[Instance]
-               for nexpr in self.n_exprs.n_exprs do
-                       var i = v.expr(nexpr)
-                       if i == null then return null
-                       val.add(i)
+               var old_comprehension = v.frame.comprehension
+               v.frame.comprehension = val
+               for nexpr in self.n_exprs do
+                       if nexpr isa AForExpr then
+                               v.stmt(nexpr)
+                       else
+                               var i = v.expr(nexpr)
+                               if i == null then return null
+                               val.add(i)
+                       end
                end
+               v.frame.comprehension = old_comprehension
                var mtype = v.unanchor_type(self.mtype.as(not null)).as(MClassType)
                var elttype = mtype.arguments.first
                return v.array_instance(val, elttype)
@@ -1460,9 +1595,7 @@ redef class AStringFormExpr
        redef fun expr(v)
        do
                var txt = self.value.as(not null)
-               var nat = v.native_string_instance(txt)
-               var res = v.send(v.force_get_primitive_method("to_s", nat.mtype), [nat]).as(not null)
-               return res
+               return v.string_instance(txt)
        end
 end
 
@@ -1475,7 +1608,7 @@ redef class ASuperstringExpr
                        if i == null then return null
                        array.add(i)
                end
-               var i = v.array_instance(array, v.mainmodule.get_primitive_class("Object").mclass_type)
+               var i = v.array_instance(array, v.mainmodule.object_type)
                var res = v.send(v.force_get_primitive_method("to_s", i.mtype), [i])
                assert res != null
                return res
@@ -1562,7 +1695,6 @@ redef class AAsNotnullExpr
        do
                var i = v.expr(self.n_expr)
                if i == null then return null
-               var mtype = v.unanchor_type(self.mtype.as(not null))
                if i.mtype isa MNullType then
                        fatal(v, "Cast failed")
                end
@@ -1596,12 +1728,8 @@ redef class ASendExpr
        do
                var recv = v.expr(self.n_expr)
                if recv == null then return null
-               var args = [recv]
-               for a in self.raw_arguments do
-                       var i = v.expr(a)
-                       if i == null then return null
-                       args.add(i)
-               end
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               if args == null then return null
 
                var res = v.callsite(callsite, args)
                return res
@@ -1613,12 +1741,8 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr)
                if recv == null then return
-               var args = [recv]
-               for a in self.raw_arguments do
-                       var i = v.expr(a)
-                       if i == null then return
-                       args.add(i)
-               end
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               if args == null then return
                var value = v.expr(self.n_value)
                if value == null then return
 
@@ -1638,16 +1762,12 @@ redef class ASuperExpr
        redef fun expr(v)
        do
                var recv = v.frame.arguments.first
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       var i = v.expr(a)
-                       if i == null then return null
-                       args.add(i)
-               end
 
                var callsite = self.callsite
                if callsite != null then
-                       # Add additionnals arguments for the super init call
+                       var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+                       if args == null then return null
+                       # Add additional arguments for the super init call
                        if args.length == 1 then
                                for i in [0..callsite.msignature.arity[ do
                                        args.add(v.frame.arguments[i+1])
@@ -1658,14 +1778,17 @@ redef class ASuperExpr
                        return res
                end
 
+               # standard call-next-method
+               var mpropdef = self.mpropdef
+               mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
+
+               var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
+               if args == null then return null
+
                if args.length == 1 then
                        args = v.frame.arguments
                end
-
-               # stantard call-next-method
-               var mpropdef = self.mpropdef
-               mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
-               var res = v.call_without_varargs(mpropdef, args)
+               var res = v.call(mpropdef, args)
                return res
        end
 end
@@ -1673,15 +1796,11 @@ end
 redef class ANewExpr
        redef fun expr(v)
        do
-               var mtype = v.unanchor_type(self.mtype.as(not null))
+               var mtype = v.unanchor_type(self.recvtype.as(not null))
                var recv: Instance = new MutableInstance(mtype)
                v.init_instance(recv)
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       var i = v.expr(a)
-                       if i == null then return null
-                       args.add(i)
-               end
+               var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+               if args == null then return null
                var res2 = v.callsite(callsite, args)
                if res2 != null then
                        #self.debug("got {res2} from {mproperty}. drop {recv}")