Merge: new `with` statement
[nit.git] / src / interpreter / naive_interpreter.nit
index 47b02cd..272219f 100644 (file)
@@ -115,7 +115,7 @@ class NaiveInterpreter
 
        # Is a return executed?
        # Set this mark to skip the evaluation until the end of the specified method frame
-       var returnmark: nullable Frame = null
+       var returnmark: nullable FRAME = null
 
        # Is a break or a continue executed?
        # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
@@ -201,8 +201,8 @@ class NaiveInterpreter
        # Return the integer instance associated with `val`.
        fun int_instance(val: Int): Instance
        do
-               var ic = get_primitive_class("Int")
-               var instance = 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
@@ -210,8 +210,8 @@ class NaiveInterpreter
        # Return the char instance associated with `val`.
        fun char_instance(val: Char): Instance
        do
-               var ic = get_primitive_class("Char")
-               var instance = 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
@@ -219,8 +219,8 @@ class NaiveInterpreter
        # Return the float instance associated with `val`.
        fun float_instance(val: Float): Instance
        do
-               var ic = get_primitive_class("Float")
-               var instance = 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
@@ -239,9 +239,9 @@ class NaiveInterpreter
        fun array_instance(values: Array[Instance], elttype: MType): Instance
        do
                assert not elttype.need_anchor
-               var nat = new PrimitiveInstance[Array[Instance]](get_primitive_class("NativeArray").get_mtype([elttype]), values)
+               var nat = new PrimitiveInstance[Array[Instance]](mainmodule.native_array_type(elttype), values)
                init_instance_primitive(nat)
-               var mtype = get_primitive_class("Array").get_mtype([elttype])
+               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)])
@@ -268,8 +268,8 @@ class NaiveInterpreter
        do
                var val = new FlatBuffer.from(txt)
                val.add('\0')
-               var ic = get_primitive_class("NativeString")
-               var instance = 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
@@ -283,11 +283,14 @@ class NaiveInterpreter
                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 = new List[Frame]
+       var frames = new List[FRAME]
 
        # Return a stack trace. One line per function
        fun stack_trace: String
@@ -314,6 +317,15 @@ class NaiveInterpreter
                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
@@ -340,14 +352,14 @@ class NaiveInterpreter
        # 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
 
@@ -415,7 +427,7 @@ class NaiveInterpreter
                var node = modelbuilder.mpropdef2node(mpropdef)
                if mpropdef.is_abstract then
                        if node != null then
-                               self.frames.unshift new Frame(node, mpropdef, args)
+                               self.frames.unshift new_frame(node, mpropdef, args)
                        end
                        fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`")
                        abort
@@ -576,13 +588,6 @@ class NaiveInterpreter
        # A hook to initialize a `PrimitiveInstance`
        fun init_instance_primitive(recv: Instance) do end
 
-       # Return the primitive `MClass` corresponding to the `name` given in parameter
-       # `name` : name of the primitive class
-       fun get_primitive_class(name: String): MClass
-       do
-               return mainmodule.get_primitive_class(name)
-       end
-
        # This function determines the correct type according to the receiver of the current propdef (self).
        fun unanchor_type(mtype: MType): MType
        do
@@ -667,7 +672,7 @@ class PrimitiveInstance[E]
 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
@@ -676,9 +681,16 @@ 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]
-       var comprehension: nullable Array[Instance] = null
 end
 
 redef class ANode
@@ -711,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
@@ -1004,6 +1016,9 @@ 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])
@@ -1011,7 +1026,7 @@ redef class AMethPropdef
                        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.get_primitive_class("String").mclass_type)
+                               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)
@@ -1085,15 +1100,6 @@ redef class AMethPropdef
                        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.get_primitive_class("ArrayCapable"))
-                       mtype = mtype.arguments.first
-                       var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
-                       var instance = new PrimitiveInstance[Array[Instance]](v.get_primitive_class("NativeArray").get_mtype([mtype]), val)
-                       v.init_instance_primitive(instance)
-                       return instance
                else if pname == "native_argc" then
                        return v.int_instance(v.arguments.length)
                else if pname == "native_argv" then
@@ -1148,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])
@@ -1163,20 +1170,22 @@ redef class AAttrPropdef
        do
                if is_lazy then return
                if has_value then
-                       evaluate_expr(v, recv)
+                       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 f = new Frame(self, self.mpropdef.as(not null), [recv])
                v.frames.unshift(f)
 
                var val
@@ -1264,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
 
@@ -1456,6 +1467,19 @@ redef class AForExpr
        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
+
 redef class AAssertExpr
        redef fun stmt(v)
        do
@@ -1584,7 +1608,7 @@ redef class ASuperstringExpr
                        if i == null then return null
                        array.add(i)
                end
-               var i = v.array_instance(array, v.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