Rename REAMDE to README.md
[nit.git] / src / loader.nit
index 492584b..0e6878b 100644 (file)
@@ -72,8 +72,9 @@ redef class ModelBuilder
                        if nmodule == null then continue # Skip error
                        # Load imported module
                        build_module_importation(nmodule)
-
-                       mmodules.add(nmodule.mmodule.as(not null))
+                       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)
@@ -100,16 +101,28 @@ redef class ModelBuilder
                                if nmodule == null then continue # Skip error
                                # Load imported module
                                build_module_importation(nmodule)
-
-                               res.add(nmodule.mmodule.as(not null))
+                               var mmodule = nmodule.mmodule
+                               if mmodule == null then continue # Skip error
+                               res.add mmodule
                        end
                end
                return res
        end
 
        # Load a bunch of modules and groups.
-       # Each name can be a module or a group.
-       # If it is a group then recursively all its modules are parsed.
+       #
+       # Each name can be:
+       #
+       # * a path to a module, a group or a directory of projects.
+       # * 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 parser and returned.
+       # * a group then recursively all its modules are parsed.
+       # * a directory of projects then all the modules of all projects are parsed.
+       # * else an error is displayed.
+       #
        # See `parse` for details.
        fun parse_full(names: Sequence[String]): Array[MModule]
        do
@@ -118,17 +131,48 @@ redef class ModelBuilder
                self.toolcontext.info("*** PARSE ***", 1)
                var mmodules = new ArraySet[MModule]
                for a in names do
+                       # Case of a group
                        var mgroup = self.get_mgroup(a)
                        if mgroup != null then
                                mmodules.add_all parse_group(mgroup)
                                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
+                               # Try each entry as a group or a module
+                               for f in fs do
+                                       var af = a/f
+                                       mgroup = get_mgroup(af)
+                                       if mgroup != null then
+                                               mmodules.add_all parse_group(mgroup)
+                                               continue
+                                       end
+                                       var mp = identify_file(af)
+                                       if mp != null then
+                                               var nmodule = self.load_module(af)
+                                               if nmodule == null then continue # Skip error
+                                               build_module_importation(nmodule)
+                                               var mmodule = nmodule.mmodule
+                                               if mmodule == null then continue # Skip error
+                                               mmodules.add mmodule
+                                       else
+                                               self.toolcontext.info("ignore file {af}", 2)
+                                       end
+                               end
+                               continue
+                       end
+
                        var nmodule = self.load_module(a)
                        if nmodule == null then continue # Skip error
                        # Load imported module
                        build_module_importation(nmodule)
-
-                       mmodules.add(nmodule.mmodule.as(not null))
+                       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)
@@ -201,9 +245,9 @@ redef class ModelBuilder
 
                if candidate == null then
                        if mgroup != null then
-                               error(anode, "Error: cannot find module {name} from {mgroup.name}. tried {lookpaths.join(", ")}")
+                               error(anode, "Error: cannot find module `{name}` from `{mgroup.name}`. Tried: {lookpaths.join(", ")}.")
                        else
-                               error(anode, "Error: cannot find module {name}. tried {lookpaths.join(", ")}")
+                               error(anode, "Error: cannot find module `{name}`. Tried: {lookpaths.join(", ")}.")
                        end
                        return null
                end
@@ -222,7 +266,7 @@ redef class ModelBuilder
                if res == null then return null # Forward error
                # Load imported module
                build_module_importation(res)
-               return res.mmodule.as(not null)
+               return res.mmodule
        end
 
        # Search a module `name` from path `lookpaths`.
@@ -240,7 +284,7 @@ redef class ModelBuilder
                                        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}")
+                                               toolcontext.error(location, "Error: conflicting module file for `{name}`: `{candidate}` `{try_file}`")
                                        end
                                end
                        end
@@ -253,7 +297,7 @@ redef class ModelBuilder
                                        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}")
+                                               toolcontext.error(location, "Error: conflicting module file for `{name}`: `{candidate}` `{try_file}`")
                                        end
                                end
                        end
@@ -350,12 +394,15 @@ redef class ModelBuilder
                        return mgroups[rdp]
                end
 
-               # Hack, a group is determined by:
+               # Hack, a group is determined by one of the following:
                # * the presence of a honomymous nit file
                # * the fact that the directory is named `src`
+               # * the fact that there is a sub-directory named `src`
                var pn = rdp.basename(".nit")
                var mp = dirpath.join_path(pn + ".nit").simplify_path
 
+               # dirpath2 is the root directory
+               # dirpath is the src subdirectory directory, if any, else it is the same that dirpath2
                var dirpath2 = dirpath
                if not mp.file_exists then
                        if pn == "src" then
@@ -363,12 +410,17 @@ redef class ModelBuilder
                                dirpath2 = rdp.dirname
                                pn = dirpath2.basename("")
                        else
-                               return null
+                               # Check a `src` subdirectory
+                               dirpath = dirpath2 / "src"
+                               if not dirpath.file_exists then
+                                       # All rules failed, so return null
+                                       return null
+                               end
                        end
                end
 
                # check parent directory
-               var parentpath = dirpath.join_path("..").simplify_path
+               var parentpath = dirpath2.join_path("..").simplify_path
                var parent = get_mgroup(parentpath)
 
                var mgroup
@@ -382,15 +434,22 @@ redef class ModelBuilder
                        mgroup = new MGroup(pn, parent.mproject, parent)
                        toolcontext.info("found sub group `{mgroup.full_name}` at {dirpath}", 2)
                end
-               var readme = dirpath2.join_path("README.md")
+
+               # search documentation
+               # in src first so the documentation of the project code can be distinct for the documentation of the project usage
+               var readme = dirpath.join_path("README.md")
+               if not readme.file_exists then readme = dirpath.join_path("README")
+               if not readme.file_exists then readme = dirpath2.join_path("README.md")
                if not readme.file_exists then readme = dirpath2.join_path("README")
                if readme.file_exists then
                        var mdoc = load_markdown(readme)
                        mgroup.mdoc = mdoc
                        mdoc.original_mentity = mgroup
                end
+
                mgroup.filepath = dirpath
-               mgroups[rdp] = mgroup
+               mgroups[module_absolute_path(dirpath)] = mgroup
+               mgroups[module_absolute_path(dirpath2)] = mgroup
                return mgroup
        end
 
@@ -426,11 +485,11 @@ redef class ModelBuilder
        fun load_module_ast(filename: String): nullable AModule
        do
                if filename.file_extension != "nit" then
-                       self.toolcontext.error(null, "Error: file {filename} is not a valid nit module.")
+                       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.")
+                       self.toolcontext.error(null, "Error: file `{filename}` not found.")
                        return null
                end
 
@@ -548,7 +607,7 @@ redef class ModelBuilder
                if decl != null then
                        var decl_name = decl.n_name.n_id.text
                        if decl_name != mod_name then
-                               error(decl.n_name, "Error: module name missmatch; declared {decl_name} file named {mod_name}")
+                               error(decl.n_name, "Error: module name mismatch; declared {decl_name} file named {mod_name}.")
                        end
                end
 
@@ -559,7 +618,7 @@ redef class ModelBuilder
                                if other.mgroup!= null and other.mgroup.mproject == mgroup.mproject 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}")
+                                       error(node, "Error: a module named `{other.full_name}` already exists at {other.location}.")
                                        break
                                end
                        end
@@ -613,24 +672,33 @@ redef class ModelBuilder
                        if aimport.n_name.n_quad != null then mgroup = null # Start from top level
                        for grp in aimport.n_name.n_path do
                                var path = search_mmodule_by_name(grp, mgroup, grp.text)
-                               if path == null then return # Skip error
+                               if path == null then
+                                       nmodule.mmodule = null # invalidate the module
+                                       return # Skip error
+                               end
                                mgroup = path.mgroup
                        end
                        var mod_name = aimport.n_name.n_id.text
                        var sup = self.get_mmodule_by_name(aimport.n_name, mgroup, mod_name)
-                       if sup == null then continue # Skip error
+                       if sup == null then
+                               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
                                error(aimport.n_visibility, "Error: only properties can be protected.")
+                               nmodule.mmodule = null # invalidate the module
                                return
                        end
                        if sup == mmodule then
-                               error(aimport.n_name, "Error: Dependency loop in module {mmodule}.")
+                               error(aimport.n_name, "Error: dependency loop in module {mmodule}.")
+                               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}.")
+                               error(aimport.n_name, "Error: dependency loop between modules {mmodule} and {sup}.")
+                               nmodule.mmodule = null # invalidate the module
                                return
                        end
                        mmodule.set_visibility_for(sup, mvisibility)
@@ -638,7 +706,9 @@ redef class ModelBuilder
                if stdimport then
                        var mod_name = "standard"
                        var sup = self.get_mmodule_by_name(nmodule, null, mod_name)
-                       if sup != null then # Skip error
+                       if sup == null then
+                               nmodule.mmodule = null # invalidate the module
+                       else # Skip error
                                imported_modules.add(sup)
                                mmodule.set_visibility_for(sup, public_visibility)
                        end