niti: Introduce a method to create and initialize a Frame
[nit.git] / src / interpreter / naive_interpreter.nit
index 5d24e6c..c92ba90 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`
@@ -173,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`)
@@ -277,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
@@ -308,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 Frame(node, mpropdef, args)
+       end
+
        # Exit the program with a message
        fun fatal(message: String)
        do
@@ -407,6 +425,14 @@ class NaiveInterpreter
                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)
@@ -452,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])
@@ -664,6 +690,7 @@ class Frame
        var arguments: Array[Instance]
        # 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
@@ -696,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
@@ -718,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
@@ -850,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
@@ -956,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)
@@ -1131,7 +1160,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])
@@ -1146,20 +1176,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
@@ -1209,7 +1241,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
@@ -1218,7 +1250,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
@@ -1532,11 +1564,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)