Loading of Nit source files

The loader takes care of looking for module and projects in the file system, and the associated case of errors. The loading requires several steps:

Identify: create an empty model entity associated to a name or a file path. Identification is used for instance when names are given in the command line. See identify_module and identify_group.

Scan: visit directories and identify their contents. Scanning is done to enable the searching of modules in projects. See scan_group and scan_full.

Parse: load the AST and associate it with the model entity. See MModule::parse.

Import: means recursively load modules imported by a module. See build_module_importation.

Load: means doing the full sequence: identify, parse and import. See ModelBuilder::parse, ModelBuilder::parse_full, MModule::load `ModelBuilder::load_module.

Redefined classes

redef class AModule

nitc :: loader $ AModule

The main node of a Nit source-file
redef class AStdImport

nitc :: loader $ AStdImport

A standard import clause. eg import x
redef class MGroup

nitc :: loader $ MGroup

A group of modules in a package
redef class MModule

nitc :: loader $ MModule

A Nit module is usually associated with a Nit source file.
redef class MPackage

nitc :: loader $ MPackage

A Nit package, that encompass a product
redef class ModelBuilder

nitc :: loader $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
redef class SourceFile

nitc :: loader $ SourceFile

A raw text Nit source file
redef class ToolContext

nitc :: loader $ ToolContext

Global context for tools

All class definitions

redef class AModule

nitc :: loader $ AModule

The main node of a Nit source-file
redef class AStdImport

nitc :: loader $ AStdImport

A standard import clause. eg import x
redef class MGroup

nitc :: loader $ MGroup

A group of modules in a package
redef class MModule

nitc :: loader $ MModule

A Nit module is usually associated with a Nit source file.
redef class MPackage

nitc :: loader $ MPackage

A Nit package, that encompass a product
redef class ModelBuilder

nitc :: loader $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
redef class SourceFile

nitc :: loader $ SourceFile

A raw text Nit source file
redef class ToolContext

nitc :: loader $ ToolContext

Global context for tools
package_diagram nitc::loader loader nitc::modelbuilder_base modelbuilder_base nitc::loader->nitc::modelbuilder_base ini ini nitc::loader->ini nitc::nitpm_shared nitpm_shared nitc::loader->nitc::nitpm_shared nitc\>model\> model nitc::modelbuilder_base->nitc\>model\> nitc::toolcontext toolcontext nitc::modelbuilder_base->nitc::toolcontext nitc\>parser\> parser nitc::modelbuilder_base->nitc\>parser\> core core ini->core nitc::nitpm_shared->core ...nitc\>model\> ... ...nitc\>model\>->nitc\>model\> ...nitc::toolcontext ... ...nitc::toolcontext->nitc::toolcontext ...nitc\>parser\> ... ...nitc\>parser\>->nitc\>parser\> ...core ... ...core->core nitc::modelbuilder modelbuilder nitc::modelbuilder->nitc::loader nitc::modelbuilder... ... nitc::modelbuilder...->nitc::modelbuilder

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module ini

ini :: ini

Read and write INI configuration files
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager

Children

Descendants

module a_star-m

a_star-m

module abstract_compiler

nitc :: abstract_compiler

Abstract compiler
module actors_generation_phase

nitc :: actors_generation_phase

Generate a support module for each module that contain a class annotated with is actor
module actors_injection_phase

nitc :: actors_injection_phase

Injects model for the classes annotated with "is actor" so
module android

nitc :: android

Compile program for the Android platform
module android_annotations

nitc :: android_annotations

Additionnal annotations to gather metadata on Android projects
module annotation

nitc :: annotation

Management and utilities on annotations
module api

nitc :: api

Components required to build a web server about the nit model.
module api_auth

nitc :: api_auth

module api_base

nitc :: api_base

Base classes used by nitweb.
module api_docdown

nitc :: api_docdown

Nitdoc specific Markdown format handling for Nitweb
module api_feedback

nitc :: api_feedback

Feedback related features
module api_light

nitc :: api_light

Highlight and collect messages from a piece of code
module api_model

nitc :: api_model

module app_annotations

nitc :: app_annotations

Annotations to gather metadata on app.nit projects
module ast_metrics

nitc :: ast_metrics

Metrics about the nodes and identifiers in the AST
module astbuilder

nitc :: astbuilder

Instantiation and transformation of semantic nodes in the AST of expressions and statements
module auto_super_init

nitc :: auto_super_init

Computing of super-constructors that must be implicitly called at the begin of constructors.
module c

nitc :: c

Support for nesting C code within a Nit program using its FFI
module c_compiler_options

nitc :: c_compiler_options

Offers the annotations cflags and ldflags to specify
module catalog

nitc :: catalog

Basic catalog generator for Nit packages
module check_annotation

nitc :: check_annotation

Check that annotation present in the AST are either primitive or user-declared
module code_gen

nitc :: code_gen

Main frontend phases plus code generation phases
module commands_base

nitc :: commands_base

Documentation commands
module commands_catalog

nitc :: commands_catalog

Commands to retrieve Catalog related data
module commands_docdown

nitc :: commands_docdown

Doc down related queries
module commands_graph

nitc :: commands_graph

Graph commands
module commands_http

nitc :: commands_http

Initialize commands from HTTP requests
module commands_model

nitc :: commands_model

Doc commands about a Model or a MEntity
module commands_parser

nitc :: commands_parser

A parser that create DocCommand from a string
module commands_usage

nitc :: commands_usage

Commands about how mentities are used
module compilation

nitc :: compilation

The compilation module of the VirtualMachine
module compiler

nitc :: compiler

Compilation to C
module compiler_ffi

nitc :: compiler_ffi

Full FFI support for the compiler
module compiler_serialization

nitc :: compiler_serialization

Serialization support for the compiler
module contracts

nitc :: contracts

Module to build contract
module cpp

nitc :: cpp

Supports the use of the C++ language through the FFI
module deriving

nitc :: deriving

Injection of automatic method definitions for standard methods, based on the attributes of the classes
module detect_covariance

nitc :: detect_covariance

Detect the static usage of covariance in the code.
module detect_variance_constraints

nitc :: detect_variance_constraints

Collect metrics about detected variances constraints on formal types.
module div_by_zero

nitc :: div_by_zero

Detection of divisions by zero in obvious cases
module dynamic_loading_ffi

nitc :: dynamic_loading_ffi

Execute FFI code by creating and loading shared libraries
module emscripten

nitc :: emscripten

Compile to JavaScript using the Emscripten SDK
module explain_assert

nitc :: explain_assert

Explain failed assert to the console by modifying the AST.
module extern_classes

nitc :: extern_classes

Manages all extern classes and their associated foreign type.
module extra_java_files

nitc :: extra_java_files

Intro the annotation extra_java_files to compile extra java files
module ffi

nitc :: ffi

Full FFI support, independent of the compiler
module ffi_base

nitc :: ffi_base

Tools and utilities for implement FFI with different languages
module flow

nitc :: flow

Intraprocedural static flow.
module frontend

nitc :: frontend

Collect and orchestration of main frontend phases
module generate_hierarchies

nitc :: generate_hierarchies

Create dot files for various hierarchies of a model.
module global_compiler

nitc :: global_compiler

Global compilation of a Nit program
module header_dependency

nitc :: header_dependency

Tracks which modules has public header code that must be imported
module highlight

nitc :: highlight

Highlighting of Nit AST
module html_commands

nitc :: html_commands

Render commands results as HTML
module html_model

nitc :: html_model

Translate mentities to html blocks.
module htmlight

nitc :: htmlight

Highlighting of Nit AST with HTML
module i18n_phase

nitc :: i18n_phase

Basic support of internationalization through the generation of id-to-string tables
module inheritance_metrics

nitc :: inheritance_metrics

Collect metrics about inheritance usage
module interpreter

nitc :: interpreter

Interpretation of Nit programs
module ios

nitc :: ios

Compile programs for the iOS platform
module java

nitc :: java

FFI support for the Java language
module java_compiler

nitc :: java_compiler

Compile Nit code to Java code
module json_commands

nitc :: json_commands

Translate command results to json
module json_model

nitc :: json_model

Make model entities Serializable.
module light

nitc :: light

Light FFI support for the compiler
module light_c

nitc :: light_c

Support for nesting C code within a Nit program using its FFI
module light_ffi

nitc :: light_ffi

Light FFI support, independent of the compiler
module light_ffi_base

nitc :: light_ffi_base

Tools and utilities for implement FFI with different languages
module light_only

nitc :: light_only

Compiler support for the light FFI only, detects unsupported usage of callbacks
module local_var_init

nitc :: local_var_init

Verify that local variables are initialized before their usage
module mclasses_metrics

nitc :: mclasses_metrics

Collect common metrics about mclasses
module md_commands

nitc :: md_commands

Render commands results as Markdown
module memory_logger

nitc :: memory_logger

Extension to inject memory-tracing instrumentation in code generated by nitc.
module mendel_metrics

nitc :: mendel_metrics

The Mendel model helps to understand class hierarchies.
module metrics

nitc :: metrics

Various statistics about Nit models and programs
module metrics_base

nitc :: metrics_base

Helpers for various statistics tools.
module mixin

nitc :: mixin

Loading and additional module refinements at link-time.
module mmodules_metrics

nitc :: mmodules_metrics

Collect common metrics about modules
module model_collect

nitc :: model_collect

Collect things from the model.
module model_hyperdoc

nitc :: model_hyperdoc

Dump of Nit model into hypertext human-readable format.
module model_index

nitc :: model_index

Search things from the Model
module model_visitor

nitc :: model_visitor

Simple visitor framework for Nit models.
module modelize

nitc :: modelize

Create a model from nit source files
module modelize_class

nitc :: modelize_class

Analysis and verification of class definitions to instantiate model element
module modelize_property

nitc :: modelize_property

Analysis and verification of property definitions to instantiate model element
module naive_interpreter

nitc :: naive_interpreter

Interpretation of a Nit program directly on the AST
module nit

nitc :: nit

A naive Nit interpreter
module nitc

nitc :: nitc

A Nit compiler
module nitcatalog

nitc :: nitcatalog

Basic catalog generator for Nit packages
module nitdoc

nitc :: nitdoc

Generator of static API documentation for the Nit language
module nith

nitc :: nith

A ligHt Nit compiler
module nitj

nitc :: nitj

Compile Nit into Java code runnable on the Java Virtual Machine.
module nitlight

nitc :: nitlight

Tool that produces highlighting for Nit programs
module nitls

nitc :: nitls

Simple tool to list Nit source files
module nitmetrics

nitc :: nitmetrics

A program that collects various metrics on nit programs and libraries
module nitni

nitc :: nitni

Native interface related services (used underneath the FFI)
module nitni_base

nitc :: nitni_base

Native interface related services (used underneath the FFI)
module nitni_callbacks

nitc :: nitni_callbacks

nitni services related to callbacks (used underneath the FFI)
module nitpackage

nitc :: nitpackage

Helpful features about packages
module nitpick

nitc :: nitpick

A program that collect potential style and code issues
module nitpretty

nitc :: nitpretty

module nitrestful

nitc :: nitrestful

Tool generating boilerplate code linking RESTful actions to Nit methods
module nitsaf

nitc :: nitsaf

Nit Static Analysis Framework client example.
module nitserial

nitc :: nitserial

Serialization support compiler, a tool to support deserialization of live generic types
module nitsmells

nitc :: nitsmells

module nituml

nitc :: nituml

UML generator in dot format.
module nitunit

nitc :: nitunit

Testing tool.
module nitvm

nitc :: nitvm

The Nit virtual machine launcher
module nitweb

nitc :: nitweb

Runs a webserver based on nitcorn that render things from model.
module nitx

nitc :: nitx

nitx, a command tool that displays useful data about Nit code
module no_warning

nitc :: no_warning

Fill toolcontext information about blacklisting of warnings.
module nullables_metrics

nitc :: nullables_metrics

Statistics about the usage of nullables
module objc

nitc :: objc

FFI support for Objective-C
module on_demand_compiler

nitc :: on_demand_compiler

Compiles extern code within a module to a static library, as needed
module parallelization_phase

nitc :: parallelization_phase

Phase generating threads for functions annotated with threaded annotation
module parse_annotations

nitc :: parse_annotations

Simple annotation parsing
module pkgconfig

nitc :: pkgconfig

Offers the PkgconfigPhase to use the external program "pkg-config" in order
module platform

nitc :: platform

Platform system, used to customize the behavior of the compiler.
module poset_metrics

nitc :: poset_metrics

Metrics about the various posets of the model of a Nit program
module pretty

nitc :: pretty

Library used to pretty print Nit code.
module rapid_type_analysis

nitc :: rapid_type_analysis

Rapid type analysis on the AST
module readme_metrics

nitc :: readme_metrics

Collect common metrics about README files
module refinement_metrics

nitc :: refinement_metrics

Collect metrics about refinement usage
module regex_phase

nitc :: regex_phase

Check for error in regular expressions from string literals
module rta_metrics

nitc :: rta_metrics

Metrics from RTA
module saf

nitc :: saf

Nit Static Analysis Framework.
module saf_base

nitc :: saf_base

Static Analysis Framework base
module scope

nitc :: scope

Identification and scoping of local variables and labels.
module self_metrics

nitc :: self_metrics

Metrics about the usage of explicit and implicit self
module semantize

nitc :: semantize

Process bodies of methods in regard with the model.
module separate_compiler

nitc :: separate_compiler

Separate compilation of a Nit program
module separate_erasure_compiler

nitc :: separate_erasure_compiler

Separate compilation of a Nit program with generic type erasure
module serialization_code_gen_phase

nitc :: serialization_code_gen_phase

Phase generating methods (code) to serialize Nit objects
module serialization_model_phase

nitc :: serialization_model_phase

Phase generating methods (model-only) to serialize Nit objects
module ssa

nitc :: ssa

Single-Static Assignment algorithm from an AST
module static

nitc :: static

Nitdoc generation framework
module static_base

nitc :: static_base

Base entities shared by all the nitdoc code
module static_cards

nitc :: static_cards

Cards templates for the static documentation
module static_html

nitc :: static_html

Render documentation pages as HTML
module static_index

nitc :: static_index

Manage indexing of Nit model for Nitdoc QuickSearch.
module static_structure

nitc :: static_structure

Composes the pages of the static documentation
module static_types_metrics

nitc :: static_types_metrics

Metrics on the usage of explicit static types.
module tables_metrics

nitc :: tables_metrics

Metrics on table generation
module term

nitc :: term

module term_model

nitc :: term_model

Markdown templates for Nit model MEntities.
module test_astbuilder

nitc :: test_astbuilder

Program used to test the clone method of the astbuilder tool
module test_highlight

nitc :: test_highlight

Program used to test the Nit highlighter
module test_model_visitor

nitc :: test_model_visitor

Example of model_visitor
module test_neo

nitc :: test_neo

Test for neo model saving and loading.
module test_phase

nitc :: test_phase

Stub for loading a runing phases on a bunch of modules
module test_test_phase

nitc :: test_test_phase

Example of simple module that aims to do some specific work on nit programs.
module testing

nitc :: testing

Test unit generation and execution for Nit.
module testing_base

nitc :: testing_base

Base options for testing tools.
module testing_doc

nitc :: testing_doc

Testing from code comments.
module testing_gen

nitc :: testing_gen

Test Suites generation.
module testing_suite

nitc :: testing_suite

Testing from external files.
module transform

nitc :: transform

Thansformations that simplify the AST of expressions
module typing

nitc :: typing

Intraprocedural resolution of static types and OO-services
module uml

nitc :: uml

Group head module for UML generation services
module uml_base

nitc :: uml_base

Exposes the base class for UML generation of a Model
module uml_class

nitc :: uml_class

Provides facilities of exporting a Model to a UML class diagram
module uml_module

nitc :: uml_module

Services for generation of a UML package diagram based on a Model
module variables_numbering

nitc :: variables_numbering

Handle all numbering operations related to local variables in the Nit virtual machine
module vim_autocomplete

nitc :: vim_autocomplete

Generate files used by the Vim plugin to autocomplete with doc
module virtual_machine

nitc :: virtual_machine

Implementation of the Nit virtual machine
module vm

nitc :: vm

Entry point of all vm components
module vm_optimizations

nitc :: vm_optimizations

Optimization of the nitvm
module xcode_templates

nitc :: xcode_templates

Templates and other services to create XCode projects
# Loading of Nit source files
#
# The loader takes care of looking for module and projects in the file system, and the associated case of errors.
# The loading requires several steps:
#
# Identify: create an empty model entity associated to a name or a file path.
# Identification is used for instance when names are given in the command line.
# See `identify_module` and `identify_group`.
#
# Scan: visit directories and identify their contents.
# Scanning is done to enable the searching of modules in projects.
# See `scan_group` and `scan_full`.
#
# Parse: load the AST and associate it with the model entity.
# See `MModule::parse`.
#
# Import: means recursively load modules imported by a module.
# See `build_module_importation`.
#
# Load: means doing the full sequence: identify, parse and import.
# See `ModelBuilder::parse`, `ModelBuilder::parse_full`, `MModule::load` `ModelBuilder::load_module.
module loader

import modelbuilder_base
import ini
import nitpm_shared

redef class ToolContext
	# Option --path
	var opt_path = new OptionArray("Add an additional include path (may be used more than once)", "-I", "--path")

	# Option --only-metamodel
	var opt_only_metamodel = new OptionBool("Stop after meta-model processing", "--only-metamodel")

	# Option --only-parse
	var opt_only_parse = new OptionBool("Only proceed to parse files", "--only-parse")

	redef init
	do
		super
		option_context.add_option(opt_path, opt_only_parse, opt_only_metamodel)
	end
end

redef class ModelBuilder
	redef init
	do
		super

		# Setup the paths value
		paths.append(toolcontext.opt_path.value)

		# Packages managed by nitpm, only use when not testing with tests.sh
		if "NIT_TESTING_TESTS_SH".environ != "true" then
			paths.add nitpm_lib_dir
		end

		var path_env = "NIT_PATH".environ
		if not path_env.is_empty then
			paths.append(path_env.split_with(':'))
		end

		var nit_dir = toolcontext.nit_dir
		if nit_dir != null then
			var libname = nit_dir/"lib"
			if libname.file_exists then paths.add(libname)
			libname = nit_dir/"contrib"
			if libname.file_exists then paths.add(libname)
		end
	end

	# Load a bunch of modules.
	# `modules` can contains filenames or module names.
	# Imported modules are automatically loaded and modelized.
	# The result is the corresponding model elements.
	# Errors and warnings are printed with the toolcontext.
	#
	# Note: class and property model elements 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
			var mmodule = nmodule.mmodule
			if mmodule == null then continue # skip error
			mmodules.add mmodule
		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)
			self.toolcontext.quit
		end

		return mmodules.to_a
	end

	# Identify a bunch of modules and groups.
	#
	# This does the same as `parse_full` but does only the identification (cf. `identify_module`)
	fun scan_full(names: Sequence[String]): Array[MModule]
	do
		var mmodules = new Array[MModule]
		for a in names do
			# Case of a group (root or sub-directory)
			var mgroup = self.identify_group(a)
			if mgroup != null then
				scan_group(mgroup)
				for mg in mgroup.in_nesting.smallers do mmodules.add_all mg.mmodules
				continue
			end

			# Case of a directory that is not a group
			var stat = a.to_path.stat
			if stat != null and stat.is_dir then
				self.toolcontext.info("look in directory {a}", 2)
				var fs = a.files
				alpha_comparator.sort(fs)
				# Try each entry as a group or a module
				for f in fs do
					if f.first == '.' then continue
					var af = a/f
					mgroup = identify_group(af)
					if mgroup != null then
						scan_group(mgroup)
						for mg in mgroup.in_nesting.smallers do mmodules.add_all mg.mmodules
						continue
					end
					var mmodule = identify_module(af)
					if mmodule != null then
						mmodules.add mmodule
					else
						self.toolcontext.info("ignore file {af}", 2)
					end
				end
				continue
			end

			var mmodule = identify_module(a)
			if mmodule == null then
				var le = last_loader_error
				if le != null then
					toolcontext.error(null, le)
				else if a.file_exists then
					toolcontext.error(null, "Error: `{a}` is not a Nit source file.")
				else
					toolcontext.error(null, "Error: cannot find module `{a}`.")
				end
				continue
			end

			mmodules.add mmodule
		end
		return mmodules
	end

	# Load a bunch of modules and groups.
	#
	# Each name can be:
	#
	# * a path to a module, a group or a directory of packages.
	# * a short name of a module or a group that are looked in the `paths` (-I)
	#
	# Then, for each entry, if it is:
	#
	# * a module, then is it parsed and returned.
	# * a group then recursively all its modules are parsed.
	# * a directory of packages then all the modules of all packages are parsed.
	# * else an error is displayed.
	#
	# See `parse` for details.
	fun parse_full(names: Sequence[String]): Array[MModule]
	do
		var time0 = get_time
		# Parse and recursively load
		self.toolcontext.info("*** PARSE ***", 1)
		var mmodules = new ArraySet[MModule]
		var scans = scan_full(names)
		for mmodule in scans do
			var ast = mmodule.load(self)
			if ast == null then continue # Skip error
			mmodules.add mmodule
		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)
			self.toolcontext.quit
		end

		return mmodules.to_a
	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
	#   * `toolcontext.nit_dir`
	# Path can be added (or removed) by the client
	var paths = new Array[String]

	# Like (and used by) `get_mmodule_by_name` but does not force the parsing of the MModule (cf. `identify_module`)
	fun search_mmodule_by_name(anode: nullable ANode, mgroup: nullable MGroup, name: String): nullable MModule
	do
		# First, look in groups
		var c = mgroup
		if c != null then
			var r = c.mpackage.root
			assert r != null
			scan_group(r)
			var res = r.mmodules_by_name(name)
			if res.not_empty then return res.first
		end

		# Look at some known directories
		var lookpaths = self.paths

		# Look in the directory of the group package also (even if not explicitly in the path)
		if mgroup != null then
			# path of the root group
			var dirname = mgroup.mpackage.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

		if mgroup != null then
			var alias = mgroup.mpackage.import_alias(name)
			if alias != null then name = alias
		end

		var loc = null
		if anode != null then loc = anode.hot_location
		var candidate = search_module_in_paths(loc, name, lookpaths)

		if candidate == null then
			if mgroup != null then
				error(anode, "Error: cannot find module `{name}` from `{mgroup.name}`. Tried: {lookpaths.join(", ")}.")
			else
				error(anode, "Error: cannot find module `{name}`. Tried: {lookpaths.join(", ")}.")
			end
			return null
		end
		return candidate
	end

	# Get a module by its short name; if required, the module is loaded, parsed and its hierarchies computed.
	# If `mgroup` is set, then the module search starts from it up to the top level (see `paths`);
	# if `mgroup` 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.
	fun get_mmodule_by_name(anode: nullable ANode, mgroup: nullable MGroup, name: String): nullable MModule
	do
		var mmodule = search_mmodule_by_name(anode, mgroup, name)
		if mmodule == null then return null # Forward error
		var ast = mmodule.load(self)
		if ast == null then return null # Forward error
		return mmodule
	end

	# Search a module `name` from path `lookpaths`.
	# If found, the module is returned.
	private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable MModule
	do
		var name_no_version
		if name.has('=') then
			name_no_version = name.split('=').first
		else name_no_version = name

		var res = new ArraySet[MModule]
		for dirname in lookpaths do
			# Try a single module file
			var mp = identify_module((dirname/"{name}.nit").simplify_path)
			if mp != null then res.add mp
			# Try the default module of a group
			var g = identify_group((dirname/name).simplify_path)
			if g != null then
				scan_group(g)
				res.add_all g.mmodules_by_name(name_no_version)
			end
		end
		if res.is_empty then return null
		if res.length > 1 then
			toolcontext.error(location, "Error: conflicting module files for `{name}`: `{[for x in res do x.filepath or else x.full_name].join("`, `")}`")
		end
		return res.first
	end

	# Search groups named `name` from paths `lookpaths`.
	private fun search_group_in_paths(name: String, lookpaths: Collection[String]): ArraySet[MGroup]
	do
		var res = new ArraySet[MGroup]
		for dirname in lookpaths do
			# try a single group directory
			var mg = identify_group(dirname/name)
			if mg != null then
				res.add mg
			end
		end
		return res
	end

	# Cache for `identify_module` by relative and real paths
	private var identified_modules_by_path = new HashMap[String, nullable MModule]

	# All the currently identified modules.
	# See `identify_module`.
	#
	# An identified module exists in the model but might be not yet parsed (no AST), or not yet analysed (no importation).
	var identified_modules = new Array[MModule]

	# All the currently parsed modules.
	#
	# A parsed module exists in the model but might be not yet analysed (no importation).
	var parsed_modules = new Array[MModule]

	# Some `loader` services are silent and return `null` on error.
	#
	# Those services can set `last_loader_error` to precise an specific error message.
	# if `last_loader_error == null` then a generic error message can be used.
	#
	# See `identified_modules` and `identify_group` for details.
	var last_loader_error: nullable String = null

	# Identify a source file and load the associated package and groups if required.
	#
	# This method does what the user expects when giving an argument to a Nit tool.
	#
	# * If `path` is an existing Nit source file (with the `.nit` extension),
	#   then the associated MModule is returned
	# * If `path` is a directory (with a `/`),
	#   then the MModule of its default module is returned (if any)
	# * If `path` is a simple identifier (eg. `digraph`),
	#   then the main module of the package `digraph` is searched in `paths` and returned.
	#
	# Silently return `null` if `path` does not exists or cannot be identified.
	# If `null` is returned, `last_loader_error` can be set to a specific error message.
	#
	# On success, it returns a module that is possibly not yet parsed (no AST), or not yet analysed (no importation).
	# If the module was already identified, or loaded, it is returned.
	fun identify_module(path: String): nullable MModule
	do
		last_loader_error = null

		# special case for not a nit file
		if not path.has_suffix(".nit") then do
			# search dirless files in known -I paths
			if not path.chars.has('/') then
				var res = search_module_in_paths(null, path, self.paths)
				if res != null then return res
			end

			# Found nothing? maybe it is a group...
			if path.file_exists then
				var mgroup = identify_group(path)
				if mgroup != null then
					var owner_path = mgroup.filepath.join_path(mgroup.name + ".nit")
					if owner_path.file_exists then
						path = owner_path
						break
					end
				end
			end

			# Found nothing? maybe it is a qualified name
			if path.chars.has(':') then
				var ids = path.split("::")
				var g = identify_group(ids.first)
				if g != null then
					scan_group(g)
					var ms = g.mmodules_by_name(ids.last)

					# Return exact match
					for m in ms do
						if m.full_name == path then
							return m
						end
					end

					# Where there is only one or two names `foo::bar`
					# then accept module that matches `foo::*::bar`
					if ids.length <= 2 then
						if ms.length == 1 then return ms.first
						if ms.length > 1 then
							var l = new Array[String]
							for m in ms do
								var fp = m.filepath
								if fp != null then fp = " ({fp})" else fp = ""
								l.add "`{m.full_name}`{fp}"
							end
							last_loader_error = "Error: conflicting module for `{path}`: {l.join(", ")} "
							return null
						end
					end

					var bests = new BestDistance[String](path.length / 2)
					# We found nothing. But propose something in the package?
					for sg in g.mpackage.mgroups do
						for m in sg.mmodules do
							var d = path.levenshtein_distance(m.full_name)
							bests.update(d, m.full_name)
						end
					end
					var last_loader_error = "Error: cannot find module `{path}`."
					if bests.best_items.not_empty then
						last_loader_error += " Did you mean " + bests.best_items.join(", ", " or ") + "?"
					end
					self.last_loader_error = last_loader_error
					return null
				end
			end

			return null
		end

		# Does the file exists?
		if not path.file_exists then
			return null
		end

		# Fast track, the path is already known
		if identified_modules_by_path.has_key(path) then return identified_modules_by_path[path]
		var rp = module_absolute_path(path)
		if identified_modules_by_path.has_key(rp) then return identified_modules_by_path[rp]

		var pn = path.basename(".nit")

		# Search for a group
		var mgrouppath = path.join_path("..").simplify_path
		var mgroup = identify_group(mgrouppath)

		if mgroup != null then
			var mpackage = mgroup.mpackage
			if not mpackage.accept(path) then
				mgroup = null
				toolcontext.info("module `{path}` excluded from package `{mpackage}`", 2)
			end
		end
		if mgroup == null then
			# singleton package
			var loc = new Location.opaque_file(path)
			var mpackage = new MPackage(pn, model, loc)
			mgroup = new MGroup(pn, loc, mpackage, null) # same name for the root group
			mpackage.root = mgroup
			toolcontext.info("found singleton package `{pn}` at {path}", 2)

			# Attach homonymous `ini` file to the package
			var inipath = path.dirname / "{pn}.ini"
			if inipath.file_exists then
				var ini = new IniFile.from_file(inipath)
				mpackage.ini = ini
			end
		end

		var loc = new Location.opaque_file(path)
		var res = new MModule(model, mgroup, pn, loc)

		identified_modules_by_path[rp] = res
		identified_modules_by_path[path] = res
		identified_modules.add(res)
		return res
	end

	# Groups by path
	private var mgroups = new HashMap[String, nullable MGroup]

	# Return the mgroup associated to a directory path.
	# If the directory is not a group null is returned.
	#
	# Silently return `null` if `dirpath` does not exists, is not a directory,
	# cannot be identified or cannot be attached to a mpackage.
	# If `null` is returned, `last_loader_error` can be set to a specific error message.
	#
	# Note: `paths` is also used to look for mgroups
	fun identify_group(dirpath: String): nullable MGroup
	do
		# Reset error
		last_loader_error = null

		var stat = dirpath.file_stat

		if stat == null or not stat.is_dir then do
			# search dirless directories in known -I paths
			if dirpath.chars.has('/') then return null
			for p in paths do
				var try = p / dirpath
				stat = try.file_stat
				if stat != null then
					dirpath = try
					break label
				end
			end
			return null
		end label

		# Filter out non-directories
		if not stat.is_dir then
			last_loader_error = "Error: `{dirpath}` is not a directory."
			return null
		end

		# Fast track, the path is already known
		var rdp = module_absolute_path(dirpath)
		if mgroups.has_key(rdp) then
			return mgroups[rdp]
		end

		# By default, the name of the package or group is the base_name of the directory
		var pn = rdp.basename

		# Check `package.ini` that indicate a package
		var ini = null
		var parent = null
		var inipath = dirpath / "package.ini"
		if inipath.file_exists then
			ini = new IniFile.from_file(inipath)
		end

		if ini == null then
			# No ini, multiple course of action

			# The root of the directory hierarchy in the file system.
			if rdp == "/" then
				mgroups[rdp] = null
				last_loader_error = "Error: `{dirpath}` is not a Nit package."
				return null
			end

			# Special stopper `packages.ini`
			if (dirpath/"packages.ini").file_exists then
				# dirpath cannot be a package since it is a package directory
				mgroups[rdp] = null
				last_loader_error = "Error: `{dirpath}` is not a Nit package."
				return null
			end

			# check the parent directory (if it does not contain the stopper file)
			var parentpath = dirpath.join_path("..").simplify_path
			var stopper = parentpath / "packages.ini"
			if not stopper.file_exists then
				# Recursively get the parent group
				parent = identify_group(parentpath)
				if parent != null then do
					var mpackage = parent.mpackage
					if not mpackage.accept(dirpath) then
						toolcontext.info("directory `{dirpath}` excluded from package `{mpackage}`", 2)
						parent = null
					end
				end
				if parent == null then
					# Parent is not a group, thus we are not a group either
					mgroups[rdp] = null
					last_loader_error = "Error: `{dirpath}` is not a Nit package."
					return null
				end
			end
		end

		var loc = new Location.opaque_file(dirpath)
		var mgroup
		if parent == null then
			# no parent, thus new package
			if ini != null then pn = ini["package.name"] or else pn
			var mpackage = new MPackage(pn, model, loc)
			mgroup = new MGroup(pn, loc, mpackage, null) # same name for the root group
			mpackage.root = mgroup
			toolcontext.info("found package `{mpackage}` at {dirpath}", 2)
			mpackage.ini = ini
		else
			mgroup = new MGroup(pn, loc, parent.mpackage, parent)
			toolcontext.info("found sub group `{mgroup.full_name}` at {dirpath}", 2)
		end

		# search documentation
		# in src first so the documentation of the package code can be distinct for the documentation of the package usage
		var readme = dirpath.join_path("README.md")
		if not readme.file_exists then readme = dirpath.join_path("README")
		if readme.file_exists then
			var mdoc = load_markdown(readme)
			mgroup.mdoc = mdoc
			mdoc.original_mentity = mgroup
		end

		mgroups[rdp] = mgroup
		return mgroup
	end

	# Load a markdown file as a documentation object
	fun load_markdown(filepath: String): MDoc
	do
		var s = new FileReader.open(filepath)
		var lines = new Array[String]
		var line_starts = new Array[Int]
		var len = 1
		while not s.eof do
			var line = s.read_line
			lines.add(line)
			line_starts.add(len)
			len += line.length + 1
		end
		s.close
		var source = new SourceFile.from_string(filepath, lines.join("\n"))
		source.line_starts.add_all line_starts
		var mdoc = new MDoc(new Location(source, 1, lines.length, 0, 0))
		mdoc.content.add_all(lines)
		return mdoc
	end

	# Force the identification of all MModule of the group and sub-groups in the file system.
	#
	# When a group is scanned, its sub-groups hierarchy is filled (see `MGroup::in_nesting`)
	# and the potential modules (and nested modules) are identified (see `MGroup::modules`).
	#
	# Basically, this recursively call `identify_group` and `identify_module` on each directory entry.
	#
	# No-op if the group was already scanned (see `MGroup::scanned`).
	fun scan_group(mgroup: MGroup) do
		if mgroup.scanned then return
		mgroup.scanned = true
		var p = mgroup.filepath
		# a virtual group has nothing to scan
		if p == null then return
		var files = p.files
		alpha_comparator.sort(files)
		for f in files do
			if f.first == '.' then continue
			var fp = p/f
			var g = identify_group(fp)
			# Recursively scan for groups of the same package
			if g == null then
				identify_module(fp)
			else if g.mpackage == mgroup.mpackage then
				scan_group(g)
			end
		end
	end

	# Transform relative paths (starting with '../') into absolute paths
	private fun module_absolute_path(path: String): String do
		return path.realpath
	end

	# Try to load a module AST using a path.
	# Display an error if there is a problem (IO / lexer / parser) and return null
	#
	# The AST is loaded as is total independence of the model and its entities.
	#
	# AST are not cached or reused thus a new AST is returned on success.
	fun load_module_ast(filename: String): nullable AModule
	do
		if not filename.has_suffix(".nit") then
			self.toolcontext.error(null, "Error: file `{filename}` is not a valid nit module.")
			return null
		end
		if not filename.file_exists then
			self.toolcontext.error(null, "Error: file `{filename}` not found.")
			return null
		end

		self.toolcontext.info("load module {filename}", 2)

		# Load the file
		var file = new FileReader.open(filename)
		var lexer = new Lexer(new SourceFile(filename, file))
		var parser = new Parser(lexer)
		var tree = parser.parse
		file.close

		# Handle lexer and parser error
		var nmodule = tree.n_base
		if nmodule == null then
			var neof = tree.n_eof
			assert neof isa AError
			error(neof, neof.message)
			return null
		end

		return nmodule
	end

	# Remove Nit source files from a list of arguments.
	#
	# Items of `args` that can be loaded as a nit file will be removed from `args` and returned.
	fun filter_nit_source(args: Array[String]): Array[String]
	do
		var keep = new Array[String]
		var res = new Array[String]
		for a in args do
			var stat = a.to_path.stat
			if stat != null and stat.is_dir then
				res.add a
				continue
			end
			var l = identify_module(a)
			if l == null then
				keep.add a
			else
				res.add a
			end
		end
		args.clear
		args.add_all(keep)
		return res
	end

	# Try to load a module using a path.
	# Display an error if there is a problem (IO / lexer / parser) and return null.
	# Note: usually, you do not need this method, use `get_mmodule_by_name` instead.
	#
	# The MModule is located, created, parsed and the importation is performed.
	fun load_module(filename: String): nullable AModule
	do
		# Look for the module
		var mmodule = identify_module(filename)
		if mmodule == null then
			var le = last_loader_error
			if le != null then
				toolcontext.error(null, le)
			else if filename.file_exists then
				toolcontext.error(null, "Error: `{filename}` is not a Nit source file.")
			else
				toolcontext.error(null, "Error: cannot find module `{filename}`.")
			end
			return null
		end

		# Load it
		return mmodule.load(self)
	end

	# Injection of a new module without source.
	# Used by the interpreter.
	fun load_rt_module(parent: nullable MModule, nmodule: AModule, mod_name: String): nullable MModule
	do
		# Create the module

		var mgroup = null
		if parent != null then mgroup = parent.mgroup
		var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
		nmodule.mmodule = mmodule
		nmodules.add(nmodule)
		parsed_modules.add mmodule
		self.mmodule2nmodule[mmodule] = nmodule

		if parent!= null then
			var imported_modules = new Array[MModule]
			imported_modules.add(parent)
			mmodule.set_visibility_for(parent, intrude_visibility)
			mmodule.set_imported_mmodules(imported_modules)
		end
		build_module_importation(nmodule)

		return mmodule
	end

	# Visit the AST and create the `MModule` object
	private fun build_a_mmodule(mgroup: nullable MGroup, nmodule: AModule)
	do
		var mmodule = nmodule.mmodule
		assert mmodule != null

		# Check the module name
		var decl = nmodule.n_moduledecl
		if decl != null then
			var decl_name = decl.n_name.n_id.text
			if decl_name != mmodule.name then
				warning(decl.n_name, "module-name-mismatch", "Error: module name mismatch; declared {decl_name} file named {mmodule.name}.")
			end
		end

		# Check for conflicting module names in the package
		if mgroup != null then
			var others = model.get_mmodules_by_name(mmodule.name)
			if others != null then for other in others do
				if other != mmodule and mmodule2nmodule.has_key(mmodule) and other.mgroup!= null and other.mgroup.mpackage == mgroup.mpackage then
					var node: ANode
					if decl == null then node = nmodule else node = decl.n_name
					error(node, "Error: a module named `{other.full_name}` already exists at {other.location}.")
					break
				end
			end
		end

		nmodules.add(nmodule)
		self.mmodule2nmodule[mmodule] = nmodule

		var source = nmodule.location.file
		if source != null then
			assert source.mmodule == null
			source.mmodule = mmodule
		end

		if decl != null then
			# Extract documentation
			var ndoc = decl.n_doc
			if ndoc != null then
				var mdoc = ndoc.to_mdoc
				mmodule.mdoc = mdoc
				mdoc.original_mentity = mmodule
			end
			# Is the module generated?
			mmodule.is_generated = not decl.get_annotations("generated").is_empty
		end
	end

	# Resolve the module identification for a given `AModuleName`.
	#
	# This method handles qualified names as used in `AModuleName`.
	fun search_module_by_amodule_name(n_name: AModuleName, mgroup: nullable MGroup): nullable MModule
	do
		var mod_name = n_name.n_id.text

		# If a quad is given, we ignore the starting group (go from path)
		if n_name.n_quad != null then mgroup = null

		# If name not qualified, just search the name
		if n_name.n_path.is_empty then
			# Fast search if no n_path
			return search_mmodule_by_name(n_name, mgroup, mod_name)
		end

		# If qualified and in a group
		if mgroup != null then
			# First search in the package
			var r = mgroup.mpackage.root
			assert r != null
			scan_group(r)
			# Get all modules with the final name
			var res = r.mmodules_by_name(mod_name)
			# Filter out the name that does not match the qualifiers
			res = [for x in res do if match_amodulename(n_name, x) then x]
			if res.not_empty then
				if res.length > 1 then
					error(n_name, "Error: conflicting module files for `{mod_name}`: `{[for x in res do x.filepath or else x.full_name].join("`, `")}`")
				end
				return res.first
			end
		end

		# If no module yet, then assume that the first element of the path
		# Is to be searched in the path.
		var root_name = n_name.n_path.first.text

		# Search for an alias in required external packages
		if mgroup != null then
			var alias = mgroup.mpackage.import_alias(root_name)
			if alias != null then root_name = alias
		end

		var roots = search_group_in_paths(root_name, paths)
		if roots.is_empty then
			error(n_name, "Error: cannot find `{root_name}`. Tried: {paths.join(", ")}.")
			return null
		end

		var res = new ArraySet[MModule]
		for r in roots do
			# Then, for each root, collect modules that matches the qualifiers
			scan_group(r)
			var root_res = r.mmodules_by_name(mod_name)
			for x in root_res do if match_amodulename(n_name, x) then res.add x
		end
		if res.not_empty then
			if res.length > 1 then
				error(n_name, "Error: conflicting module files for `{mod_name}`: `{[for x in res do x.filepath or else x.full_name].join("`, `")}`")
			end
			return res.first
		end
		# If still nothing, just call a basic search that will fail and will produce an error message
		error(n_name, "Error: cannot find module `{mod_name}` from `{root_name}`. Tried: {paths.join(", ")}.")
		return null
	end

	# Is elements of `n_name` correspond to the group nesting of `m`?
	#
	# Basically it check that `bar::foo` matches `bar/foo.nit` and `bar/baz/foo.nit`
	# but not `baz/foo.nit` nor `foo/bar.nit`
	#
	# Is used by `search_module_by_amodule_name` to validate qualified names.
	private fun match_amodulename(n_name: AModuleName, m: MModule): Bool
	do
		var g: nullable MGroup = m.mgroup
		for grp in n_name.n_path.reverse_iterator do
			while g != null and grp.text != g.name do
				g = g.parent
			end
		end
		return g != null
	end

	# Analyze the module importation and fill the module_importation_hierarchy
	#
	# If the importation was already done (`nmodule.is_importation_done`), this method does a no-op.
	#
	# REQUIRE `nmodule.mmodule != null`
	# ENSURE `nmodule.is_importation_done`
	fun build_module_importation(nmodule: AModule)
	do
		if nmodule.is_importation_done then return
		nmodule.is_importation_done = true
		var mmodule = nmodule.mmodule.as(not null)
		var stdimport = true
		var imported_modules = new Array[MModule]
		for aimport in nmodule.n_imports do
			# Do not imports conditional
			var atconditionals = aimport.get_annotations("conditional")
			if atconditionals.not_empty then continue

			stdimport = false
			if not aimport isa AStdImport then
				continue
			end

			# Load the imported module
			var sup = search_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
			if sup == null then
				mmodule.is_broken = true
				nmodule.mmodule = null # invalidate the module
				continue # Skip error
			end
			var ast = sup.load(self)
			if ast == null then
				mmodule.is_broken = true
				nmodule.mmodule = null # invalidate the module
				continue # Skip error
			end

			aimport.mmodule = sup
			imported_modules.add(sup)
			var mvisibility = aimport.n_visibility.mvisibility
			if mvisibility == protected_visibility then
				mmodule.is_broken = true
				error(aimport.n_visibility, "Error: only properties can be protected.")
				mmodule.is_broken = true
				nmodule.mmodule = null # invalidate the module
				return
			end
			if sup == mmodule then
				error(aimport.n_name, "Error: dependency loop in module {mmodule}.")
				mmodule.is_broken = true
				nmodule.mmodule = null # invalidate the module
			end
			if sup.in_importation < mmodule then
				error(aimport.n_name, "Error: dependency loop between modules {mmodule} and {sup}.")
				mmodule.is_broken = true
				nmodule.mmodule = null # invalidate the module
				return
			end
			mmodule.set_visibility_for(sup, mvisibility)
		end
		if stdimport then
			var mod_name = "core"
			var sup = self.get_mmodule_by_name(nmodule, null, mod_name)
			if sup == null then
				mmodule.is_broken = true
				nmodule.mmodule = null # invalidate the module
			else # Skip error
				imported_modules.add(sup)
				mmodule.set_visibility_for(sup, public_visibility)
			end
		end

		# Declare conditional importation
		for aimport in nmodule.n_imports do
			if not aimport isa AStdImport then continue
			var atconditionals = aimport.get_annotations("conditional")
			if atconditionals.is_empty then continue

			var suppath = search_module_by_amodule_name(aimport.n_name, mmodule.mgroup)
			if suppath == null then continue # skip error

			for atconditional in atconditionals do
				var nargs = atconditional.n_args
				if nargs.is_empty then
					error(atconditional, "Syntax Error: `conditional` expects module identifiers as arguments.")
					continue
				end

				# The rule
				var rule = new Array[MModule]

				# First element is the goal, thus
				rule.add suppath

				# Second element is the first condition, that is to be a client of the current module
				rule.add mmodule

				# Other condition are to be also a client of each modules indicated as arguments of the annotation
				for narg in nargs do
					var id = narg.as_id
					if id == null then
						error(narg, "Syntax Error: `conditional` expects module identifier as arguments.")
						continue
					end

					var mp = search_mmodule_by_name(narg, mmodule.mgroup, id)
					if mp == null then continue

					rule.add mp
				end

				conditional_importations.add rule
			end
		end

		mmodule.set_imported_mmodules(imported_modules)

		apply_conditional_importations(mmodule)

		self.toolcontext.info("{mmodule} imports {mmodule.in_importation.direct_greaters.join(", ")}", 3)

		# Force `core` to be public if imported
		for sup in mmodule.in_importation.greaters do
			if sup.name == "core" then
				mmodule.set_visibility_for(sup, public_visibility)
			end
		end

		# TODO: Correctly check for useless importation
		# It is even doable?
		var directs = mmodule.in_importation.direct_greaters
		for nim in nmodule.n_imports do
			if not nim isa AStdImport then continue
			var im = nim.mmodule
			if im == null then continue
			if directs.has(im) then continue
			# This generates so much noise that it is simpler to just comment it
			#warning(nim, "Warning: possible useless importation of {im}")
		end
	end

	# Global list of conditional importation rules.
	#
	# Each rule is a "Horn clause"-like sequence of modules.
	# It means that the first module is the module to automatically import.
	# The remaining modules are the conditions of the rule.
	#
	# Rules are declared by `build_module_importation` and are applied by `apply_conditional_importations`
	# (and `build_module_importation` that calls it).
	#
	# TODO (when the loader will be rewritten): use a better representation and move up rules in the model.
	var conditional_importations = new Array[SequenceRead[MModule]]

	# Extends the current importations according to imported rules about conditional importation
	fun apply_conditional_importations(mmodule: MModule)
	do
		# Because a conditional importation may cause additional conditional importation, use a fixed point
		# The rules are checked naively because we assume that it does not worth to be optimized
		var check_conditional_importations = true
		while check_conditional_importations do
			check_conditional_importations = false

			for ci in conditional_importations do
				# Check conditions
				for i in [1..ci.length[ do
					var m = ci[i]
					# Is imported?
					if mmodule == m or not mmodule.in_importation.greaters.has(m) then continue label
				end
				# Still here? It means that all conditions modules are loaded and imported

				# Identify the module to automatically import
				var sup = ci.first
				var ast = sup.load(self)
				if ast == null then continue

				# Do nothing if already imported
				if mmodule.in_importation.greaters.has(sup) then continue label

				# Import it
				self.toolcontext.info("{mmodule} conditionally imports {sup}", 3)
				# TODO visibility rules (currently always public)
				mmodule.set_visibility_for(sup, public_visibility)
				# TODO linearization rules (currently added at the end in the order of the rules)
				mmodule.set_imported_mmodules([sup])

				# Prepare to reapply the rules
				check_conditional_importations = true
			end label
		end
	end

	# All the loaded modules
	var nmodules = new Array[AModule]

	# Register the nmodule associated to each mmodule
	#
	# Public clients need to use `mmodule2node` to access stuff.
	private var mmodule2nmodule = new HashMap[MModule, AModule]

	# Retrieve the associated AST node of a mmodule.
	# This method is used to associate model entity with syntactic entities.
	#
	# If the module is not associated with a node, returns null.
	fun mmodule2node(mmodule: MModule): nullable AModule
	do
		return mmodule2nmodule.get_or_null(mmodule)
	end
end

redef class MModule
	# Force the parsing of the module using `modelbuilder`.
	#
	# If the module was already parsed, the existing ASI is returned.
	# Else the source file is loaded, and parsed and some
	#
	# The importation is not done by this
	#
	# REQUIRE: `filepath != null`
	# ENSURE: `modelbuilder.parsed_modules.has(self)`
	fun parse(modelbuilder: ModelBuilder): nullable AModule
	do
		# Already known and loaded? then return it
		var nmodule = modelbuilder.mmodule2nmodule.get_or_null(self)
		if nmodule != null then return nmodule

		var filepath = self.filepath
		assert filepath != null
		# Load it manually
		nmodule = modelbuilder.load_module_ast(filepath)
		if nmodule == null then return null # forward error

		# build the mmodule
		nmodule.mmodule = self
		self.location = nmodule.location
		modelbuilder.build_a_mmodule(mgroup, nmodule)

		modelbuilder.parsed_modules.add self
		return nmodule
	end

	# Parse and process importation of a given MModule.
	#
	# Basically chains `parse` and `build_module_importation`.
	fun load(modelbuilder: ModelBuilder): nullable AModule
	do
		var nmodule = parse(modelbuilder)
		if nmodule == null then return null

		modelbuilder.build_module_importation(nmodule)
		return nmodule
	end
end

redef class MPackage
	# The associated `.ini` file, if any
	#
	# The `ini` file is given as is and might contain invalid or missing information.
	#
	# Some packages, like stand-alone packages or virtual packages have no `ini` file associated.
	var ini: nullable IniFile = null

	# Array of relative source paths excluded according to the `source.exclude` key of the `ini`
	var excludes: nullable Array[String] is lazy do
		var ini = self.ini
		if ini == null then return null
		var exclude = ini["source.exclude"]
		if exclude == null then return null
		var excludes = exclude.split(":")
		return excludes
	end

	# Does the source inclusion/inclusion rules of the package `ini` accept such path?
	fun accept(filepath: String): Bool
	do
		var excludes = self.excludes
		if excludes != null then
			var relpath = root.filepath.relpath(filepath)
			if excludes.has(relpath) then return false
		end
		return true
	end

	# Get the name to search for, for a `root_name` declared as `import` in `ini`
	fun import_alias(root_name: String): nullable String
	do
		var map = import_aliases_parsed
		if map == null then return null

		var val = map.get_or_null(root_name)
		if val == null then return null

		return val.dir_name
	end

	private var import_aliases_parsed: nullable Map[String, ExternalPackage] is lazy do
		var ini = ini
		if ini == null then return null

		var import_line = ini["package.import"]
		if import_line == null then return null

		var map = import_line.parse_import
		if map.is_empty then return null

		return map
	end
end

redef class MGroup
	# Is the group interesting for a final user?
	#
	# Groups are mandatory in the model but for simple packages they are not
	# always interesting.
	#
	# A interesting group has, at least, one of the following true:
	#
	# * it has 2 modules or more
	# * it has a subgroup
	# * it has a documentation
	fun is_interesting: Bool
	do
		return mmodules.length > 1 or
			not in_nesting.direct_smallers.is_empty or
			mdoc != null or
			(mmodules.length == 1 and default_mmodule == null)
	end

	# Are files and directories in self scanned?
	#
	# See `ModelBuilder::scan_group`.
	var scanned = false

	# Return the modules in self and subgroups named `name`.
	#
	# If `self` is not scanned (see `ModelBuilder::scan_group`) the
	# results might be partial.
	fun mmodules_by_name(name: String): Array[MModule]
	do
		var res = new Array[MModule]
		for g in in_nesting.smallers do
			for mp in g.mmodules do
				if mp.name == name then
					res.add mp
				end
			end
		end
		return res
	end
end

redef class SourceFile
	# Associated mmodule, once created
	var mmodule: nullable MModule = null
end

redef class AStdImport
	# The imported module once determined
	var mmodule: nullable MModule = null
end

redef class AModule
	# The associated MModule once build by a `ModelBuilder`
	var mmodule: nullable MModule = null

	# Flag that indicate if the importation is already completed
	var is_importation_done: Bool = false
end
src/loader.nit:17,1--1288,3