X-Git-Url: http://nitlanguage.org diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index 6e4a356..5cc3892 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -21,6 +21,7 @@ import literal import semantize private import parser::tables import mixin +import primitive_types redef class ToolContext # --discover-call-trace @@ -67,16 +68,17 @@ class NaiveInterpreter var arguments: Array[String] # The main Sys instance - var mainobj: nullable 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 @@ -101,13 +103,14 @@ 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? @@ -170,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`) @@ -192,46 +201,55 @@ 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 ic = get_primitive_class("Int") + var instance = new PrimitiveInstance[Int](ic.mclass_type, 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 ic = get_primitive_class("Char") + var instance = new PrimitiveInstance[Char](ic.mclass_type, 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 ic = get_primitive_class("Float") + var instance = new PrimitiveInstance[Float](ic.mclass_type, val) + init_instance_primitive(instance) + return instance end # The unique instance of the `true` value. - var true_instance: Instance + var true_instance: Instance is noinit # The unique instance of the `false` value. - var false_instance: Instance + var false_instance: Instance is noinit # The unique instance of the `null` value. - var null_instance: Instance + 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]](get_primitive_class("NativeArray").get_mtype([elttype]), values) + init_instance_primitive(nat) + var mtype = get_primitive_class("Array").get_mtype([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 @@ -250,8 +268,10 @@ class NaiveInterpreter 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 ic = get_primitive_class("NativeString") + var instance = new PrimitiveInstance[Buffer](ic.mclass_type, val) + init_instance_primitive(instance) + return instance end # Return a new String instance for `txt` @@ -281,13 +301,27 @@ 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 + # 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 @@ -295,10 +329,11 @@ 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 @@ -375,16 +410,18 @@ 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 var val = mpropdef.constant_value - 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.is_root_init then - var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef] - self.parameter_check(nclassdef, mpropdef, args) - return nclassdef.call(self, mpropdef, args) + + var node = modelbuilder.mpropdef2node(mpropdef) + 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 @@ -393,7 +430,7 @@ class NaiveInterpreter 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 @@ -405,6 +442,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) @@ -419,7 +458,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]) @@ -448,7 +487,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 @@ -508,13 +547,7 @@ class NaiveInterpreter var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do - if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue - 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 @@ -532,10 +565,20 @@ 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 + + # 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 - 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. @@ -570,7 +613,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) @@ -583,18 +626,12 @@ end # Special instance to handle primitives values (int, bool, etc.) # The trick it just to encapsulate the <> 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 @@ -604,17 +641,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) @@ -633,6 +670,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 @@ -768,6 +806,12 @@ redef class AMethPropdef 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 @@ -889,6 +933,8 @@ 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 == "new" then @@ -949,12 +995,22 @@ redef class AMethPropdef else if pname == "atof" then return v.float_instance(recvval.to_f) 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.get_primitive_class("String").mclass_type) + end else if pname == "calloc_string" then return v.native_string_instance("!" * args[1].to_i) else if cname == "NativeArray" 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 @@ -968,46 +1024,66 @@ 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 = 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) - return new PrimitiveInstance[Array[Instance]](v.mainmodule.get_primitive_class("NativeArray").get_mtype([mtype]), val) + 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 @@ -1020,6 +1096,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 @@ -1040,19 +1119,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 @@ -1076,8 +1152,7 @@ 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 + if has_value then evaluate_expr(v, recv) return end @@ -1091,12 +1166,26 @@ redef class AAttrPropdef private fun evaluate_expr(v: NaiveInterpreter, recv: Instance): 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) @@ -1450,11 +1539,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) @@ -1478,7 +1574,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.get_primitive_class("Object").mclass_type) var res = v.send(v.force_get_primitive_method("to_s", i.mtype), [i]) assert res != null return res @@ -1666,7 +1762,7 @@ 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 = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)