- # Note: class and property model element are not analysed.
- fun parse(modules: Sequence[String]): Array[MModule]
- do
- var time0 = get_time
- # Parse and recursively load
- self.toolcontext.info("*** PARSE ***", 1)
- var mmodules = new ArraySet[MModule]
- for a in modules do
- var nmodule = self.load_module(a)
- if nmodule == null then continue # Skip error
- mmodules.add(nmodule.mmodule.as(not null))
- end
- var time1 = get_time
- self.toolcontext.info("*** END PARSE: {time1-time0} ***", 2)
-
- self.toolcontext.check_errors
-
- if toolcontext.opt_only_parse.value then
- self.toolcontext.info("*** ONLY PARSE...", 1)
- exit(0)
- end
-
- return mmodules.to_a
- end
-
- # Return a class named `name` visible by the module `mmodule`.
- # Visibility in modules is correctly handled.
- # If no such a class exists, then null is returned.
- # If more than one class exists, then an error on `anode` is displayed and null is returned.
- # FIXME: add a way to handle class name conflict
- fun try_get_mclass_by_name(anode: ANode, mmodule: MModule, name: String): nullable MClass
- do
- var classes = model.get_mclasses_by_name(name)
- if classes == null then
- return null
- end
-
- var res: nullable MClass = null
- for mclass in classes do
- if not mmodule.in_importation <= mclass.intro_mmodule then continue
- if not mmodule.is_visible(mclass.intro_mmodule, mclass.visibility) then continue
- if res == null then
- res = mclass
- else
- error(anode, "Ambigous class name '{name}'; conflict between {mclass.full_name} and {res.full_name}")
- return null
- end
- end
- return res
- end
-
- # Return a property named `name` on the type `mtype` visible in the module `mmodule`.
- # Visibility in modules is correctly handled.
- # Protected properties are returned (it is up to the caller to check and reject protected properties).
- # If no such a property exists, then null is returned.
- # If more than one property exists, then an error on `anode` is displayed and null is returned.
- # FIXME: add a way to handle property name conflict
- fun try_get_mproperty_by_name2(anode: ANode, mmodule: MModule, mtype: MType, name: String): nullable MProperty
- do
- var props = self.model.get_mproperties_by_name(name)
- if props == null then
- return null
- end
-
- var cache = self.try_get_mproperty_by_name2_cache[mmodule, mtype, name]
- if cache != null then return cache
-
- var res: nullable MProperty = null
- var ress: nullable Array[MProperty] = null
- for mprop in props do
- if not mtype.has_mproperty(mmodule, mprop) then continue
- if not mmodule.is_visible(mprop.intro_mclassdef.mmodule, mprop.visibility) then continue
- if res == null then
- res = mprop
- else
- var restype = res.intro_mclassdef.bound_mtype
- var mproptype = mprop.intro_mclassdef.bound_mtype
- if restype.is_subtype(mmodule, null, mproptype) then
- # we keep res
- else if mproptype.is_subtype(mmodule, null, restype) then
- res = mprop
- else
- if ress == null then ress = new Array[MProperty]
- ress.add(mprop)
- end
- end
- end
- if ress != null then
- var restype = res.intro_mclassdef.bound_mtype
- for mprop in ress do
- var mproptype = mprop.intro_mclassdef.bound_mtype
- if not restype.is_subtype(mmodule, null, mproptype) then
- self.error(anode, "Ambigous property name '{name}' for {mtype}; conflict between {mprop.full_name} and {res.full_name}")
- return null
- end
- end
- end
-
- self.try_get_mproperty_by_name2_cache[mmodule, mtype, name] = res
- return res
- end
-
- private var try_get_mproperty_by_name2_cache: HashMap3[MModule, MType, String, nullable MProperty] = new HashMap3[MModule, MType, String, nullable MProperty]
-
-
- # Alias for try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.mtype, name)
- fun try_get_mproperty_by_name(anode: ANode, mclassdef: MClassDef, name: String): nullable MProperty
- do
- return try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.bound_mtype, name)
- end
-
- # The list of directories to search for top level modules
- # The list is initially set with :
- # * the toolcontext --path option
- # * the NIT_PATH environment variable
- # * some heuristics including the NIT_DIR environment variable and the progname of the process
- # Path can be added (or removed) by the client
- var paths: Array[String] = new Array[String]
-
- # Get a module by its short name; if required, the module is loaded, parsed and its hierarchies computed.
- # If `mmodule` is set, then the module search starts from it up to the top level (see `paths`);
- # if `mmodule` is null then the module is searched in the top level only.
- # If no module exists or there is a name conflict, then an error on `anode` is displayed and null is returned.
- # FIXME: add a way to handle module name conflict
- fun get_mmodule_by_name(anode: ANode, mmodule: nullable MModule, name: String): nullable MModule
- do
- # First, look in groups of the module
- if mmodule != null then
- var mgroup = mmodule.mgroup
- while mgroup != null do
- var dirname = mgroup.filepath
- if dirname == null then break # virtual group
- if dirname.has_suffix(".nit") then break # singleton project
-
- # Second, try the directory to find a file
- var try_file = dirname + "/" + name + ".nit"
- if try_file.file_exists then
- var res = self.load_module(try_file.simplify_path)
- if res == null then return null # Forward error
- return res.mmodule.as(not null)
- end
-
- # Third, try if the requested module is itself a group
- try_file = dirname + "/" + name + "/" + name + ".nit"
- if try_file.file_exists then
- mgroup = get_mgroup(dirname + "/" + name)
- var res = self.load_module(try_file.simplify_path)
- if res == null then return null # Forward error
- return res.mmodule.as(not null)
- end
-
- mgroup = mgroup.parent
- end
- end
-
- # Look at some known directories
- var lookpaths = self.paths
-
- # Look in the directory of module project also (even if not explicitely in the path)
- if mmodule != null and mmodule.mgroup != null then
- # path of the root group
- var dirname = mmodule.mgroup.mproject.root.filepath
- if dirname != null then
- dirname = dirname.join_path("..").simplify_path
- if not lookpaths.has(dirname) and dirname.file_exists then
- lookpaths = lookpaths.to_a
- lookpaths.add(dirname)
- end
- end
- end
-
- var candidate = search_module_in_paths(anode.hot_location, name, lookpaths)
-
- if candidate == null then
- if mmodule != null then
- error(anode, "Error: cannot find module {name} from {mmodule}. tried {lookpaths.join(", ")}")
- else
- error(anode, "Error: cannot find module {name}. tried {lookpaths.join(", ")}")
- end
- return null
- end
- var res = self.load_module(candidate)
- if res == null then return null # Forward error
- return res.mmodule.as(not null)
- end
-
- # Search a module `name` from path `lookpaths`.
- # If found, the path of the file is returned
- private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable String
- do
- var candidate: nullable String = null
- for dirname in lookpaths do
- var try_file = (dirname + "/" + name + ".nit").simplify_path
- if try_file.file_exists then
- if candidate == null then
- candidate = try_file
- else if candidate != try_file then
- # try to disambiguate conflicting modules
- var abs_candidate = module_absolute_path(candidate)
- var abs_try_file = module_absolute_path(try_file)
- if abs_candidate != abs_try_file then
- toolcontext.error(location, "Error: conflicting module file for {name}: {candidate} {try_file}")
- end
- end
- end
- try_file = (dirname + "/" + name + "/" + name + ".nit").simplify_path
- if try_file.file_exists then
- if candidate == null then
- candidate = try_file
- else if candidate != try_file then
- # try to disambiguate conflicting modules
- var abs_candidate = module_absolute_path(candidate)
- var abs_try_file = module_absolute_path(try_file)
- if abs_candidate != abs_try_file then
- toolcontext.error(location, "Error: conflicting module file for {name}: {candidate} {try_file}")
- end
- end
- end
- end
- return candidate
- end
-
- # cache for `identify_file` by realpath
- private var identified_files = new HashMap[String, nullable ModulePath]
-
- # Identify a source file
- # Load the associated project and groups if required
- private fun identify_file(path: String): nullable ModulePath
- do
- if not path.file_exists then
- toolcontext.error(null, "Error: `{path}` does not exists")
- return null
- end
-
- # Fast track, the path is already known
- var pn = path.basename(".nit")
- var rp = module_absolute_path(path)
- if identified_files.has_key(rp) then return identified_files[rp]
-
- # Search for a group
- var mgrouppath = path.join_path("..").simplify_path
- var mgroup = get_mgroup(mgrouppath)
-
- if mgroup == null then
- # singleton project
- var mproject = new MProject(pn, model)
- mgroup = new MGroup(pn, mproject, null) # same name for the root group
- mgroup.filepath = path
- mproject.root = mgroup
- toolcontext.info("found project `{pn}` at {path}", 2)
- end
-
- var res = new ModulePath(pn, path, mgroup)
-
- identified_files[rp] = res
- return res