Merge remote-tracking branch 'origin/master' into init_auto
[nit.git] / src / interpreter / naive_interpreter.nit
index 8b0f291..7389e2e 100644 (file)
@@ -25,7 +25,7 @@ import primitive_types
 
 redef class ToolContext
        # --discover-call-trace
-       var opt_discover_call_trace = 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 methods", "--discover-call-trace")
 
        redef init
        do
@@ -113,17 +113,20 @@ class NaiveInterpreter
                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 or a continue executed?
+       # Is a return, 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 an abort being executed ?
+       # Set this mark to return to the last `catch` bloc or effectively aborting if there isn't any
+       var catch_mark = new EscapeMark
+
+       # The count of `catch` blocs that have been encountered and can catch an abort
+       var catch_count = 0
+
        # 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 escapemark != null
+       fun is_escaping: Bool do return escapemark != null
 
        # The value associated with the current return/break/continue, if any.
        # Set the value when you set a escapemark.
@@ -328,6 +331,16 @@ class NaiveInterpreter
                return instance
        end
 
+       # Return a new native string initialized with `txt`
+       fun native_string_instance_from_ns(txt: NativeString, len: Int): Instance
+       do
+               var instance = native_string_instance_len(len)
+               var val = instance.val
+               txt.copy_to(val, len, 0, 0)
+
+               return instance
+       end
+
        # Return a new native string initialized of `length`
        fun native_string_instance_len(length: Int): PrimitiveInstance[NativeString]
        do
@@ -437,7 +450,7 @@ class NaiveInterpreter
        # Return `null` if one of the evaluation of the arguments return null.
        fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
        do
-               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               var msignature = mpropdef.msignature.as(not null)
                var res = new Array[Instance]
                res.add(recv)
 
@@ -471,8 +484,8 @@ class NaiveInterpreter
                                res.add(null_instance)
                                continue
                        end
-                       if param.is_vararg and map.vararg_decl > 0 then
-                               var vararg = exprs.sub(j, map.vararg_decl)
+                       if param.is_vararg and args[i].vararg_decl > 0 then
+                               var vararg = exprs.sub(j, args[i].vararg_decl)
                                var elttype = param.mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
                                var arg = self.array_instance(vararg, elttype)
                                res.add(arg)
@@ -528,8 +541,10 @@ class NaiveInterpreter
        do
                var msignature = mpropdef.msignature.as(not null)
                for i in [0..msignature.arity[ do
+                       var mp = msignature.mparameters[i]
+
                        # skip test for vararg since the array is instantiated with the correct polymorphic type
-                       if msignature.vararg_rank == i then continue
+                       if mp.is_vararg then continue
 
                        # skip if the cast is not required
                        var origmtype =  mpropdef.mproperty.intro.msignature.mparameters[i].mtype
@@ -538,7 +553,7 @@ class NaiveInterpreter
                        #print "{mpropdef}: {mpropdef.mproperty.intro.msignature.mparameters[i]}"
 
                        # get the parameter type
-                       var mtype = msignature.mparameters[i].mtype
+                       var mtype = mp.mtype
                        var anchor = args.first.mtype.as(MClassType)
                        var amtype = mtype.anchor_to(self.mainmodule, anchor)
                        if not args[i+1].mtype.is_subtype(self.mainmodule, anchor, amtype) then
@@ -567,28 +582,6 @@ class NaiveInterpreter
        fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance
        do
                if callsite == null then return null
-               var initializers = callsite.mpropdef.initializers
-               if not initializers.is_empty then
-                       var recv = arguments.first
-                       var i = 1
-                       for p in initializers do
-                               if p isa MMethod then
-                                       var args = [recv]
-                                       for x in p.intro.msignature.mparameters do
-                                               args.add arguments[i]
-                                               i += 1
-                                       end
-                                       self.send(p, args)
-                               else if p isa MAttribute then
-                                       assert recv isa MutableInstance
-                                       write_attribute(p, recv, arguments[i])
-                                       i += 1
-                               else abort
-                       end
-                       assert i == arguments.length
-
-                       return send(callsite.mproperty, [recv])
-               end
                return send(callsite.mproperty, arguments)
        end
 
@@ -836,10 +829,8 @@ redef class AMethPropdef
                var f = v.new_frame(self, mpropdef, args)
                var res = call_commons(v, mpropdef, args, f)
                v.frames.shift
-               if v.returnmark == f then
-                       v.returnmark = null
+               if v.is_escape(self.return_mark) then
                        res = v.escapevalue
-                       v.escapevalue = null
                        return res
                end
                return res
@@ -874,21 +865,27 @@ redef class AMethPropdef
                        v.call(superpd, arguments)
                end
 
+               # First, try intern
                if mpropdef.is_intern or mpropdef.is_extern then
                        var res = intern_call(v, mpropdef, arguments)
                        if res != v.error_instance then return res
                end
-
+               # Then, try extern
+               if mpropdef.is_extern then
+                       var res = call_extern(v, mpropdef, arguments, f)
+                       if res != v.error_instance then return res
+               end
+               # Else try block
                if n_block != null then
                        v.stmt(self.n_block)
                        return null
                end
 
+               # Fail if nothing succeed
                if mpropdef.is_intern then
                        fatal(v, "NOT YET IMPLEMENTED intern {mpropdef}")
                else if mpropdef.is_extern then
-                       var res = call_extern(v, mpropdef, arguments, f)
-                       if res != v.error_instance then return res
+                       fatal(v, "NOT YET IMPLEMENTED extern {mpropdef}")
                else
                        fatal(v, "NOT YET IMPLEMENTED <wat?> {mpropdef}")
                end
@@ -898,7 +895,6 @@ redef class AMethPropdef
        # Call this extern method
        protected fun call_extern(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance], f: Frame): nullable Instance
        do
-               fatal(v, "NOT YET IMPLEMENTED extern {mpropdef}")
                return v.error_instance
        end
 
@@ -973,6 +969,10 @@ redef class AMethPropdef
                                return v.bool_instance(recvval >= args[1].to_i)
                        else if pname == "<=>" then
                                return v.int_instance(recvval <=> args[1].to_i)
+                       else if pname == "&" then
+                               return v.int_instance(recvval & args[1].to_i)
+                       else if pname == "|" then
+                               return v.int_instance(recvval | args[1].to_i)
                        else if pname == "to_f" then
                                return v.float_instance(recvval.to_f)
                        else if pname == "to_b" then
@@ -991,9 +991,6 @@ redef class AMethPropdef
                                return v.int32_instance(recvval.to_i32)
                        else if pname == "to_u32" then
                                return v.uint32_instance(recvval.to_u32)
-                       else if pname == "rand" then
-                               var res = recvval.rand
-                               return v.int_instance(res)
                        end
                else if cname == "Byte" then
                        var recvval = args[0].to_b
@@ -1021,6 +1018,10 @@ redef class AMethPropdef
                                return v.bool_instance(recvval >= args[1].to_b)
                        else if pname == "<=>" then
                                return v.int_instance(recvval <=> args[1].to_b)
+                       else if pname == "&" then
+                               return v.byte_instance(recvval & args[1].to_b)
+                       else if pname == "|" then
+                               return v.byte_instance(recvval | args[1].to_b)
                        else if pname == "to_f" then
                                return v.float_instance(recvval.to_f)
                        else if pname == "to_i" then
@@ -1115,8 +1116,6 @@ redef class AMethPropdef
                                return v.float_instance(args[0].to_f.log)
                        else if pname == "pow" then
                                return v.float_instance(args[0].to_f.pow(args[1].to_f))
-                       else if pname == "rand" then
-                               return v.float_instance(args[0].to_f.rand)
                        else if pname == "abs" then
                                return v.float_instance(args[0].to_f.abs)
                        else if pname == "hypot_with" then
@@ -1153,6 +1152,12 @@ redef class AMethPropdef
                        else if pname == "fast_cstring" then
                                var ns = recvval.fast_cstring(args[1].to_i)
                                return v.native_string_instance(ns.to_s)
+                       else if pname == "fetch_4_chars" then
+                               return v.int_instance(args[0].val.as(NativeString).fetch_4_chars(args[1].to_i))
+                       else if pname == "fetch_4_hchars" then
+                               return v.int_instance(args[0].val.as(NativeString).fetch_4_hchars(args[1].to_i))
+                       else if pname == "utf8_length" then
+                               return v.int_instance(args[0].val.as(NativeString).utf8_length(args[1].to_i, args[2].to_i))
                        end
                else if pname == "calloc_string" then
                        return v.native_string_instance_len(args[1].to_i)
@@ -1476,7 +1481,12 @@ redef class AAttrPropdef
                        return evaluate_expr(v, recv, f)
                else if mpropdef == mwritepropdef then
                        assert args.length == 2
-                       v.write_attribute(attr, recv, args[1])
+                       var arg = args[1]
+                       if is_optional and arg.mtype isa MNullType then
+                               var f = v.new_frame(self, mpropdef, args)
+                               arg = evaluate_expr(v, recv, f)
+                       end
+                       v.write_attribute(attr, recv, arg)
                        return null
                else
                        abort
@@ -1514,10 +1524,9 @@ redef class AAttrPropdef
                        val = v.expr(nexpr)
                else if nblock != null then
                        v.stmt(nblock)
-                       assert v.returnmark == f
+                       assert v.escapemark == return_mark
                        val = v.escapevalue
-                       v.returnmark = null
-                       v.escapevalue = null
+                       v.escapemark = null
                else
                        abort
                end
@@ -1532,16 +1541,41 @@ end
 
 redef class AClassdef
        # Execute an implicit `mpropdef` associated with the current node.
-       private fun call(v: NaiveInterpreter, mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
+       private fun call(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance]): nullable Instance
        do
                if mpropdef.mproperty.is_root_init then
-                       assert args.length == 1
+                       assert arguments.length == 1
                        if not mpropdef.is_intro then
                                # standard call-next-method
-                               var superpd = mpropdef.lookup_next_definition(v.mainmodule, args.first.mtype)
-                               v.call(superpd, args)
+                               var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype)
+                               v.call(superpd, arguments)
                        end
                        return null
+               else if mclassdef.auto_init == mpropdef then
+                       var recv = arguments.first
+                       var initializers = mpropdef.initializers
+                       var no_init = false
+                       if not initializers.is_empty then
+                               var i = 1
+                               for p in initializers do
+                                       if p isa MMethod then
+                                               var args = [recv]
+                                               for x in p.intro.msignature.mparameters do
+                                                       args.add arguments[i]
+                                                       i += 1
+                                               end
+                                               v.send(p, args)
+                                               if p.intro.is_calling_init then no_init = true
+                                       else if p isa MAttribute then
+                                               assert recv isa MutableInstance
+                                               v.write_attribute(p, recv, arguments[i])
+                                               i += 1
+                                       else abort
+                               end
+                               assert i == arguments.length
+                       end
+                       if not no_init then v.send(mclass.the_root_init_mmethod.as(not null), [recv])
+                       return null
                else
                        abort
                end
@@ -1662,24 +1696,17 @@ redef class AEscapeExpr
        end
 end
 
-redef class AReturnExpr
-       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.returnmark = v.frame
-       end
-end
-
 redef class AAbortExpr
        redef fun stmt(v)
        do
-               fatal(v, "Aborted")
-               exit(1)
+               # Abort as asked if there is no `catch` bloc
+               if v.catch_count <= 0 then
+                       fatal(v, "Aborted")
+                       exit(1)
+               else
+                       # Abort mode, skipping everything until a `catch` bloc is reached
+                       v.escapemark = v.catch_mark
+               end
        end
 end
 
@@ -1723,8 +1750,15 @@ end
 redef class ADoExpr
        redef fun stmt(v)
        do
+               # If this bloc has a catch, register it in the counter
+               if self.n_catch != null then v.catch_count += 1
                v.stmt(self.n_block)
                v.is_escape(self.break_mark) # Clear the break (if any)
+               if self.n_catch != null then
+                       v.catch_count -= 1
+                       # Are we in abort mode? then this catch is executing
+                       if v.is_escape(v.catch_mark) then v.stmt(self.n_catch)
+               end
        end
 end
 
@@ -1812,7 +1846,13 @@ redef class AWithExpr
                v.callsite(method_start, [expr])
                v.stmt(self.n_block)
                v.is_escape(self.break_mark) # Clear the break
+
+               # Execute the finally without an escape
+               var old_mark = v.escapemark
+               v.escapemark = null
                v.callsite(method_finish, [expr])
+               # Restore the escape unless another escape was provided
+               if v.escapemark == null then v.escapemark = old_mark
        end
 end
 
@@ -1908,6 +1948,8 @@ end
 redef class ACharExpr
        redef fun expr(v)
        do
+               if is_ascii then return v.byte_instance(self.value.as(not null).ascii)
+               if is_code_point then return v.int_instance(self.value.as(not null).code_point)
                return v.char_instance(self.value.as(not null))
        end
 end
@@ -1934,11 +1976,71 @@ redef class AArrayExpr
        end
 end
 
+redef class AugmentedStringFormExpr
+       # Factorize the making of a `Regex` object from a literal prefixed string
+       fun make_re(v: NaiveInterpreter, rs: Instance): nullable Instance do
+               var tore = to_re
+               assert tore != null
+               var res = v.callsite(tore, [rs])
+               if res == null then
+                       print "Cannot call property `to_re` on {self}"
+                       abort
+               end
+               for j in suffix.chars do
+                       if j == 'i' then
+                               var prop = ignore_case
+                               assert prop != null
+                               v.callsite(prop, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if j == 'm' then
+                               var prop = newline
+                               assert prop != null
+                               v.callsite(prop, [res, v.bool_instance(true)])
+                               continue
+                       end
+                       if j == 'b' then
+                               var prop = extended
+                               assert prop != null
+                               v.callsite(prop, [res, v.bool_instance(false)])
+                               continue
+                       end
+                       # Should not happen, this needs to be updated
+                       # along with the addition of new suffixes
+                       abort
+               end
+               return res
+       end
+end
+
 redef class AStringFormExpr
-       redef fun expr(v)
-       do
-               var txt = self.value.as(not null)
-               return v.string_instance(txt)
+       redef fun expr(v) do return v.string_instance(value)
+end
+
+redef class AStringExpr
+       redef fun expr(v) do
+               var s = v.string_instance(value)
+               if is_string then return s
+               if is_bytestring then
+                       var ns = v.native_string_instance_from_ns(bytes.items, bytes.length)
+                       var ln = v.int_instance(bytes.length)
+                       var prop = to_bytes_with_copy
+                       assert prop != null
+                       var res = v.callsite(prop, [ns, ln])
+                       if res == null then
+                               print "Cannot call property `to_bytes` on {self}"
+                               abort
+                       end
+                       s = res
+               else if is_re then
+                       var res = make_re(v, s)
+                       assert res != null
+                       s = res
+               else
+                       print "Unimplemented prefix or suffix for {self}"
+                       abort
+               end
+               return s
        end
 end
 
@@ -1954,6 +2056,7 @@ redef class ASuperstringExpr
                var i = v.array_instance(array, v.mainmodule.object_type)
                var res = v.send(v.force_get_primitive_method("plain_to_s", i.mtype), [i])
                assert res != null
+               if is_re then res = make_re(v, res)
                return res
        end
 end