Merge branch 'privat' into topub
authorAlexis Laferrière <alexis.laf@xymus.net>
Sat, 8 Feb 2014 03:06:22 +0000 (22:06 -0500)
committerAlexis Laferrière <alexis.laf@xymus.net>
Sat, 8 Feb 2014 03:06:22 +0000 (22:06 -0500)
43 files changed:
README
clib/gc_chooser.c
lib/ordered_tree.nit [new file with mode: 0644]
lib/privileges.nit
lib/standard/collection/abstract_collection.nit
lib/standard/collection/array.nit
lib/standard/collection/range.nit
lib/standard/file.nit
lib/standard/file_nit.h
misc/README
misc/syntaxhighlighter/shBrushNit.js [new file with mode: 0644]
share/nitdoc/scripts/Nitdoc.GitHub.js
share/nitdoc/styles/Nitdoc.GitHub.css
src/abstract_compiler.nit
src/frontend.nit
src/global_compiler.nit
src/interpretor_type_test.nit [deleted file]
src/metrics/generate_hierarchies.nit
src/metrics/model_hyperdoc.nit
src/metrics/poset_metrics.nit
src/model/mmodule.nit [new file with mode: 0644]
src/model/model.nit
src/model/model_base.nit
src/model/mproject.nit [new file with mode: 0644]
src/model_viz.nit [new file with mode: 0644]
src/modelbuilder.nit
src/naive_interpreter.nit
src/nitdoc.nit
src/nitunit.nit
src/nitx.nit
src/separate_compiler.nit
src/test_phase.nit [new file with mode: 0644]
tests/nitdoc.args
tests/nitmetrics.args
tests/nitunit.args
tests/sav/error_mod_unk.res
tests/sav/nitg.res
tests/sav/nitmetrics_args1.res
tests/sav/nitunit_args1.res
tests/sav/test_phase.res [new file with mode: 0644]
tests/test_nitunit.nit [new file with mode: 0644]
tests/testfull.sh
tests/tests.sh

diff --git a/README b/README
index 0b17475..c3e7c1b 100644 (file)
--- a/README
+++ b/README
@@ -20,10 +20,12 @@ Requirement:
        * ccache        http://ccache.samba.org/        to improve recompilation
        * libgc-dev     http://www.hpl.hp.com/personal/Hans_Boehm/gc/
        * graphviz      http://www.graphviz.org/        to enable graphes with the nitdoc tool
+       * libunwind     http://nongnu.org/libunwind
+       * gperf         http://gnu.org/software/gperf   to enable mapping from C to Nit function names in a stack trace
 
 Those are available in most linux distributions
 
-    # sudo apt-get install build-essential ccache libgc-dev graphviz
+    # sudo apt-get install build-essential ccache libgc-dev graphviz libunwind gperf
 
 Important files and directory:
 
index 5a8cffe..ca06552 100644 (file)
@@ -25,7 +25,6 @@
 enum gc_option { gc_opt_large, gc_opt_malloc, gc_opt_boehm } gc_option;
 
 #ifdef WITH_LIBGC
-#define GC_DEBUG
 #include <gc/gc.h>
 #endif
 
diff --git a/lib/ordered_tree.nit b/lib/ordered_tree.nit
new file mode 100644 (file)
index 0000000..ede2196
--- /dev/null
@@ -0,0 +1,93 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# management and display of ordered trees
+module ordered_tree
+
+# Generic structure to manage and display an ordered tree
+#
+# Ordered tree are tree where the elements of a same parent are in a specific order
+#
+# The class can be used as it to work with generic tree.
+# The class can also be specialized to provide more specific behavior.
+class OrderedTree[E: Object]
+       # Sequence
+       var roots = new Array[E]
+       var sub = new HashMap[E, Array[E]]
+
+       # Add a new element `e` in the tree
+       # `p` is the parent of `e`.
+       # if `p` is null, then `e` is a root element.
+       #
+       # By defauld, the elements with a same parent 
+       # are displayed in the order they are added.
+       #
+       # The `sort_with` method can be used reorder elements
+       fun add(p: nullable E, e: E)
+       do
+               if p == null then
+                       roots.add(e)
+               else if sub.has_key(p) then
+                       sub[p].add(e)
+               else
+                       sub[p] = [e]
+               end
+       end
+
+       # print the full tree on `o`
+       # Write a ASCII-style tree and use the `display` method to label elements
+       fun pretty(o: OStream)
+       do
+               var last = roots.last
+               for r in roots do
+                       o.write display(r)
+                       o.write "\n"
+                       sub_pretty(o, r, "")
+               end
+       end
+
+       private fun sub_pretty(o: OStream, e: E, prefix: String)
+       do
+               if not sub.has_key(e) then return
+               var subs = sub[e]
+               var last = subs.last
+               for e2 in subs do
+                       if e2 != last then
+                               o.write "{prefix}|--{display(e2)}\n"
+                               sub_pretty(o, e2, prefix+"|  ")
+                       else
+                               o.write "{prefix}`--{display(e2)}\n"
+                               sub_pretty(o, e2, prefix+"   ")
+                       end
+               end
+       end
+
+       # Sort roots and other elements using a comparator method
+       # This method basically sorts roots then each group of children
+       fun sort_with(comparator: AbstractSorter[E])
+       do
+               comparator.sort(roots)
+               for a in sub.values do
+                       comparator.sort(a)
+               end
+       end
+
+       # How to display a specific element of the tree
+       # By defaut, uses `to_s`
+       #
+       # Subclasses should redefine this method to provide a specific output
+       fun display(e: E): String do return e.to_s
+end
index 05f8fc7..20a2179 100644 (file)
@@ -22,10 +22,16 @@ module privileges
 
 import opts
 
+# Class to manage user groups
 class UserGroup
+
+       # User name
        var user: String
+
+       # Group name
        var group: nullable String
 
+       # Drop privileges of a user and set his privileges back to default (program privileges)
        fun drop_privileges
        do
                var passwd = new Passwd.from_name(user)
index ab04618..b178386 100644 (file)
@@ -113,7 +113,7 @@ interface Collection[E]
                return nb
        end
 
-       # Return one the item of the collection
+       # Return the first item of the collection
        #
        #    assert [1,2,3].first                == 1
        fun first: E
@@ -151,6 +151,7 @@ interface Iterator[E]
 end
 
 # A collection that contains only one item.
+# Used to pass arguments by reference
 class Container[E]
        super Collection[E]
 
@@ -222,7 +223,7 @@ end
 
 # Abstract sets.
 #
-# Set contains contains only one element with the same value (according to ==).
+# Set is a collection without ducplicates (according to ==)
 #      var s: Set[String] = new ArraySet[String]
 #      var a = "Hello"
 #      var b = "Hel" + "lo"
index 5de71cd..073471e 100644 (file)
@@ -80,7 +80,7 @@ abstract class AbstractArrayRead[E]
                return -1
        end
 
-       # The index of the last occurrence of an element starting from pos.
+       # The index of the first occurrence of an element starting from pos, by decremanting the index
        # Return -1 if not found.
        fun last_index_of_from(item: E, pos: Int): Int
        do
index eb3aef3..818813a 100644 (file)
@@ -73,7 +73,7 @@ class Range[E: Discrete]
        end
 end
 
-class IteratorRange[E: Discrete]
+private class IteratorRange[E: Discrete]
        # Iterator on ranges.
        super Iterator[E]
        var _range: Range[E]
index 9114ff1..239f785 100644 (file)
@@ -251,6 +251,14 @@ redef class String
                end
        end
 
+       # Return the canonicalized absolute pathname (see POSIX function `realpath`)
+       fun realpath: String do
+               var cs = to_cstring.file_realpath
+               var res = cs.to_s_with_copy
+               # cs.free_malloc # FIXME memory leak
+               return res
+       end
+
        # Simplify a file path by remove useless ".", removing "//", and resolving ".."
        # ".." are not resolved if they start the path
        # starting "/" is not removed
@@ -261,9 +269,10 @@ redef class String
        #  * the validity of the path is not checked
        #
        #     assert "some/./complex/../../path/from/../to/a////file//".simplify_path        ==  "path/to/a/file"
-       #     assert "../dir/file".simplify_path      ==  "../dir/file"
-       #     assert "dir/../../".simplify_path      ==  ".."
-       #     assert "//absolute//path/".simplify_path      ==  "/absolute/path"
+       #     assert "../dir/file".simplify_path       ==  "../dir/file"
+       #     assert "dir/../../".simplify_path        ==  ".."
+       #     assert "dir/..".simplify_path            ==  "."
+       #     assert "//absolute//path/".simplify_path ==  "/absolute/path"
        fun simplify_path: String
        do
                var a = self.split_with("/")
@@ -277,6 +286,7 @@ redef class String
                        end
                        a2.push(x)
                end
+               if a2.is_empty then return "."
                return a2.join("/")
        end
 
@@ -360,6 +370,7 @@ redef class NativeString
        private fun file_mkdir: Bool is extern "string_NativeString_NativeString_file_mkdir_0"
        private fun file_delete: Bool is extern "string_NativeString_NativeString_file_delete_0"
        private fun file_chdir is extern "string_NativeString_NativeString_file_chdir_0"
+       private fun file_realpath: NativeString is extern "file_NativeString_realpath"
 end
 
 extern FileStat `{ struct stat * `}
index f34769d..6d7967f 100644 (file)
@@ -50,6 +50,7 @@ extern int string_NativeString_NativeString_file_delete_0(char *f);
 #define string_NativeString_NativeString_file_mkdir_0(p) (mkdir(p, 0777))
 #define string_NativeString_NativeString_file_getcwd_0(p) (getcwd(NULL, 0))
 #define string_NativeString_NativeString_file_chdir_0(p) (chdir(p)?-1:0) /* hack to avoid warn_unused_result */
+#define file_NativeString_realpath(p) (realpath(p, NULL))
 
 #define file_stdin_poll_in(self) file_stdin_poll_in_()
 int file_stdin_poll_in_(void);
index 5166943..6f19e2c 100644 (file)
@@ -1,6 +1,16 @@
 # gtksourceview (gedit and other GTK editors)
 To install in your home, just link (or copy) the language definition file in ~/.local/share/gtksourceview-2.0/language-specs
 
+# syntaxhighlighter
+
+Nit brush for the Alex Gorbatchev's JS syntaxhighlighter.
+
+To install the JS syntaxhighlighter, please refer to http://alexgorbatchev.com/SyntaxHighlighter/
+
+Then can add the brush to your html page:
+
+       <script type="text/javascript" src="shBrushNit.js"></script>
+
 # vim
 
 vim is a powerful text editor.
diff --git a/misc/syntaxhighlighter/shBrushNit.js b/misc/syntaxhighlighter/shBrushNit.js
new file mode 100644 (file)
index 0000000..e9d1f78
--- /dev/null
@@ -0,0 +1,52 @@
+/* This file is part of NIT ( http://www.nitlanguage.org ).
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Nit Brush for SyntaxHighlighter
+ * see http://alexgorbatchev.com/SyntaxHighlighter
+ */
+
+;(function()
+{
+       // CommonJS
+       typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+       function Brush()
+       {
+               var keywords =  'abort abstract and as assert break class continue do else end enum extern false for fun' +
+                                               'if import in init interface intrude is isa isset label loop module new null nullable not' +
+                                               'once or protected private redef return self super then type true universal var' +
+                                               'when while writable';
+               var builtins =  'exit sys args get_time getc getcwd gets print printn';
+
+               this.regexList = [
+                       { regex: SyntaxHighlighter.regexLib.singleLinePerlComments,     css: 'comments' },      // one line comments
+                       { regex: SyntaxHighlighter.regexLib.doubleQuotedString,         css: 'string' },        // double quoted strings
+                       { regex: SyntaxHighlighter.regexLib.singleQuotedString,         css: 'string' },        // single quoted strings
+                       { regex: /\b[A-Z0-9_]+\b/g,                                                                     css: 'constants' },     // types
+                       { regex: /[A-Z][A-Za-z0-9_]*/g,                                                         css: 'color2' },        // classes
+                       { regex: new RegExp(this.getKeywords(keywords), 'gm'),          css: 'keyword' },       // keywords
+                       { regex: new RegExp(this.getKeywords(builtins), 'gm'),          css: 'color3' }         // builtins
+               ];
+               this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+       };
+
+       Brush.prototype = new SyntaxHighlighter.Highlighter();
+       Brush.aliases   = ['nit'];
+
+       SyntaxHighlighter.brushes.Nit = Brush;
+
+       // CommonJS
+       typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
+
index 37f4104..5b5782c 100644 (file)
@@ -49,6 +49,9 @@ Nitdoc.GitHub.UI = function() {
 \r
                // parse origin\r
                var parts = upstream.split(":");\r
+               if(parts.length < 3) {\r
+                       console.error("Incorrect upstream name `" + upstream + "`, should be of the form user:repo:branch");\r
+               }\r
                origin = {\r
                        user: parts[0],\r
                        repo: parts[1],\r
@@ -572,7 +575,7 @@ Nitdoc.GitHub.API = function() {
                                base_tree: baseTree.sha,\r
                                tree: [{\r
                                        path: path,\r
-                                       mode: 100644, // file (blob)\r
+                                       mode: "100644", // file (blob)\r
                                        type: "blob",\r
                                        sha: blob.sha\r
                                }]\r
@@ -740,10 +743,6 @@ Nitdoc.GitHub.LoginBox = function() {
                        .addClass("nitdoc-github-loginbox-arrow")\r
                        .append("&nbsp;")\r
                )\r
-               .append(\r
-                       $(document.createElement("h3"))\r
-                       .append("Github Sign In")\r
-               )\r
                .append(loginBoxContent);\r
 \r
                loginBoxLi.append(loginBox);\r
@@ -754,6 +753,10 @@ Nitdoc.GitHub.LoginBox = function() {
        var displayLogout = function(origin, user) {\r
                var panel = $(document.createElement("div"))\r
                .append(\r
+                       $(document.createElement("h3"))\r
+                       .append("Signed in Github")\r
+               )\r
+               .append(\r
                        $(document.createElement("h4"))\r
                        .append("Hello ")\r
                        .append(\r
@@ -765,30 +768,32 @@ Nitdoc.GitHub.LoginBox = function() {
                .append(\r
                        $(document.createElement("label"))\r
                        .attr("for", "github-origin")\r
-                       .append("Origin")\r
+                       .append("Upstram branch")\r
                )\r
                .append(\r
-                       $(document.createElement("input"))\r
+                       $(document.createElement("a"))\r
+                       .addClass("nitdoc-github-loginbox-githublink")\r
                        .attr({\r
-                               id: "github-origin",\r
-                               type: "text",\r
-                               disabled: "disabled",\r
-                               value: origin.user + ":" + origin.repo + ":" + origin.branch\r
+                               title: "Open branch in GitHub",\r
+                               href: "https://github.com/" + origin.user + "/" + origin.repo + "/tree/" + origin.branch,\r
+                               target: "_blank"\r
                        })\r
+                       .append(origin.user + ":" + origin.repo + ":" + origin.branch)\r
                )\r
                .append(\r
                        $(document.createElement("label"))\r
                        .attr("for", "github-base")\r
-                       .append("Base")\r
+                       .append("Your branch")\r
                )\r
                .append(\r
-                       $(document.createElement("input"))\r
+                       $(document.createElement("a"))\r
+                       .addClass("nitdoc-github-loginbox-githublink")\r
                        .attr({\r
-                               id: "github-base",\r
-                               type: "text",\r
-                               disabled: "disabled",\r
-                               value: user.login + ":" + user.repo + ":" + user.branch\r
+                               title: "Open branch in GitHub",\r
+                               href: "https://github.com/" + user.login + "/" + user.repo + "/tree/" + user.branch,\r
+                               target: "_blank"\r
                        })\r
+                       .append(origin.user + ":" + origin.repo + ":" + origin.branch)\r
                )\r
                .append(\r
                        $(document.createElement("button"))\r
@@ -812,6 +817,10 @@ Nitdoc.GitHub.LoginBox = function() {
        var displayLogin = function() {\r
                var panel = $(document.createElement("form"))\r
                .append(\r
+                       $(document.createElement("h3"))\r
+                       .append("Sign in Github")\r
+               )\r
+               .append(\r
                        $(document.createElement("label"))\r
                        .attr("for", "nitdoc-github-login-field")\r
                        .append("Username")\r
@@ -971,7 +980,7 @@ Nitdoc.GitHub.CommentBox.prototype.open = function(baseArea) {
                $(document.createElement("button"))\r
                .addClass("nitdoc-github-button")\r
                .addClass("nitdoc-github-commit")\r
-               .append("Commit")\r
+               .append("Commit...")\r
                .click(function() {\r
                        instance.infos.newComment = tarea.val();\r
                        instance.infos.commentBox = instance;\r
index 3c7179b..c4cd574 100644 (file)
        margin-bottom: 20px;\r
 }\r
 \r
+#nitdoc-github-loginbox a.nitdoc-github-loginbox-githublink {\r
+       display: block;\r
+       margin: 10px;\r
+       color: #0D8921;\r
+}\r
+\r
 /* \r
  * Nitdoc Github buttons\r
  */\r
index 0f71dff..4705f08 100644 (file)
@@ -50,6 +50,8 @@ redef class ToolContext
        var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other")
        # --typing-test-metrics
        var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
+       # --stack-trace-C-to-Nit-name-binding
+       var opt_stacktrace: OptionBool = new OptionBool("Enables the use of gperf to bind C to Nit function names when encountering a Stack trace at runtime", "--nit-stacktrace")
 
        redef init
        do
@@ -57,6 +59,7 @@ redef class ToolContext
                self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other)
                self.option_context.add_option(self.opt_typing_test_metrics)
+               self.option_context.add_option(self.opt_stacktrace)
        end
 end
 
@@ -111,6 +114,9 @@ redef class ModelBuilder
                if compile_dir == null then compile_dir = ".nit_compile"
 
                compile_dir.mkdir
+
+               if self.toolcontext.opt_stacktrace.value then compiler.build_c_to_nit_bindings
+
                var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd
 
                var outname = self.toolcontext.opt_output.value
@@ -256,6 +262,9 @@ end
 abstract class AbstractCompiler
        type VISITOR: AbstractCompilerVisitor
 
+       # Table corresponding c_names to nit names (methods)
+       var names = new HashMap[String, String]
+
        # The main module of the program currently compiled
        # Is assigned during the separate compilation
        var mainmodule: MModule writable
@@ -306,6 +315,40 @@ abstract class AbstractCompiler
 
        private var provided_declarations = new HashMap[String, String]
 
+       # Builds the .c and .h files to be used when generating a Stack Trace
+       # Binds the generated C function names to Nit function names
+       fun build_c_to_nit_bindings
+       do
+               var compile_dir = modelbuilder.toolcontext.opt_compile_dir.value
+               if compile_dir == null then compile_dir = ".nit_compile"
+
+               var stream = new OFStream.open("{compile_dir}/C_fun_names")
+               stream.write("%\{\n#include \"c_functions_hash.h\"\n%\}\n")
+               stream.write("%define lookup-function-name get_nit_name\n")
+               stream.write("struct C_Nit_Names;\n")
+               stream.write("%%\n")
+               stream.write("####\n")
+               for i in names.keys do
+                       stream.write(i)
+                       stream.write(",\t\"")
+                       stream.write(names[i])
+                       stream.write("\"\n")
+               end
+               stream.write("####\n")
+               stream.write("%%\n")
+               stream.close
+
+               stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
+               stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
+               stream.write("const struct C_Nit_Names* get_nit_name(register const char *str, register unsigned int len);\n")
+               stream.close
+
+               var x = new Process("gperf","{compile_dir}/C_fun_names","-t","-7","--output-file={compile_dir}/c_functions_hash.c","-C")
+               x.wait
+
+               extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
+       end
+
        # Compile C headers
        # This method call compile_header_strucs method that has to be refined
        fun compile_header do
@@ -314,6 +357,9 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
+               if modelbuilder.toolcontext.opt_stacktrace.value then
+                       self.header.add_decl("#include \"c_functions_hash.h\"")
+               end
                self.header.add_decl("#include <libunwind.h>")
                self.header.add_decl("#include <signal.h>")
                self.header.add_decl("#include <gc_chooser.h>")
@@ -365,11 +411,20 @@ abstract class AbstractCompiler
                v.add_decl("unw_getcontext(&uc);")
                v.add_decl("unw_init_local(&cursor, &uc);")
                v.add_decl("printf(\"-------------------------------------------------\\n\");")
-               v.add_decl("printf(\"-- C Stack Trace   ------------------------------\\n\");")
+               v.add_decl("printf(\"--   Stack Trace   ------------------------------\\n\");")
                v.add_decl("printf(\"-------------------------------------------------\\n\");")
                v.add_decl("while (unw_step(&cursor) > 0) \{")
                v.add_decl("    unw_get_proc_name(&cursor, procname, 100, &ip);")
+               if modelbuilder.toolcontext.opt_stacktrace.value then
+               v.add_decl("    const C_Nit_Names* recv = get_nit_name(procname, strlen(procname));")
+               v.add_decl("    if (recv != 0)\{")
+               v.add_decl("            printf(\"` %s\\n\", recv->nit_name);")
+               v.add_decl("    \}else\{")
+               v.add_decl("            printf(\"` %s\\n\", procname);")
+               v.add_decl("    \}")
+               else
                v.add_decl("    printf(\"` %s \\n\",procname);")
+               end
                v.add_decl("\}")
                v.add_decl("printf(\"-------------------------------------------------\\n\");")
                v.add_decl("free(procname);")
index a5ebfc6..e3f4be7 100644 (file)
@@ -38,19 +38,4 @@ redef class ToolContext
                phases.add_edge(scope_phase, simple_misc_analysis_phase)
                return true
        end
-
-       fun run_global_phases(mainmodule: MModule)
-       do
-               for phase in phases_list do
-                       phase.process_mainmodule(mainmodule)
-               end
-       end
 end
-
-redef class Phase
-       # Specific action to execute on the whole program
-       # Called by the `ToolContext::run_global_phases`
-       # @toimplement
-       fun process_mainmodule(mainmodule: MModule) do end
-end
-
index 8d0290d..6dc5295 100644 (file)
@@ -978,6 +978,7 @@ private class CustomizedRuntimeFunction
                        v.add("return {frame.returnvar.as(not null)};")
                end
                v.add("\}")
+               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
 
        redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
diff --git a/src/interpretor_type_test.nit b/src/interpretor_type_test.nit
deleted file mode 100644 (file)
index b023130..0000000
+++ /dev/null
@@ -1,1110 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2012 Alexandre Terrasa <alexandre@moz-concept.com>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Introduce incremental typing in naive interpreter
-# Typing modes are :
-# * Legacy: default mode, using request to precomputed metamodel
-# * BM: binary matrix
-# * CL: coloration
-# * PHAND: perfect hashing using binary AND
-# * PHMOD: perfect hashing using modulo
-module interpretor_type_test
-
-intrude import naive_interpreter
-
-# Add options for incremental typing
-redef class ToolContext
-       # --ic-typing-mode BM | CL | PHMOD | PHAND
-       var opt_ic_typing_mode = new OptionString("Incremental typing method. Possible values are:\n\t\t\t\t - BM: binary matrix\n\t\t\t\t - CL: coloration\n\t\t\t\t - PHAND: perfect hashing whith AND\n\t\t\t\t - PHMOD: perfect hashing whith MOD", "--ic-typing-mode")
-       # --ic-load-mode
-       var opt_ic_load_mode = new OptionString("Dynamic loading mode. Possible values are:\n\t\t\t\t - type (default): load only the new type\n\t\t\t\t - module: load the module of new type", "--ic-load-mode")
-       # --ic-bootstrap
-       var opt_ic_bootstrap = new OptionBool("Bootstrap typing with standard library (default is no bootstrap)", "--ic-bootstrap")
-       # --ic-recompute-mode always | never | threshold | increment
-       var opt_ic_recompute_mode = new OptionString("When to recompute typing. Possible values are:\n\t\t\t\t - always: recompute for each new type\n\t\t\t\t - never (default):  never recompute, use fallback instead\n\t\t\t\t - threshold:  recompute only when the threshold is reached, use fallback before\n\t\t\t\t - increment: try to update structures instead of recompute", "--ic-recompute-mode")
-       # --ic-threshold-mode increment | fallback
-       var opt_ic_threshold_mode = new OptionString("Threshold mode. Possible values are:\n\t\t\t\t - increment: recompute when the number of new type reach the threshold count\n\t\t\t\t -fallback: recompute when the use number of fallback use reach the threshold count", "--ic-threshold-mode")
-       # --ic-threshold-count <int>
-       var opt_ic_threshold_count = new OptionInt("Threshold count. Take an integer", 0, "--ic-threshold-count")
-       # --ic-cache-size <int>
-       var opt_ic_cache_size = new OptionInt("Cache size. Take an integer", 0, "--ic-cache-size")
-       # --ic-show-stats
-       var opt_ic_show_stats = new OptionBool("Show statistics about typing at the end of the interpretation", "--ic-show-stats")
-
-       redef init
-       do
-               super
-               self.option_context.add_option(self.opt_ic_typing_mode)
-               self.option_context.add_option(self.opt_ic_load_mode)
-               self.option_context.add_option(self.opt_ic_bootstrap)
-               self.option_context.add_option(self.opt_ic_recompute_mode)
-               self.option_context.add_option(self.opt_ic_threshold_mode)
-               self.option_context.add_option(self.opt_ic_threshold_count)
-               self.option_context.add_option(self.opt_ic_cache_size)
-               self.option_context.add_option(self.opt_ic_show_stats)
-       end
-end
-
-redef class ModelBuilder
-       # Add statistics display at the end of the interpretation
-       redef fun run_naive_interpreter(mainmodule, arguments) do
-               var time0 = get_time
-               self.toolcontext.info("*** START INTERPRETING ***", 1)
-
-               var interpreter = new NaiveInterpreter(self, mainmodule, arguments)
-               init_naive_interpreter(interpreter, mainmodule)
-
-               # stats
-               if interpreter.show_stats then
-                       print "# Interpretation statistics"
-
-                       print "## Dynamic loading"
-                       print " - nb dynamically lodaed types: {interpreter.typing.nb_loads} ({interpreter.typing.nb_gen_loads} were generic)"
-                       print " - nb dynamically lodaed modules: {interpreter.typing.nb_mod_loads}"
-
-                       print "## Typing structures"
-                       print " - nb increments: {interpreter.typing.nb_increment_loads} ({interpreter.typing.nb_gen_increment_loads} were generic)"
-                       print " - nb destructive increments: {interpreter.typing.nb_destructive_increments} ({interpreter.typing.nb_gen_destructive_increments} were generic)"
-                       print " - nb full structure recomputation: {interpreter.typing.nb_recomputes}"
-                       print " - overall max size table: {interpreter.typing.max_size_tables}"
-                       print " - total: size needed for tables: {interpreter.typing.sum_size_tables}"
-
-                       print "## Type test"
-                       print " - nb isa: {interpreter.typing.nb_isas} ({interpreter.typing.nb_gen_isas} involving generic types)"
-                       print " - nb isa using fallback: {interpreter.typing.nb_fallbacks} ({interpreter.typing.nb_gen_fallbacks} involving generic types)"
-                       print " - nb isa using fallback responded from cache: {interpreter.typing.nb_cache_res}"
-               end
-
-               var time1 = get_time
-               self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2)
-       end
-end
-
-# Add gestion handling for incremental typing mecanisms
-redef class NaiveInterpreter
-
-       var typing: Typing
-       var module_increment: Bool
-       var recompute_mode: String
-       var threshold_mode: String = "null"
-       var threshold_count: Int = 0
-       var cache_size: Int = 0
-       var show_stats: Bool = false
-
-       # Add option handling
-       redef init(modelbuilder, mainmodule, arguments) do
-               super(modelbuilder, mainmodule, arguments)
-
-               # choose typing method
-               var mode = modelbuilder.toolcontext.opt_ic_typing_mode.value
-               if mode == "BM" then
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using BM typing", 1)
-                       typing = new BMTyping(self)
-               else if mode == "CL" then
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using CL typing", 1)
-                       typing = new CLTyping(self)
-               else if mode == "PHMOD" then
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using PH-MOD typing", 1)
-                       typing = new PHModTyping(self)
-               else if mode == "PHAND" then
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using PH-AND typing", 1)
-                       typing = new PHAndTyping(self)
-               else
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using legacy typing", 1)
-                       typing = new LegacyTyping(self)
-               end
-
-               # load full module or juste the type ?
-               mode = modelbuilder.toolcontext.opt_ic_load_mode.value
-               if mode == "module" then
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using load mode: module", 1)
-                       module_increment = true
-               else
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using load mode: type", 1)
-                       module_increment = false
-               end
-
-               # bootstrap type structures with stdlib types
-               if modelbuilder.toolcontext.opt_ic_bootstrap.value then
-                       self.modelbuilder.toolcontext.info("IC-TYPING: Using bootstrap", 1)
-                       typing.bootstrap
-               end
-
-               # typing recomputation mode
-               var recompute_mode = modelbuilder.toolcontext.opt_ic_recompute_mode.value
-               if recompute_mode != null then
-                       self.recompute_mode = recompute_mode
-
-                       if recompute_mode == "threshold" then
-                               var threshold_mode = modelbuilder.toolcontext.opt_ic_threshold_mode.value
-                               if threshold_mode == null then
-                                       print "IC-ERROR : Recompute mode 'threshold' need a threshold mode"
-                                       abort
-                               else
-                                       self.modelbuilder.toolcontext.info("IC-TYPING: Using threshold mode: {threshold_mode}", 1)
-                                       self.threshold_mode = threshold_mode
-                                       var threshold_count = modelbuilder.toolcontext.opt_ic_threshold_count.value
-                                       if threshold_count == 0 then
-                                               print "IC-ERROR : Recompute mode 'threshold' need a threshold count"
-                                               abort
-                                       else
-                                               self.threshold_count = threshold_count
-                                               self.modelbuilder.toolcontext.info("IC-TYPING: Using threshold value: {threshold_count}", 1)
-                                       end
-                               end
-                       end
-               else
-                       self.recompute_mode = "never"
-               end
-               self.modelbuilder.toolcontext.info("IC-TYPING: Using recompute mode: {self.recompute_mode}", 1)
-
-               # cache size
-               cache_size = modelbuilder.toolcontext.opt_ic_cache_size.value
-               self.modelbuilder.toolcontext.info("IC-TYPING: Using cache size: {cache_size}", 1)
-
-               # stats
-               if modelbuilder.toolcontext.opt_ic_show_stats.value then show_stats = true
-       end
-
-       # Handle subtypes tests and redispatch to selected mode
-       redef fun is_subtype(sub, sup) do
-               self.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: {sub} ISA {sup}", 2)
-
-               # stats
-               typing.nb_isas += 1
-               if sup isa MGenericType or sub isa MGenericType then typing.nb_gen_isas += 1
-
-               # special case of nullables types
-               if sub isa MNullType and sup isa MNullableType then
-                       return true
-               else if sub isa MNullType and not sup isa MNullableType then
-                       return false
-               end
-               if sub isa MNullableType then sub = sub.mtype
-               if sup isa MNullableType then sup = sup.mtype
-
-               # std case
-               var t1 = typing.load_increment(sub.as(MClassType))
-               var t2 = typing.load_increment(sup.as(MClassType))
-
-               var res: Bool
-               if typing.is_valid then
-                       res = t1.is_subtype(t2)
-               else
-                       # recompute mode = thresold
-                       if recompute_mode == "threshold" then
-                               if threshold_mode == "fallback" then
-                                       typing.increment_count += 1
-                                       # recompute instead fallback
-                                       if typing.increment_count > threshold_count then
-                                               typing.compute_typing
-                                               typing.increment_count = 0
-                                               return t1.is_subtype(t2)
-                                       end
-                               end
-                       end
-                       res = t1.is_subtype_fallback(t2)
-               end
-               return res
-       end
-end
-
-# Incremental typing
-
-# The base of incremental typing
-private abstract class Typing
-       type T: Type
-
-       # List of already loaded types
-       var loaded_types: HashMap[MType, T] = new HashMap[MType, T]
-       # List of already loaded modules
-       var loaded_modules: HashSet[MModule] = new HashSet[MModule]
-       # Last type ID used
-       var lastID: Int = -1
-       # Is the typing still valid (no destructive increment)
-       var is_valid: Bool = false
-       # Increment count since the last full recomputation
-       var increment_count = 0
-
-       private var interpreter: NaiveInterpreter
-
-       # stats
-       var nb_loads = 0
-       var nb_gen_loads = 0
-       var nb_mod_loads = 0
-       var nb_increment_loads = 0
-       var nb_gen_increment_loads = 0
-       var nb_destructive_increments = 0
-       var nb_gen_destructive_increments = 0
-       var nb_isas = 0
-       var nb_gen_isas = 0
-       var nb_recomputes = 0
-       var nb_fallbacks = 0
-       var nb_gen_fallbacks = 0
-       var nb_cache_res = 0
-       var max_size_tables = 0
-       var sum_size_tables = 0
-
-       private init(interpreter: NaiveInterpreter) do
-               self.interpreter = interpreter
-       end
-
-       # Load the std lib types and compute typing
-       fun bootstrap do
-               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: bootstrap start", 2)
-               var mods = interpreter.mainmodule.model.get_mmodules_by_name("standard")
-               if mods != null then
-                       var std = mods.first
-                       for m in std.in_nesting.direct_greaters do
-                               load_module(m)
-                       end
-               end
-               compute_typing
-               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: bootstrap end", 2)
-       end
-
-       # Load all types contained in a module
-       fun load_module(mmodule: MModule) do
-               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: load module {mmodule}", 2)
-               # import nested module first
-               for m in mmodule.in_nesting.greaters do
-                       if mmodule == m then continue
-                       load_module(m)
-               end
-
-               # load classes in module
-               if loaded_modules.has(mmodule) then return
-
-               # stats
-               nb_mod_loads += 1
-
-               for c in mmodule.intro_mclasses do
-                       # skip unanchored types (they will be handled by load_supertypes of anchored types
-                       if c.mclass_type.need_anchor then continue
-                       load_type(c.mclass_type)
-               end
-               loaded_modules.add(mmodule)
-       end
-
-       # Load a MType and return its Type representation
-       fun load_type(mtype: MClassType): T do
-               if is_loaded(mtype) then
-                       return loaded_types[mtype]
-               else
-                       # stats
-                       nb_loads += 1
-                       if mtype isa MGenericType then nb_gen_loads += 1
-
-                       var supertypes = load_supertypes(mtype)
-                       var t = init_type(lastID + 1, mtype, supertypes, self)
-                       lastID = t.id
-                       loaded_types[mtype] = t
-
-                       # load formal parameter types
-                       if mtype isa MGenericType then
-                               for arg in mtype.arguments do
-                                       if arg isa MClassType then load_type(arg)
-                               end
-                       end
-
-                       return t
-               end
-       end
-
-       # Load compile time known super-types of a type
-       # supertypes are all known super classes at compile time and unresolved generic classes
-       fun load_supertypes(child: MClassType): Array[T] do
-               var parents = child.mclass.in_hierarchy(interpreter.mainmodule).greaters
-               var plist = new Array[T]
-
-               # load each parent
-               for p in parents do
-                       # not a parent but the type itself
-                       if p.mclass_type.mclass == child.mclass then continue
-
-                       if p.mclass_type.need_anchor then
-                               # the parent need to be anchored to the child (contains a formal type)
-                               var anchor = p.mclass_type.anchor_to(interpreter.mainmodule, child)
-                               plist.add(load_type(anchor))
-                       else
-                               # already revolved type
-                               plist.add(load_type(p.mclass_type))
-                       end
-               end
-               return plist
-       end
-
-       # Increment types structures with the new type
-       fun load_increment(mtype: MClassType): Type do
-               # old type, no need of recomputation
-               if is_loaded(mtype) then
-                       return load_type(mtype)
-               else
-                       # current structures are expired, a new type appeared
-                       is_valid = false
-               end
-
-               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: load increment {mtype}", 2)
-
-               # load the module
-               if interpreter.module_increment then
-                       load_module(mtype.mclass.intro_mmodule)
-               end
-
-               var t = load_type(mtype)
-
-               # stats
-               nb_increment_loads += 1
-               if mtype isa MGenericType then nb_gen_increment_loads += 1
-
-               # recompute mode = always
-               if interpreter.recompute_mode == "always" then
-                       compute_typing
-               end
-
-               # recompute mode = thresold
-               if interpreter.recompute_mode == "threshold" then
-                       if interpreter.threshold_mode == "increment" then
-                               increment_count += 1
-
-                               if increment_count > interpreter.threshold_count then
-                                       compute_typing
-                                       increment_count = 0
-                               end
-                       end
-               end
-
-               # recompute mode = increment
-               if interpreter.recompute_mode == "increment" then
-                       compute_increment(t)
-                       if not is_valid then compute_typing
-               end
-
-               return t
-       end
-
-       # Factory for specializations of Type
-       fun init_type(id: Int, mtype: MClassType, supertypes: Array[T], typing: Typing): T is abstract
-
-       # Is the type already loaded ?
-       fun is_loaded(mtype: MType): Bool do return loaded_types.has_key(mtype)
-
-       # Run the typing full computation
-       # Do nothing, need to be redefined
-       fun compute_typing do
-               nb_recomputes += 1
-               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: compute typing", 1)
-               is_valid = true
-       end
-
-       # Compute typing with the increment
-       # if the increment is destructive, need to recompute the whole typing structure
-       # else just compute typing structure for the new type
-       fun compute_increment(t: T) is abstract
-
-       # Is the increment destructive ?
-       # destructive increment depends on typing method used
-       fun is_destructive_increment(t: T): Bool is abstract
-end
-
-# An incremental typing type representation
-private abstract class Type
-       type T: Type
-
-       # The unique type id
-       var id: Int
-       # all resolved super-types
-       var supertypes: Array[T]
-
-       var mtype: MClassType
-       var typing: Typing
-       var cache: Cache
-
-       init(id: Int, mtype: MClassType, supertypes: Array[T], typing: Typing) do
-               self.id = id
-               self.mtype = mtype
-               self.supertypes = supertypes
-               self.typing = typing
-               self.cache = new Cache(typing.interpreter.cache_size)
-               self.supertypes.add(self)
-       end
-
-       # Subtyping using computed representation
-       fun is_subtype(sup: T): Bool is abstract
-
-       # Subtyping for uncomputed types
-       # unefficient algorithme using linear probing in known super-types
-       fun is_subtype_fallback(sup: T): Bool do
-               # stats
-               typing.nb_fallbacks += 1
-               if self.mtype isa MGenericType or sup.mtype isa MGenericType then typing.nb_gen_fallbacks += 1
-
-               # try in cache cache
-               if cache.contains(sup) then
-                       typing.nb_cache_res += 1
-                       return cache.response(sup)
-               end
-
-               # test implies generic typing
-               if sup.mtype isa MGenericType then
-                       for p in supertypes do
-                               if p == sup then
-                                       # found the same type (ie. p = B[Int] = sup)
-                                       if not cache.contains(sup) then cache.add(sup, true)
-                                       return true
-                               else if p.mtype.mclass == sup.mtype.mclass then
-                                       # found the same class (ie. p.mclass = B[B#0] = sup.mclass)
-                                       # compare formal types arguments
-                                       for i in [0..p.mtype.arguments.length[ do
-                                               # erase nullable annotation of p arg
-                                               var sarg = p.mtype.arguments[i]
-                                               if sarg isa MNullableType then sarg = sarg.mtype
-                                               var sft = typing.load_type(sarg.as(MClassType))
-                                               # erase nullable annotation of super arg
-                                               var suparg = sup.mtype.arguments[i]
-                                               if suparg isa MNullableType then suparg = suparg.mtype
-                                               var pft = typing.load_type(suparg.as(MClassType))
-                                               if not sft.is_subtype_fallback(pft) then
-                                                       if not cache.contains(sup) then cache.add(sup, false)
-                                                       return false
-                                               end
-                                       end
-                                       if not cache.contains(sup) then cache.add(sup, true)
-                                       return true
-                               end
-                       end
-                       if not cache.contains(sup) then cache.add(sup, false)
-                       return false
-               end
-
-               # for non generic type: look up in supertypes
-               # 0(n) where n = nb supertypes of the current type
-               for p in supertypes do
-                       if p == sup then
-                               if not cache.contains(sup) then cache.add(sup, true)
-                               return true
-                       end
-               end
-               if not cache.contains(sup) then cache.add(sup, false)
-               return false
-       end
-
-       redef fun to_s do return "{mtype.to_s} (id: {id})"
-end
-
-# Binary Matrix (BM) typing
-private class BMTyping
-       super Typing
-
-       redef type T: BMType
-
-       init(interpreter: NaiveInterpreter) do super(interpreter)
-
-       redef fun init_type(id, mtype, supertypes, typing) do return new BMType(id, mtype, supertypes, typing)
-
-       # Compute rows in the binary matrix for each loaded type
-       redef fun compute_typing do
-               super
-               for t in loaded_types.values do
-                       t.compute_row(loaded_types, interpreter.mainmodule)
-               end
-       end
-
-       # If the increment is destructive, the typing representation is invalidated
-       # else add a row to the matrix and compute it
-       redef fun compute_increment(t) do
-               #print "DEBUG: compute increment {t}: is destructive = {is_destructive_increment(t)}"
-
-               # compute increments of supertypes first
-               for st in t.supertypes do
-                       if t == st then continue
-                       if st.row == null then compute_increment(st)
-               end
-
-               if is_destructive_increment(t) then
-                       # increment is destructive, need to recompute the whole typing structure
-                       is_valid = false
-               else
-                       # increment is additive, compute only the type (and newly added parents)
-                       is_valid = true
-                       t.compute_row(loaded_types, interpreter.mainmodule)
-               end
-       end
-
-       # For BM typing, destructive increments are only supertypes of already computed types
-       # This appends only when a new generic type on an already computed generic class appears
-       # ie. increment is GenericType[Something] and loaded-types already contains GenericType[SomethingElse]
-       redef fun is_destructive_increment(t) do
-               if t.mtype isa MGenericType then
-                       for ot in loaded_types.values do
-                               if ot.mtype.mclass == t.mtype.mclass then
-                                       nb_destructive_increments += 1
-                                       nb_gen_destructive_increments += 1
-                                       return true
-                               end
-                       end
-               end
-               return false
-       end
-end
-
-private class BMType
-       super Type
-
-       redef type T: BMType
-
-       # A row is an array of bits
-       var row: nullable Array[Bool]
-
-       # For each other types, fill the cell with 1 if subtype else 0
-       fun compute_row(types: HashMap[MType, T], mainmodule: MModule) do
-               row = new Array[Bool].filled_with(false, types.length)
-               for t in types.values do
-                       if self.is_subtype_fallback(t) then
-                               row[t.id] = true
-                       end
-               end
-
-               # stats
-               if row.length > typing.max_size_tables then typing.max_size_tables = row.length
-               typing.sum_size_tables += row.length
-       end
-
-       # O(1)
-       # return self.row[t.id]
-       redef fun is_subtype(t) do
-               if t.id >= self.row.length then return false
-               return self.row[t.id]
-       end
-end
-
-# Perfect Hashing (PH) typing
-private abstract class PHTyping
-       super Typing
-
-       redef type T: PHType
-
-       init(interpreter: NaiveInterpreter) do
-               super(interpreter)
-               lastID = 0
-       end
-
-       redef fun compute_typing do
-               super
-               for t in loaded_types.values do
-                       t.compute_row(loaded_types, interpreter.mainmodule)
-               end
-       end
-
-       # If the increment is destructive, the typing representation is invalidated
-       # else compute the hashmap of the type
-       redef fun compute_increment(t) do
-               # compute increments of supertypes first
-               for st in t.supertypes do
-                       if t == st then continue
-                       if st.row == null then compute_increment(st)
-               end
-
-               if is_destructive_increment(t) then
-                       # increment is destructive, need to recompute the whole typing structure
-                       is_valid = false
-               else
-                       # increment is additive, compute only the type (and newly added parents)
-                       is_valid = true
-                       t.compute_row(loaded_types, interpreter.mainmodule)
-               end
-       end
-
-       # For HP typing, destructive increments are only supertypes of already computed types
-       # This appends only when a new generic type on an already computed generic class appears
-       # ie. increment is GenericType[Something] and loaded-types already contains GenericType[SomethingElse]
-       redef fun is_destructive_increment(t) do
-               if t.mtype isa MGenericType then
-                       for ot in loaded_types.values do
-                               if ot.mtype.mclass == t.mtype.mclass then
-                                       # stats
-                                       nb_destructive_increments += 1
-                                       nb_gen_destructive_increments += 1
-                                       return true
-                               end
-                       end
-               end
-               return false
-       end
-end
-
-private abstract class PHType
-       super Type
-
-       redef type T: PHType
-
-       # Mask is used to hash an id
-       var mask: nullable Int
-       # A row is an array of IDs (Int)
-       var row: nullable Array[Int]
-
-       # First compute the mask needed to perfectly hash the parents list
-       # Then fill the hashmap
-       fun compute_row(types: HashMap[MType, T], mainmodule: MModule) do
-               # Create super type list
-               var stypes = new List[Type]
-               for st in types.values do
-                       if self.is_subtype_fallback(st) then stypes.add(st)
-               end
-
-               # Compute the hashing 'mask'
-               self.mask = compute_mask(stypes)
-
-               # Fill the row
-               row = new Array[Int]
-               for st in stypes do
-                       var idx = phash(st.id)
-                       if idx > row.length then for i in [row.length .. idx[ do row[i] = 0
-                       row[idx] = st.id
-               end
-
-               # stats
-               if row.length > typing.max_size_tables then typing.max_size_tables = row.length
-               typing.sum_size_tables += row.length
-       end
-
-       # Hash IDs
-       fun phash(id: Int): Int is abstract
-
-       # O(1)
-       # return self.row[phash(t.id)] == t.id
-       redef fun is_subtype(t) do
-               if self.row.length <= phash(t.id) then return false
-               return self.row[phash(t.id)] == t.id
-       end
-
-       fun compute_mask(stypes: List[Type]): Int is abstract
-end
-
-private class PHModTyping
-       super PHTyping
-       redef type T: PHModType
-       redef fun init_type(id, mtype, supertypes, typing) do return new PHModType(id, mtype, supertypes, typing)
-end
-
-private class PHModType
-       super PHType
-
-       redef type T: PHModType
-
-       # Hash using modulo
-       # return mask % id
-       redef fun phash(id: Int): Int do return mask.as(not null) % id
-
-       # Look for the first mask allowing perfect hashing for stypes ids
-       # TODO: Look for a more efficien way to do this
-       redef fun compute_mask(stypes: List[Type]): Int do
-               var mask = 0
-               loop
-                       var used = new List[Int]
-                       for st in stypes do
-                               var res = (mask % st.id)
-                               if used.has(res) then
-                                       break
-                               else
-                                       used.add(res)
-                               end
-                       end
-                       if used.length == stypes.length then break
-                       mask += 1
-               end
-               return mask
-       end
-end
-
-private class PHAndTyping
-       super PHTyping
-       redef type T: PHAndType
-       redef fun init_type(id, mtype, supertypes, typing) do return new PHAndType(id, mtype, supertypes, typing)
-end
-
-private class PHAndType
-       super PHType
-
-       redef type T: PHAndType
-
-       # Hash using binary and
-       # return mask & id
-       redef fun phash(id: Int): Int do return mask.as(not null).bin_and(id)
-
-       # Look for the first mask allowing perfect hashing for stypes ids
-       # TODO: Look for a more efficien way to do this
-       redef fun compute_mask(stypes: List[Type]): Int do
-               var mask = 0
-               loop
-                       var used = new List[Int]
-                       for st in stypes do
-                               var res = (mask.bin_and(st.id))
-                               if used.has(res) then
-                                       break
-                               else
-                                       used.add(res)
-                               end
-                       end
-                       if used.length == stypes.length then break
-                       mask += 1
-               end
-               return mask
-       end
-end
-
-# Coloration (CL) typing
-private class CLTyping
-       super Typing
-
-       redef type T: CLType
-
-       # Core of type hierarchy (types with at least two supertypes and their indirect supertypes)
-       var core: Array[T] = new Array[T]
-       # All types in single inheritance that are not part of core
-       var crown: Array[T] = new Array[T]
-       # All minimal classes of core
-       var border: Array[T] = new Array[T]
-
-       # Conflicts graph of hierarchy
-       var conflict_graph: HashMap[T, HashSet[T]] = new HashMap[T, HashSet[T]]
-
-       # Max color used
-       # Increments are colored with injective colors
-       var max_color = 0
-
-       var sorter: TypeSorter = new TypeSorter
-
-       init(interpreter: NaiveInterpreter) do
-               super(interpreter)
-               lastID = 0
-       end
-
-       redef fun init_type(id, mtype, supertypes, typing) do return new CLType(id, mtype, supertypes, typing)
-
-       # Colorize all known types and fill the type tables
-       redef fun compute_typing do
-               super
-               compute_init
-               compute_conflicts
-               colorize(core)
-               colorize(border)
-               colorize(crown)
-               fill_tables
-       end
-
-       # Compute a linear extension of parents of each types and tag each type as core, border or crown
-       fun compute_init do
-               core.clear
-               crown.clear
-               border.clear
-
-               # clear already build tables
-               for t in loaded_types.values do
-                       #t.parents = new HashSet[T]
-                       t.nb_parents = 0
-                       t.sub_types = new HashSet[T]
-               end
-
-               # compute linear ext of each type type
-               for t in loaded_types.values do compute_linear_ext(t)
-
-               # tag each type as core, crown or border
-               for t in loaded_types.values do tag_type(t)
-
-               # sort by reverse linearization order
-               sorter.sort(core)
-               sorter.sort(crown)
-               sorter.sort(border)
-       end
-
-       # Compute linear extension of the type
-       fun compute_linear_ext(t: T) do
-               var lin = new Array[T]
-               for st in loaded_types.values do
-                       if t.is_subtype_fallback(st) then
-                               lin.add(st)
-                               t.nb_parents = t.mtype.mclass.in_hierarchy(interpreter.mainmodule).direct_greaters.length
-                               if t != st then st.sub_types.add(t)
-                       end
-               end
-               sorter.sort(lin)
-               t.lin = lin
-       end
-
-       # Tag type as core, crown or border
-       fun tag_type(t: T) do
-               if t.nb_parents > 1 then
-                       if t.sub_types.length <= 1 then
-                               # border
-                               border.add(t)
-                       else
-                               # core
-                               core.add(t)
-                       end
-                       for st in t.lin do if t != st and not core.has(st) then core.add(st)
-               else
-                       # crown
-                       var subtypes_are_si = true
-                       for sub in t.sub_types do if sub.nb_parents > 1 then subtypes_are_si = false
-                       if subtypes_are_si then crown.add(t)
-               end
-       end
-
-       # Compute related classes to detect coloring conflicts
-       fun compute_conflicts do
-               conflict_graph.clear
-               var mi_types = new Array[T]
-               mi_types.add_all(core)
-               mi_types.add_all(border)
-               sorter.sort(mi_types)
-
-               for t in mi_types do
-                       for i in t.lin do
-                               if i == t then continue
-                               var lin_i = i.lin
-
-                               for j in t.lin do
-                                       if i == j or j == t then continue
-                                       var lin_j = j.lin
-
-                                       var d_ij = lin_i - lin_j
-                                       var d_ji = lin_j - lin_i
-
-                                       for ed1 in d_ij do
-                                               if not conflict_graph.has_key(ed1) then conflict_graph[ed1] = new HashSet[T]
-                                               # add ed1 x ed2 to conflicts graph
-                                               for ed2 in d_ji do conflict_graph[ed1].add(ed2)
-                                       end
-                                       for ed1 in d_ij do
-                                               if not conflict_graph.has_key(ed1) then conflict_graph[ed1] = new HashSet[T]
-                                               # add ed1 x ed2 to conflicts graph
-                                               for ed2 in d_ji do conflict_graph[ed1].add(ed2)
-                                       end
-                               end
-                       end
-               end
-               self.conflict_graph = conflict_graph
-       end
-
-       # Colorize an array of types
-       fun colorize(elts: Array[T]) do
-               var min_color = 0
-
-               for t in elts do
-                       var color = min_color
-                       while not color_free(t, color) do
-                               color += 1
-                       end
-                       t.color = color
-                       if color > max_color then max_color = color
-                       color = min_color
-               end
-       end
-
-       # Check if a related type to t already use the color
-       fun color_free(t: T, color: Int): Bool do
-               if conflict_graph.has_key(t) then
-                       for st in conflict_graph[t] do if st.color == color then return false
-               end
-               for st in t.lin do
-                       if st == t then continue
-                       if st.color == color then return false
-               end
-               return true
-       end
-
-       # Fill de colored tables
-       fun fill_tables do for t in loaded_types.values do fill_table(t)
-
-       fun fill_table(t: T) do
-               t.row = new Array[Int]
-               for st in t.lin do
-                       if t.row.length <= st.color.as(not null) then
-                               for i in [t.row.length..st.color.as(not null)[ do t.row.add(0)
-                       end
-                       t.row[st.color.as(not null)] = st.id
-               end
-
-               # stats
-               if t.row.length > max_size_tables then max_size_tables = t.row.length
-               sum_size_tables += t.row.length
-       end
-
-       # Compute table for a type increment: compute linearization of parents and fill the table
-       fun compute_table_increment(t: T) do
-               compute_linear_ext(t)
-               fill_table(t)
-       end
-
-       redef fun compute_increment(t) do
-               #print "DEBUG: compute increment {t}: is destructive = {is_destructive_increment(t)}"
-
-               # injective color
-               max_color += 1
-               t.color = max_color
-
-               # parents first
-               for st in t.supertypes do
-                       if t == st then continue
-                       if st.row == null then compute_increment(st)
-               end
-
-               if is_destructive_increment(t) then
-                       is_valid = false
-               else
-                       # increment is additive, compute only the type (and newly added parents)
-                       is_valid = true
-                       compute_table_increment(t)
-               end
-       end
-
-       # For CL typing, destructive increments are supertypes of already computed types and common types of two already computed types
-       redef fun is_destructive_increment(t) do
-               if t.mtype isa MGenericType then
-                       for ot in loaded_types.values do
-                               if ot.mtype.mclass == t.mtype.mclass then
-                                       # stats
-                                       nb_destructive_increments += 1
-                                       nb_gen_destructive_increments += 1
-                                       return true
-                               end
-                       end
-               end
-
-               compute_linear_ext(t)
-               if t.nb_parents > 1 then
-                       # stats
-                       nb_destructive_increments += 1
-                       if t.mtype isa MGenericType then nb_gen_destructive_increments = 0
-                       return true
-               end
-
-               return false
-       end
-end
-
-private class CLType
-       super Type
-
-       redef type T: CLType
-
-       # Linearization of super-types including the type itself
-       var lin: Array[T] = new Array[T]
-       # The color assignated to the type
-       var color: nullable Int
-       # Number of direct super-types
-       var nb_parents: Int = 0
-       # All sub types
-       var sub_types: HashSet[T] = new HashSet[T]
-
-       # A row is the an array of IDs
-       var row: nullable Array[Int]
-
-       # O(1)
-       # return row[t.color] == t.id
-       redef fun is_subtype(t) do
-               if t.color >= row.length then return false
-               return row[t.color.as(not null)] == t.id
-       end
-end
-
-# Wrapper for interpretor legacy typing based on precomputed metalmodel
-private class LegacyTyping
-       super Typing
-       redef type T: LegacyType
-       init(interpreter: NaiveInterpreter) do super(interpreter)
-       redef fun init_type(id, mtype, supertypes, typing) do return new LegacyType(id, mtype, supertypes, typing)
-end
-
-# Wrapper for interpretor legacy typing based on precomputed metalmodel
-private class LegacyType
-       super Type
-       redef type T: LegacyType
-       redef fun is_subtype(t) do return self.mtype.is_subtype(typing.interpreter.mainmodule, typing.interpreter.frame.arguments.first.mtype.as(MClassType), t.mtype)
-end
-
-# Tools
-
-private class Cache
-
-       var elements: Array[CachePair]
-       var size: Int
-       var cache: nullable CachePair
-
-       init(size: Int) do
-               self.size = size
-               self.elements = new Array[CachePair].with_capacity(size)
-       end
-
-       # Add a response to the cache
-       fun add(t: Type, res: Bool) do
-               var pair = new CachePair(t, res)
-               elements.insert(pair, 0)
-               if elements.length > size then elements.pop
-       end
-
-       # Check if the cache contains the subtype test response
-       fun contains(t: Type): Bool do
-               for e in elements do
-                       if e.t == t then
-                               cache = e
-                               return true
-                       end
-               end
-               return false
-       end
-
-       # Get a subtype test response from the cache
-       # ensure contains before
-       fun response(t: Type): Bool do
-               if cache != null and cache.t == t then return cache.res
-
-               for e in elements do if e.t == t then return e.res
-
-               print "IC-ERROR: Cache fault"
-               abort
-       end
-end
-
-private class CachePair
-       var t: Type
-       var res: Bool
-end
-
-# Add operator - to Array
-redef class Array[T]
-       # Return a new array with the elements only contened in 'self' and not in 'o'
-       fun -(o: Array[T]): Array[T] do
-               var res = new Array[T]
-               for e in self do if not o.has(e) then res.add(e)
-               return res
-       end
-end
-
-# A sorter for reversed linearization of types
-private class TypeSorter
-       super AbstractSorter[Type]
-
-       redef fun compare(a, b) do
-               if a == b then
-                       return 0
-               else if a.is_subtype_fallback(b) then
-                       return 1
-               end
-               return -1
-       end
-end
index 9765a2e..448bada 100644 (file)
@@ -21,6 +21,7 @@ module generate_hierarchies
 import model
 private import metrics_base
 import frontend
+import model_viz
 
 redef class ToolContext
        var generate_hierarchies_phase: Phase = new GenerateHierarchyPhase(self, null)
@@ -44,40 +45,15 @@ end
 # Nesting relation is represented with nested boxes
 fun generate_module_hierarchy(toolcontext: ToolContext, model: Model)
 do
-       var buf = new Buffer
-       buf.append("digraph \{\n")
-       buf.append("node [shape=box];\n")
-       buf.append("rankdir=BT;\n")
-       for mmodule in model.mmodules do
-               if mmodule.direct_owner == null then
-                       generate_module_hierarchy_for(mmodule, buf)
-               end
-       end
-       for mmodule in model.mmodules do
-               for s in mmodule.in_importation.direct_greaters do
-                       buf.append("\"{mmodule}\" -> \"{s}\";\n")
-               end
-       end
-       buf.append("\}\n")
-       var f = new OFStream.open(toolcontext.output_dir.join_path("module_hierarchy.dot"))
-       f.write(buf.to_s)
-       f.close
-end
+       var dot = new MProjectDot(model)
 
-# Helper function for `generate_module_hierarchy`.
-# Create graphviz nodes for the module and recusrively for its nested modules
-private fun generate_module_hierarchy_for(mmodule: MModule, buf: Buffer)
-do
-       if mmodule.in_nesting.direct_greaters.is_empty then
-               buf.append("\"{mmodule.name}\";\n")
-       else
-               buf.append("subgraph \"cluster_{mmodule.name}\" \{label=\"\"\n")
-               buf.append("\"{mmodule.name}\";\n")
-               for s in mmodule.in_nesting.direct_greaters do
-                       generate_module_hierarchy_for(s, buf)
-               end
-               buf.append("\}\n")
-       end
+       var projectpath = toolcontext.output_dir.join_path("project_hierarchy.dot")
+       dot.mprojects.add(model.mprojects.first)
+       dot.render(projectpath)
+
+       var modulepath = toolcontext.output_dir.join_path("module_hierarchy.dot")
+       dot.mprojects.add_all(model.mprojects)
+       dot.render(modulepath)
 end
 
 # Create a dot file representing the class hierarchy of a model.
index 0e66ba4..fea58da 100644 (file)
@@ -43,17 +43,50 @@ do
        buf.append("<html>\n<body>\n")
        buf.append("<h1>Model</h1>\n")
 
+       buf.append("<h2>Projects</h2>\n")
+       for mproject in model.mprojects do
+               buf.append("<h3 id='project-{mproject}'>Project {mproject}</h3>\n")
+               buf.append("<dl>\n")
+               buf.append("<dt>groups</dt>\n")
+               for x in mproject.mgroups do
+                       buf.append("<dd>{linkto(x)}</dd>\n")
+               end
+               buf.append("</dl>\n")
+       end
+
+       buf.append("<h2>Groups</h2>\n")
+       for mproject in model.mprojects do
+               for mgroup in mproject.mgroups do
+                       buf.append("<h3 id='group-{mgroup}'>Group {mgroup}</h3>\n")
+                       buf.append("<dl>\n")
+                       buf.append("<dt>project</dt>\n")
+                       buf.append("<dd>{linkto(mproject)}</dd>\n")
+                       buf.append("<dt>filepath</dt>\n")
+                       buf.append("<dd>{mgroup.filepath}</dd>\n")
+                       var p = mgroup.parent
+                       if p != null then
+                               buf.append("<dt>parent group</dt>\n")
+                               buf.append("<dd>{linkto(p)}</dd>\n")
+                       end
+                       buf.append("<dt>nested groups</dt>\n")
+                       for x in mgroup.in_nesting.direct_smallers do
+                               buf.append("<dd>{linkto(x)}</dd>\n")
+                       end
+                       buf.append("<dt>modules</dt>\n")
+                       for x in mgroup.mmodules do
+                               buf.append("<dd>{linkto(x)}</dd>\n")
+                       end
+               end
+               buf.append("</dl>\n")
+       end
+
        buf.append("<h2>Modules</h2>\n")
        for mmodule in model.mmodules do
                buf.append("<h3 id='module-{mmodule}'>{mmodule}</h3>\n")
                buf.append("<dl>\n")
-               buf.append("<dt>direct owner</dt>\n")
-               var own = mmodule.direct_owner
-               if own != null then buf.append("<dd>{linkto(own)}</dd>\n")
-               buf.append("<dt>nested</dt>\n")
-               for x in mmodule.in_nesting.direct_greaters do
-                       buf.append("<dd>{linkto(x)}</dd>\n")
-               end
+               buf.append("<dt>group</dt>\n")
+               var grp = mmodule.mgroup
+               if grp != null then buf.append("<dd>{linkto(grp)}</dd>\n")
                buf.append("<dt>direct import</dt>\n")
                for x in mmodule.in_importation.direct_greaters do
                        buf.append("<dd>{linkto(x)}</dd>\n")
@@ -163,7 +196,11 @@ end
 
 private fun linkto(o: Object): String
 do
-       if o isa MModule then
+       if o isa MProject then
+               return "<a href='#project-{o}'>{o}</a>"
+       else if o isa MGroup then
+               return "<a href='#group-{o}'>{o}</a>"
+       else if o isa MModule then
                return "<a href='#module-{o}'>{o}</a>"
        else if o isa MClass then
                return "<a href='#class-{o}'>{o}</a>"
index 8ec89b1..2276742 100644 (file)
@@ -34,8 +34,6 @@ private class PosetMetricsPhase
                print "--- Poset metrics ---"
                print "## Module importation hierarchy"
                model.mmodule_importation_hierarchy.print_metrics
-               print "## Module nesting hierarchy"
-               model.mmodule_nesting_hierarchy.print_metrics
                print "## Classdef hierarchy"
                model.mclassdef_hierarchy.print_metrics
                print "## Class hierarchy"
diff --git a/src/model/mmodule.nit b/src/model/mmodule.nit
new file mode 100644 (file)
index 0000000..96779cc
--- /dev/null
@@ -0,0 +1,207 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# modules and module hierarchies in the metamodel
+module mmodule
+
+import poset
+import location
+import mproject
+private import more_collections
+
+# The container class of a Nit object-oriented model.
+# A model knows modules, classes and properties and can retrieve them.
+redef class Model
+       # All known modules
+       var mmodules: Array[MModule] = new Array[MModule]
+
+       # placebo for old module nesting hierarchy.
+       # where mainmodule < mainmodule::nestedmodule
+       #
+       # TODO REMOVE, rely on mgroup instead
+       var mmodule_nesting_hierarchy: POSet[MModule] = new POSet[MModule]
+
+       # Full module importation hierarchy including private or nested links.
+       var mmodule_importation_hierarchy: POSet[MModule] = new POSet[MModule]
+
+       # Collections of modules grouped by their short names
+       private var mmodules_by_name: MultiHashMap[String, MModule] = new MultiHashMap[String, MModule]
+
+       # Return all module named `name`
+       # If such a module does not exist, null is returned (instead of an empty array)
+       #
+       # Visibility or modules are not considered
+       fun get_mmodules_by_name(name: String): nullable Array[MModule]
+       do
+               if mmodules_by_name.has_key(name) then
+                       return mmodules_by_name[name]
+               else
+                       return null
+               end
+       end
+end
+
+redef class MGroup
+       # The loaded modules of this group
+       var mmodules = new Array[MModule]
+end
+
+# A Nit module is usually associated with a Nit source file.
+class MModule
+       # The model considered
+       var model: Model
+
+       # placebo for old module nesting hierarchy
+       # return null if self is not nested (ie. is a top-level module)
+       #
+       # TODO REMOVE, rely on mgroup instead
+       var direct_owner: nullable MModule
+
+       # The group of module in the project if any
+       var mgroup: nullable MGroup
+
+       # The short name of the module
+       var name: String
+
+       # The origin of the definition
+       var location: Location
+
+       # Alias for `name`
+       redef fun to_s do return self.name
+
+       # placebo for old module nesting hierarchy
+       # The view of the module in the `model.mmodule_nesting_hierarchy`
+       #
+       # TODO REMOVE, rely on mgroup instead
+       var in_nesting: POSetElement[MModule]
+
+       # The view of the module in the `model.mmodule_importation_hierarchy`
+       var in_importation: POSetElement[MModule]
+
+       # The canonical name of the module
+       # Example: `"project::name"`
+       fun full_name: String
+       do
+               var mgroup = self.mgroup
+               if mgroup == null or mgroup.mproject.name == self.name then
+                       return self.name
+               else
+                       return "{mgroup.mproject.name}::{self.name}"
+               end
+       end
+
+       # Create a new empty module and register it to a model
+       init(model: Model, mgroup: nullable MGroup, name: String, location: Location)
+       do
+               self.model = model
+               self.name = name
+               self.location = location
+               model.mmodules_by_name.add_one(name, self)
+               model.mmodules.add(self)
+               self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
+               self.mgroup = mgroup
+               if mgroup != null then
+                       mgroup.mmodules.add(self)
+                       # placebo for old module nesting hierarchy
+                       var direct_owner = mgroup.mmodules.first
+                       if direct_owner == self and mgroup.parent != null and not mgroup.parent.mmodules.is_empty then
+                               direct_owner = mgroup.parent.mmodules.first
+                       end
+                       if direct_owner != self then
+                               self.direct_owner = direct_owner
+                               model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
+                       end
+               end
+               self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
+       end
+
+       # Register the imported modules (ie "import some_module")
+       # This function can only invoked once by mmodule.
+       # The visibility must be set with `set_visibility_for`.
+       fun set_imported_mmodules(imported_mmodules: Array[MModule])
+       do
+               assert unique_invocation: self.in_importation.direct_greaters.is_empty
+               for m in imported_mmodules do
+                       self.model.mmodule_importation_hierarchy.add_edge(self, m)
+               end
+       end
+
+       private var intrude_mmodules: HashSet[MModule] = new HashSet[MModule]
+       private var public_mmodules: HashSet[MModule] = new HashSet[MModule]
+       private var private_mmodules: HashSet[MModule] = new HashSet[MModule]
+
+       # Return the visibility level of an imported module `m`
+       fun visibility_for(m: MModule): MVisibility
+       do
+               if m == self then return intrude_visibility
+               if self.intrude_mmodules.has(m) then return intrude_visibility
+               if self.public_mmodules.has(m) then return public_visibility
+               if self.private_mmodules.has(m) then return private_visibility
+               return none_visibility
+       end
+
+       # Set the visibility of an imported module
+       # REQUIRE: the visibility of the modules imported by `m` are already set for `m`
+       fun set_visibility_for(m: MModule, v: MVisibility)
+       do
+               if v == intrude_visibility then
+                       self.intrude_mmodules.add(m)
+                       self.intrude_mmodules.add_all(m.intrude_mmodules)
+                       self.public_mmodules.add_all(m.public_mmodules)
+                       self.private_mmodules.add_all(m.private_mmodules)
+               else if v == public_visibility then
+                       self.public_mmodules.add(m)
+                       self.public_mmodules.add_all(m.intrude_mmodules)
+                       self.public_mmodules.add_all(m.public_mmodules)
+               else if v == private_visibility then
+                       self.private_mmodules.add(m)
+                       self.private_mmodules.add_all(m.intrude_mmodules)
+                       self.private_mmodules.add_all(m.public_mmodules)
+               else
+                       print "{self} visibility for {m} = {v}"
+                       abort # invalid visibility
+               end
+       end
+
+       # placebo for old module nesting hierarchy
+       fun public_owner: nullable MModule
+       do
+               var mgroup = self.mgroup
+               if mgroup == null then return null
+               mgroup = mgroup.mproject.root
+               if mgroup.mmodules.is_empty then return null
+               var res = mgroup.mmodules.first
+               if res == self then return null
+               return res
+       end
+
+       # Return true if a class or a property introduced in `intro_mmodule` with a visibility of `visibility` is visible in self.
+       fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
+       do
+               var v = visibility_for(intro_mmodule)
+               if v == intrude_visibility then
+                       return visibility >= private_visibility
+               else if v == public_visibility then
+                       return visibility > private_visibility
+               else if v == private_visibility then
+                       return visibility > private_visibility
+               else if v == none_visibility then
+                       return false
+               else
+                       abort
+               end
+       end
+end
index c356f8d..ff211ea 100644 (file)
@@ -31,7 +31,7 @@ module model
 
 import poset
 import location
-import model_base
+import mmodule
 private import more_collections
 
 redef class Model
index c72297e..dda72bb 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# The abstract concept of model and related common things
 module model_base
 
-import poset
-import location
-private import more_collections
-
 # The container class of a Nit object-oriented model.
 # A model knows modules, classes and properties and can retrieve them.
 class Model
-       # All known modules
-       var mmodules: Array[MModule] = new Array[MModule]
-
-       # Module nesting hierarchy.
-       # where mainmodule < mainmodule::nestedmodule
-       var mmodule_nesting_hierarchy: POSet[MModule] = new POSet[MModule]
-
-       # Full module importation hierarchy including private or nested links.
-       var mmodule_importation_hierarchy: POSet[MModule] = new POSet[MModule]
-
-       # Collections of modules grouped by their short names
-       private var mmodules_by_name: MultiHashMap[String, MModule] = new MultiHashMap[String, MModule]
-
-       # Return all module named `name`
-       # If such a module does not exist, null is returned (instead of an empty array)
-       #
-       # Visibility or modules are not considered
-       fun get_mmodules_by_name(name: String): nullable Array[MModule]
-       do
-               if mmodules_by_name.has_key(name) then
-                       return mmodules_by_name[name]
-               else
-                       return null
-               end
-       end
-end
-
-# A Nit module is usually associated with a Nit source file.
-# Modules can be nested (see `direct_owner`, `public_owner`, and `in_nesting`)
-class MModule
-       # The model considered
-       var model: Model
-
-       # The direct nesting module, return null if self is not nested (ie. is a top-level module)
-       var direct_owner: nullable MModule
-
-       # The short name of the module
-       var name: String
-
-       # The origin of the definition
-       var location: Location
-
-       # Alias for `name`
-       redef fun to_s do return self.name
-
-       # The view of the module in the `model.mmodule_nesting_hierarchy`
-       var in_nesting: POSetElement[MModule]
-
-       # The view of the module in the `model.mmodule_importation_hierarchy`
-       var in_importation: POSetElement[MModule]
-
-       # The canonical name of the module
-       # Example: `"owner::name"`
-       fun full_name: String
-       do
-               var owner = self.public_owner
-               if owner == null then
-                       return self.name
-               else
-                       return "{owner.full_name}::{self.name}"
-               end
-       end
-
-       # Create a new empty module and register it to a model
-       # `direct_owner` is the direct owner (null if top-level module)
-       init(model: Model, direct_owner: nullable MModule, name: String, location: Location)
-       do
-               self.model = model
-               self.name = name
-               self.location = location
-               model.mmodules_by_name.add_one(name, self)
-               model.mmodules.add(self)
-               self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
-               self.direct_owner = direct_owner
-               if direct_owner != null then
-                       model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
-               end
-               self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
-       end
-
-       # Register the imported modules (ie "import some_module")
-       # This function can only invoked once by mmodule.
-       # The visibility must be set with `set_visibility_for`.
-       fun set_imported_mmodules(imported_mmodules: Array[MModule])
-       do
-               assert unique_invocation: self.in_importation.direct_greaters.is_empty
-               for m in imported_mmodules do
-                       self.model.mmodule_importation_hierarchy.add_edge(self, m)
-               end
-       end
-
-       private var intrude_mmodules: HashSet[MModule] = new HashSet[MModule]
-       private var public_mmodules: HashSet[MModule] = new HashSet[MModule]
-       private var private_mmodules: HashSet[MModule] = new HashSet[MModule]
-
-       # Return the visibility level of an imported module `m`
-       fun visibility_for(m: MModule): MVisibility
-       do
-               if m == self then return intrude_visibility
-               if self.intrude_mmodules.has(m) then return intrude_visibility
-               if self.public_mmodules.has(m) then return public_visibility
-               if self.private_mmodules.has(m) then return private_visibility
-               return none_visibility
-       end
-
-       # Set the visibility of an imported module
-       # REQUIRE: the visibility of the modules imported by `m` are already set for `m`
-       fun set_visibility_for(m: MModule, v: MVisibility)
-       do
-               if v == intrude_visibility then
-                       self.intrude_mmodules.add(m)
-                       self.intrude_mmodules.add_all(m.intrude_mmodules)
-                       self.public_mmodules.add_all(m.public_mmodules)
-                       self.private_mmodules.add_all(m.private_mmodules)
-               else if v == public_visibility then
-                       self.public_mmodules.add(m)
-                       self.public_mmodules.add_all(m.intrude_mmodules)
-                       self.public_mmodules.add_all(m.public_mmodules)
-               else if v == private_visibility then
-                       self.private_mmodules.add(m)
-                       self.private_mmodules.add_all(m.intrude_mmodules)
-                       self.private_mmodules.add_all(m.public_mmodules)
-               else
-                       print "{self} visibility for {m} = {v}"
-                       abort # invalid visibility
-               end
-       end
-
-       # The first module in the nesting hierarchy to export self as public
-       # This function is used to determine the canonical name of modules, classes and properties.
-       # REQUIRE: the visibility of all nesting modules is already set.
-       fun public_owner: nullable MModule
-       do
-               var res = self.direct_owner
-               var last = res
-               while last != null do
-                       if last.visibility_for(self) >= public_visibility then res = last
-                       last = last.direct_owner
-               end
-               return res
-       end
-
-       # Return true if a class or a property introduced in `intro_mmodule` with a visibility of `visibility` is visible in self.
-       fun is_visible(intro_mmodule: MModule, visibility: MVisibility): Bool
-       do
-               var v = visibility_for(intro_mmodule)
-               if v == intrude_visibility then
-                       return visibility >= private_visibility
-               else if v == public_visibility then
-                       return visibility > private_visibility
-               else if v == private_visibility then
-                       return visibility > private_visibility
-               else if v == none_visibility then
-                       return false
-               else
-                       abort
-               end
-       end
 end
 
 # A visibility (for modules, class and properties)
diff --git a/src/model/mproject.nit b/src/model/mproject.nit
new file mode 100644 (file)
index 0000000..9c1765f
--- /dev/null
@@ -0,0 +1,108 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Modelisation of a Nit project
+module mproject
+
+import model_base
+private import more_collections
+import poset
+
+# A Nit project, thas encompass a product
+class MProject
+       # The name of the project
+       var name: String
+
+       # The model of the project
+       var model: Model
+
+       # The root of the group tree
+       var root: nullable MGroup writable = null
+
+       # The group tree, as a POSet
+       var mgroups = new POSet[MGroup]
+
+       redef fun to_s do return name
+
+       init(name: String, model: Model)
+       do
+               self.name = name
+               self.model = model
+               model.mprojects.add(self)
+               model.mproject_by_name.add_one(name, self)
+       end
+end
+
+# A group of modules in a project
+class MGroup
+       # The name of the group
+       # empty name for a default group in a single-module project
+       var name: String
+
+       # The englobing project
+       var mproject: MProject
+
+       # The parent group if any
+       # see `in_nesting` for more
+       var parent: nullable MGroup
+
+       # fully qualified name
+       fun full_name: String
+       do
+               var p = parent
+               if p == null then return name
+               return "{p.full_name}/{name}"
+       end
+
+       # The group is the group tree on the project (`mproject.mgroups`)
+       # nested groups (children) are smallers
+       # nesting group (see `parent`) is bigger
+       var in_nesting: POSetElement[MGroup]
+
+       # The filepath (usualy a directory) of the group, if any
+       var filepath: nullable String writable
+
+       init (name: String, mproject: MProject, parent: nullable MGroup)
+       do
+               self.name = name
+               self.mproject = mproject
+               self.parent = parent
+               var tree = mproject.mgroups
+               self.in_nesting = tree.add_node(self)
+               if parent != null then
+                       tree.add_edge(self, parent)
+               end
+       end
+
+       redef fun to_s do return name
+end
+
+redef class Model
+       # projects of the model
+       var mprojects = new Array[MProject]
+
+       # Collections of project grouped by their names
+       private var mproject_by_name: MultiHashMap[String, MProject] = new MultiHashMap[String, MProject]
+
+       # Return all project named `name`
+       # If such a project is not yet loaded, null is returned (instead of an empty array)
+       fun get_mprojects_by_name(name: String): nullable Array[MProject]
+       do
+               if mproject_by_name.has_key(name) then
+                       return mproject_by_name[name]
+               else
+                       return null
+               end
+       end
+end
diff --git a/src/model_viz.nit b/src/model_viz.nit
new file mode 100644 (file)
index 0000000..1471a49
--- /dev/null
@@ -0,0 +1,269 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Visualisation of Nit models
+module model_viz
+
+import model
+import ordered_tree
+
+# A simple specialisation of OrderedTree to display projects, groups and modules
+# FIXME do not use Object, but a better common interface of MModule and MGroup
+class MProjectTree
+       super OrderedTree[Object]
+
+       # The model where to look for information
+       var model: Model
+
+       init(model: Model) do self.model = model
+
+       redef fun display(a) do
+               if a isa MGroup then
+                       if a.parent == null then return "{a.mproject.name} ({a.filepath})"
+                       return a.name + " (group)"
+               else if a isa MModule then
+                       return a.name
+               else
+                       abort
+               end
+       end
+
+       var alpha_comparator = new AlphaComparator
+
+       var linex_comparator: nullable LinexComparator = null
+
+       # Sort modules and groups with their names
+       fun sort_with_alpha
+       do
+               sort_with(alpha_comparator)
+       end
+
+       # Sort modules and groups with a loosly adaptation of the linerarisation of modules
+       fun sort_with_linex
+       do
+               var c = linex_comparator
+               if c == null then
+                       c = new LinexComparator(self)
+                       linex_comparator = c
+               end
+               sort_with(c)
+       end
+end
+
+# Just compare objects by using the `to_s` method
+private class AlphaComparator
+       super AbstractSorter[Object]
+       redef fun compare(a,b) do return a.to_s <=> b.to_s
+end
+
+# Compare modules and groups using the
+# FIXME do not use Object, but a better common interface of MModule and MGroup
+private class LinexComparator
+       super AbstractSorter[Object]
+       var mins = new HashMap [MGroup, nullable MModule]
+       var maxs = new HashMap [MGroup, nullable MModule]
+       fun min(o: Object): nullable MModule do
+               if o isa MModule then return o
+               assert o isa MGroup
+               if not mins.has_key(o) then computeminmax(o)
+               return mins[o]
+       end
+       fun max(o: Object): nullable MModule do
+               if o isa MModule then return o
+               assert o isa MGroup
+               if not maxs.has_key(o) then computeminmax(o)
+               return maxs[o]
+       end
+       fun computeminmax(o: MGroup) do
+               if not tree.sub.has_key(o) then
+                       mins[o] = null
+                       maxs[o] = null
+                       return
+               end
+               var subs = tree.sub[o]
+               var minres = min(subs.first)
+               var maxres = max(subs.first)
+               var order = minres.model.mmodule_importation_hierarchy
+               for o2 in subs do
+                       var c = min(o2)
+                       if c == null then continue
+                       if minres == null or order.compare(minres, c) > 0 then minres = c
+                       c = max(o2)
+                       if c == null then continue
+                       if maxres == null or order.compare(maxres, c) < 0 then maxres = c
+               end
+               mins[o] = minres
+               maxs[o] = maxres
+               #if minres != maxres then print "* {o} {minres}..{maxres}"
+       end
+       redef fun compare(a,b) do
+               var ma = min(a)
+               var mb = min(b)
+               if ma == null then
+                       if mb == null then return 0 else return -1
+               else if mb == null then
+                       return 1
+               end
+               var order = ma.model.mmodule_importation_hierarchy
+               return order.compare(ma, mb)
+       end
+       var tree: OrderedTree[Object]
+end
+
+redef class Model
+       # Generate a MProjectTree based on the projects, groups and modules known in the model
+       fun to_mproject_tree: MProjectTree
+       do
+               var res = new MProjectTree(self)
+               for p in mprojects do
+                       for g in p.mgroups do
+                               res.add(g.parent, g)
+                               for m in g.mmodules do
+                                       res.add(g, m)
+                               end
+                       end
+               end
+               return res
+       end
+end
+
+# Generate graphiz files based on projects, groups and modules
+#
+# Interessing elements must be selected. See `mmodules`, ``
+# Display configuration can be set. See `cluster_group`, `project_group`
+class MProjectDot
+       # The model where to look for information
+       var model: Model
+
+       # Set of projects to expand fully (ie all groups and modules are displayed)
+       # Initially empty, projects can be added
+       var mprojects = new HashSet[MProject]
+
+       # Set of modules to display
+       # Initially empty, modules can be added
+       var mmodules = new HashSet[MModule]
+
+       private fun node_for(mmodule: MModule): nullable String
+       do
+               return "m_{mmodule.object_id}"
+       end
+
+       # Should groups be shown as clusters?
+       var cluster_group writable = true
+
+       # Should projects be shown as clusters?
+       var project_group writable = true
+
+       # Recursively generate noed ans clusters for a mroup
+       private fun dot_cluster(o: OStream, mgroup: MGroup)
+       do
+               # Open the cluster, if required
+               if mgroup.parent == null then
+                       # is is a root group, so display the project
+                       if project_group then
+                               o.write("subgraph cluster_{mgroup.object_id} \{\nlabel=\"{mgroup.mproject.name}\\n({mgroup.filepath})\"\ncolor=black\nstyle=dotted\n")
+                       end
+               else
+                       if cluster_group then
+                               o.write("subgraph cluster_{mgroup.object_id} \{\nlabel=\"{mgroup.name}\"\ncolor=blue\nstyle=dotted\n")
+                       end
+               end
+
+               # outputs the mmodules to show
+               for mmodule in mgroup.mmodules do
+                       if not mmodules.has(mmodule) then continue
+                       o.write("\t{node_for(mmodule)} [label=\"{mmodule.name}\",color=green]\n")
+               end
+
+               # recusively progress on sub-clusters
+               for d in mgroup.in_nesting.direct_smallers do
+                       dot_cluster(o, d)
+               end
+
+               # close the cluster if required
+               if mgroup.parent == null then
+                       if project_group then o.write("\}\n")
+               else
+                       if cluster_group then o.write("\}\n")
+               end
+       end
+
+       # Extends the set of `mmodules` by recurdively adding the most specific imported modules of foreign projects
+       fun collect_important_importation
+       do
+               var todo = new List[MModule]
+               todo.add_all(mmodules)
+               while not todo.is_empty do
+                       var m = todo.pop
+
+                       for psm in m.in_importation.greaters do
+                               if m.mgroup.mproject != psm.mgroup.mproject then continue
+                               for ssm in psm.in_importation.direct_greaters do
+                                       if psm.mgroup.mproject == ssm.mgroup.mproject then continue
+                                       mmodules.add(ssm)
+                                       todo.add(ssm)
+                               end
+                       end
+               end
+       end
+
+       # Generate the dot-file named `filepath` with the current configuration
+       fun render(filepath: String)
+       do
+               # Collect interessing nodes
+               for m in model.mmodules do
+                       # filter out modules outside wanted projects
+                       if not mprojects.has(m.mgroup.mproject) then continue
+
+                       mmodules.add(m)
+               end
+
+               collect_important_importation
+
+               # Collect interessing edges
+               var sub_hierarchy = new POSet[MModule]
+               for m in mmodules do
+                       sub_hierarchy.add_node(m)
+                       for sm in m.in_importation.greaters do
+                               if sm == m then continue
+                               if not mmodules.has(sm) then continue
+                               sub_hierarchy.add_edge(m, sm)
+                       end
+               end
+
+               print "generating {filepath}"
+               var dot = new OFStream.open(filepath)
+               dot.write("digraph g \{\n")
+               dot.write("rankdir=BT;node[shape=box];\n")
+               # Generate the nodes
+               for p in model.mprojects do
+                       dot_cluster(dot, p.root.as(not null))
+               end
+               # Generate the edges
+               for m in mmodules do
+                       for sm in sub_hierarchy[m].direct_greaters do
+                               var nm = node_for(m)
+                               var nsm = node_for(sm)
+                               if m.in_importation.direct_greaters.has(sm) then
+                                       dot.write("\t{nm} -> {nsm}[style=bold]\n")
+                               else
+                                       dot.write("\t{nm} -> {nsm}[style=solid]\n")
+                               end
+                       end
+               end
+               dot.write("\}\n")
+               dot.close
+               # sys.system("xdot -f dot {filepath}")
+       end
+end
index bb7d46c..c9e5603 100644 (file)
@@ -52,8 +52,22 @@ redef class ToolContext
        fun modelbuilder: ModelBuilder do return modelbuilder_real.as(not null)
        private var modelbuilder_real: nullable ModelBuilder = null
 
+       fun run_global_phases(mainmodule: MModule)
+       do
+               for phase in phases_list do
+                       phase.process_mainmodule(mainmodule)
+               end
+       end
 end
 
+redef class Phase
+       # Specific action to execute on the whole program
+       # Called by the `ToolContext::run_global_phases`
+       # @toimplement
+       fun process_mainmodule(mainmodule: MModule) do end
+end
+
+
 # A model builder knows how to load nit source files and build the associated model
 class ModelBuilder
        # The model where new modules, classes and properties are added
@@ -118,9 +132,9 @@ class ModelBuilder
                var time0 = get_time
                # Parse and recursively load
                self.toolcontext.info("*** PARSE ***", 1)
-               var mmodules = new Array[MModule]
+               var mmodules = new ArraySet[MModule]
                for a in modules do
-                       var nmodule = self.load_module(null, a)
+                       var nmodule = self.load_module(a)
                        if nmodule == null then continue # Skip error
                        mmodules.add(nmodule.mmodule.as(not null))
                end
@@ -134,7 +148,7 @@ class ModelBuilder
                        exit(0)
                end
 
-               return mmodules
+               return mmodules.to_a
        end
 
        # Return a class named `name` visible by the module `mmodule`.
@@ -238,73 +252,52 @@ class ModelBuilder
        # FIXME: add a way to handle module name conflict
        fun get_mmodule_by_name(anode: ANode, mmodule: nullable MModule, name: String): nullable MModule
        do
-               var origmmodule = mmodule
-               var modules = model.get_mmodules_by_name(name)
-
+               # what path where tried to display on error message
                var tries = new Array[String]
 
-               var lastmodule = mmodule
-               while mmodule != null do
-                       var dirname = mmodule.location.file.filename.dirname
-
-                       # Determine the owner
-                       var owner: nullable MModule
-                       if dirname.basename("") != mmodule.name then
-                               owner = mmodule.direct_owner
-                       else
-                               owner = mmodule
-                       end
-
-                       # First, try the already known nested modules
-                       if modules != null then
-                               for candidate in modules do
-                                       if candidate.direct_owner == owner then
-                                               return candidate
-                                       end
+               # 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"
+                               tries.add try_file
+                               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
-                       end
-
-                       # Second, try the directory to find a file
-                       var try_file = dirname + "/" + name + ".nit"
-                       tries.add try_file
-                       if try_file.file_exists then
-                               var res = self.load_module(owner, 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 an owner
-                       try_file = dirname + "/" + name + "/" + name + ".nit"
-                       if try_file.file_exists then
-                               var res = self.load_module(owner, try_file.simplify_path)
-                               if res == null then return null # Forward error
-                               return res.mmodule.as(not null)
-                       end
-
-                       lastmodule = mmodule
-                       mmodule = mmodule.direct_owner
-               end
 
-               if modules != null then
-                       for candidate in modules do
-                               if candidate.direct_owner == null then
-                                       return candidate
+                               # 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 the last module also (event if not in the path)
-               if lastmodule != null then
-                       var dirname = lastmodule.location.file.filename.dirname
-                       if dirname.basename("") == lastmodule.name then
-                               dirname = dirname.dirname
-                       end
-                       if not lookpaths.has(dirname) then
-                               lookpaths = lookpaths.to_a
-                               lookpaths.add(dirname)
+               # 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
 
@@ -339,33 +332,100 @@ class ModelBuilder
                        end
                end
                if candidate == null then
-                       if origmmodule != null then
-                               error(anode, "Error: cannot find module {name} from {origmmodule}. tried {tries.join(", ")}")
+                       if mmodule != null then
+                               error(anode, "Error: cannot find module {name} from {mmodule}. tried {tries.join(", ")}")
                        else
                                error(anode, "Error: cannot find module {name}. tried {tries.join(", ")}")
                        end
                        return null
                end
-               var res = self.load_module(mmodule, candidate)
+               var res = self.load_module(candidate)
                if res == null then return null # Forward error
                return res.mmodule.as(not null)
        end
 
-       # Transform relative paths (starting with '../') into absolute paths
-       private fun module_absolute_path(path: String): String do
-               if path.has_prefix("..") then
-                       return getcwd.join_path(path).simplify_path
+       # 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
-               return path
+
+               # 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
        end
 
-       # loaded module by absolute path
-       private var loaded_nmodules = new HashMap[String, AModule]
+       # 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
+       private fun get_mgroup(dirpath: String): nullable MGroup
+       do
+               var rdp = module_absolute_path(dirpath)
+               if mgroups.has_key(rdp) then
+                       return mgroups[rdp]
+               end
+
+               # Hack, a dir is determined by the presence of a honomymous nit file
+               var pn = rdp.basename(".nit")
+               var mp = dirpath.join_path(pn + ".nit").simplify_path
+
+               if not mp.file_exists then return null
+
+               # check parent directory
+               var parentpath = dirpath.join_path("..").simplify_path
+               var parent = get_mgroup(parentpath)
+
+               var mgroup
+               if parent == null then
+                       # no parent, thus new project
+                       var mproject = new MProject(pn, model)
+                       mgroup = new MGroup(pn, mproject, null) # same name for the root group
+                       mproject.root = mgroup
+                       toolcontext.info("found project `{mproject}` at {dirpath}", 2)
+               else
+                       mgroup = new MGroup(pn, parent.mproject, parent)
+                       toolcontext.info("found sub group `{mgroup.full_name}` at {dirpath}", 2)
+               end
+               mgroup.filepath = dirpath
+               mgroups[rdp] = mgroup
+               return mgroup
+       end
+
+       # Transform relative paths (starting with '../') into absolute paths
+       private fun module_absolute_path(path: String): String do
+               return getcwd.join_path(path).simplify_path
+       end
 
-       # Try to load a module using a path.
+       # Try to load a module AST 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.
-       fun load_module(owner: nullable MModule, filename: String): nullable AModule
+       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.")
@@ -376,13 +436,7 @@ class ModelBuilder
                        return null
                end
 
-               var module_path = module_absolute_path(filename)
-               if loaded_nmodules.keys.has(module_path) then
-                       return loaded_nmodules[module_path]
-               end
-
-               var x = if owner != null then owner.to_s else "."
-               self.toolcontext.info("load module {filename} in {x}", 2)
+               self.toolcontext.info("load module {filename}", 2)
 
                # Load the file
                var file = new IFStream.open(filename)
@@ -391,41 +445,71 @@ class ModelBuilder
                var tree = parser.parse
                file.close
                var mod_name = filename.basename(".nit")
-               return load_module_commons(owner, tree, mod_name)
+
+               # 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
 
-       fun load_rt_module(owner: MModule, nmodule: AModule, mod_name: String): nullable AModule
+       # Try to load a module and its imported modules using a path.
+       # Display an error if there is a problem (IO / lexer / parser / importation) and return null
+       # Note: usually, you do not need this method, use `get_mmodule_by_name` instead.
+       fun load_module(filename: String): nullable AModule
+       do
+               # Look for the module
+               var file = identify_file(filename)
+               if file == null then return null # forward error
+
+               # Already known and loaded? then return it
+               var mmodule = file.mmodule
+               if mmodule != null then
+                       return mmodule2nmodule[mmodule]
+               end
+
+               # Load it manually
+               var nmodule = load_module_ast(filename)
+               if nmodule == null then return null # forward error
+
+               # build the mmodule and load imported modules
+               mmodule = build_a_mmodule(file.mgroup, file.name, nmodule)
+
+               if mmodule == null then return null # forward error
+
+               # Update the file information
+               file.mmodule = mmodule
+
+               return nmodule
+       end
+
+       fun load_rt_module(parent: MModule, nmodule: AModule, mod_name: String): nullable AModule
        do
                # Create the module
-               var mmodule = new MModule(model, owner, mod_name, nmodule.location)
+               var mmodule = new MModule(model, parent.mgroup, mod_name, nmodule.location)
                nmodule.mmodule = mmodule
                nmodules.add(nmodule)
                self.mmodule2nmodule[mmodule] = nmodule
 
                var imported_modules = new Array[MModule]
 
-               imported_modules.add(owner)
-               mmodule.set_visibility_for(owner, intrude_visibility)
+               imported_modules.add(parent)
+               mmodule.set_visibility_for(parent, intrude_visibility)
 
                mmodule.set_imported_mmodules(imported_modules)
 
                return nmodule
        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.
-       private fun load_module_commons(owner: nullable MModule, tree: Start, mod_name: String): nullable AModule
+       # Visit the AST and create the `MModule` object
+       # Then, recursively load imported modules
+       private fun build_a_mmodule(mgroup: nullable MGroup, mod_name: String, nmodule: AModule): nullable MModule
        do
-               # 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
-
                # Check the module name
                var decl = nmodule.n_moduledecl
                if decl == null then
@@ -438,14 +522,14 @@ class ModelBuilder
                end
 
                # Create the module
-               var mmodule = new MModule(model, owner, mod_name, nmodule.location)
+               var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
                nmodule.mmodule = mmodule
                nmodules.add(nmodule)
                self.mmodule2nmodule[mmodule] = nmodule
 
                build_module_importation(nmodule)
 
-               return nmodule
+               return mmodule
        end
 
        # Analysis the module importation and fill the module_importation_hierarchy
@@ -518,6 +602,25 @@ class ModelBuilder
        end
 end
 
+# placeholder to a module file identified but not always loaded in a project
+private class ModulePath
+       # The name of the module
+       # (it's the basename of the filepath)
+       var name: String
+
+       # The human path of the module
+       var filepath: String
+
+       # The group (and the project) of the possible module
+       var mgroup: MGroup
+
+       # The loaded module (if any)
+       var mmodule: nullable MModule = null
+
+       redef fun to_s do return filepath
+end
+
+
 redef class AStdImport
        # The imported module once determined
        var mmodule: nullable MModule = null
index 3058bec..74b22b3 100644 (file)
@@ -900,6 +900,8 @@ redef class AExternMethPropdef
                        else if pname == "file_chdir" then
                                recvval.to_s.chdir
                                return null
+                       else if pname == "file_realpath" then
+                               return v.native_string_instance(recvval.to_s.realpath)
                        else if pname == "get_environ" then
                                var txt = recvval.to_s.environ
                                return v.native_string_instance(txt)
index f8bb81c..280e273 100644 (file)
@@ -78,7 +78,7 @@ class NitdocContext
                if arguments.length < 1 then
                        print "usage: nitdoc [options] file..."
                        toolcontext.option_context.usage
-                       exit(1)
+                       exit(0)
                end
                self.process_options
 
@@ -161,7 +161,7 @@ class NitdocContext
        end
 
        private fun overview do
-               var overviewpage = new NitdocOverview(self, dot_dir)
+               var overviewpage = new NitdocOverview(self)
                overviewpage.save("{output_dir.to_s}/index.html")
        end
 
@@ -173,14 +173,14 @@ class NitdocContext
        private fun modules do
                for mmodule in model.mmodules do
                        if mmodule.name == "<main>" then continue
-                       var modulepage = new NitdocModule(mmodule, self, dot_dir)
+                       var modulepage = new NitdocModule(mmodule, self)
                        modulepage.save("{output_dir.to_s}/{mmodule.url}")
                end
        end
 
        private fun classes do
                for mclass in mbuilder.model.mclasses do
-                       var classpage = new NitdocClass(mclass, self, dot_dir, source)
+                       var classpage = new NitdocClass(mclass, self)
                        classpage.save("{output_dir.to_s}/{mclass.url}")
                end
        end
@@ -223,8 +223,6 @@ end
 # Nitdoc base page
 abstract class NitdocPage
 
-       var dot_dir: nullable String
-       var source: nullable String
        var ctx: NitdocContext
 
        init(ctx: NitdocContext) do
@@ -280,7 +278,7 @@ abstract class NitdocPage
 
        # Generate a clickable graphviz image using a dot content
        protected fun generate_dot(dot: String, name: String, alt: String) do
-               var output_dir = dot_dir
+               var output_dir = ctx.dot_dir
                if output_dir == null then return
                var file = new OFStream.open("{output_dir}/{name}.dot")
                file.write(dot)
@@ -297,6 +295,7 @@ abstract class NitdocPage
        # Add a (source) link for a given location
        protected fun show_source(l: Location): String
        do
+               var source = ctx.source
                if source == null then
                        return "({l.file.filename.simplify_path})"
                else
@@ -307,6 +306,7 @@ abstract class NitdocPage
                        source = x.join(l.line_start.to_s)
                        x = source.split_with("%L")
                        source = x.join(l.line_end.to_s)
+                       source = source.simplify_path
                        return " (<a target='_blank' title='Show source' href=\"{source.to_s}\">source</a>)"
                end
        end
@@ -371,10 +371,9 @@ class NitdocOverview
        private var mbuilder: ModelBuilder
        private var mmodules = new Array[MModule]
 
-       init(ctx: NitdocContext, dot_dir: nullable String) do
+       init(ctx: NitdocContext) do
                super(ctx)
                self.mbuilder = ctx.mbuilder
-               self.dot_dir = dot_dir
                # get modules
                var mmodules = new HashSet[MModule]
                for mmodule in mbuilder.model.mmodule_importation_hierarchy do
@@ -464,7 +463,6 @@ class NitdocSearch
 
        init(ctx: NitdocContext) do
                super(ctx)
-               self.dot_dir = null
        end
 
        redef fun title do return "Search"
@@ -553,11 +551,10 @@ class NitdocModule
        private var intro_mclasses = new HashSet[MClass]
        private var redef_mclasses = new HashSet[MClass]
 
-       init(mmodule: MModule, ctx: NitdocContext, dot_dir: nullable String) do
+       init(mmodule: MModule, ctx: NitdocContext) do
                super(ctx)
                self.mmodule = mmodule
                self.mbuilder = ctx.mbuilder
-               self.dot_dir = dot_dir
                # get local mclasses
                for m in mmodule.in_nesting.greaters do
                        for mclassdef in m.mclassdefs do
@@ -746,11 +743,9 @@ class NitdocClass
        private var meths = new HashSet[MMethodDef]
        private var inherited = new HashSet[MPropDef]
 
-       init(mclass: MClass, ctx: NitdocContext, dot_dir: nullable String, source: nullable String) do
+       init(mclass: MClass, ctx: NitdocContext) do
                super(ctx)
                self.mclass = mclass
-               self.dot_dir = dot_dir
-               self.source = source
                # load properties
                var locals = new HashSet[MProperty]
                for mclassdef in mclass.mclassdefs do
index 05cdcc5..5bb2b67 100644 (file)
@@ -149,15 +149,22 @@ end
 redef class ModelBuilder
        fun test_markdown(mmodule: MModule): HTMLTag
        do
+               var ts = new HTMLTag("testsuite")
                toolcontext.info("nitunit: {mmodule}", 2)
+               if not mmodule2nmodule.has_key(mmodule) then return ts
+
+               var nmodule = mmodule2nmodule[mmodule]
+               assert nmodule != null
+
+               # what module to import in the unit test.
+               # try to detect the main module of the project
+               # TODO do things correctly once the importation of arbitraty nested module is legal
                var o = mmodule
-               var d = o.public_owner
-               while d != null do
-                       o = d
-                       d = o.public_owner
+               var g = o.mgroup
+               if g != null then
+                       o = get_mmodule_by_name(nmodule, mmodule, g.mproject.name).as(not null)
                end
 
-               var ts = new HTMLTag("testsuite")
                ts.attr("package", mmodule.full_name)
 
                var prefix = toolcontext.opt_dir.value
@@ -167,39 +174,36 @@ redef class ModelBuilder
 
                var tc
 
-               if mmodule2nmodule.has_key(mmodule) then
-                       var nmodule = mmodule2nmodule[mmodule]
-                       do
-                               var nmoduledecl = nmodule.n_moduledecl
-                               if nmoduledecl == null then break label x
-                               var ndoc = nmoduledecl.n_doc
-                               if ndoc == null then break label x
-                               tc = new HTMLTag("testcase")
-                               # NOTE: jenkins expects a '.' in the classname attr
-                               tc.attr("classname", mmodule.full_name + ".<module>")
-                               tc.attr("name", "<module>")
-                               d2m.extract(ndoc, tc)
-                       end label x
-                       for nclassdef in nmodule.n_classdefs do
-                               var mclassdef = nclassdef.mclassdef.as(not null)
-                               if nclassdef isa AStdClassdef then
-                                       var ndoc = nclassdef.n_doc
-                                       if ndoc != null then
-                                               tc = new HTMLTag("testcase")
-                                               tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name)
-                                               tc.attr("name", "<class>")
-                                               d2m.extract(ndoc, tc)
-                                       end
+               do
+                       var nmoduledecl = nmodule.n_moduledecl
+                       if nmoduledecl == null then break label x
+                       var ndoc = nmoduledecl.n_doc
+                       if ndoc == null then break label x
+                       tc = new HTMLTag("testcase")
+                       # NOTE: jenkins expects a '.' in the classname attr
+                       tc.attr("classname", mmodule.full_name + ".<module>")
+                       tc.attr("name", "<module>")
+                       d2m.extract(ndoc, tc)
+               end label x
+               for nclassdef in nmodule.n_classdefs do
+                       var mclassdef = nclassdef.mclassdef.as(not null)
+                       if nclassdef isa AStdClassdef then
+                               var ndoc = nclassdef.n_doc
+                               if ndoc != null then
+                                       tc = new HTMLTag("testcase")
+                                       tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name)
+                                       tc.attr("name", "<class>")
+                                       d2m.extract(ndoc, tc)
                                end
-                               for npropdef in nclassdef.n_propdefs do
-                                       var mpropdef = npropdef.mpropdef.as(not null)
-                                       var ndoc = npropdef.n_doc
-                                       if ndoc != null then
-                                               tc = new HTMLTag("testcase")
-                                               tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name)
-                                               tc.attr("name", mpropdef.mproperty.full_name)
-                                               d2m.extract(ndoc, tc)
-                                       end
+                       end
+                       for npropdef in nclassdef.n_propdefs do
+                               var mpropdef = npropdef.mpropdef.as(not null)
+                               var ndoc = npropdef.n_doc
+                               if ndoc != null then
+                                       tc = new HTMLTag("testcase")
+                                       tc.attr("classname", mmodule.full_name + "." + mclassdef.mclass.full_name)
+                                       tc.attr("name", mpropdef.mproperty.full_name)
+                                       d2m.extract(ndoc, tc)
                                end
                        end
                end
index 4b5183f..84c0815 100644 (file)
@@ -370,16 +370,16 @@ end
 redef class MModule
        super IndexMatch
        # prototype of the module
-       #       module ownername::name
+       #       module name
        private fun prototype: String do return "module {name.bold}"
 
        # namespace of the module
-       #       ownername::name
+       #       project::name
        private fun namespace: String do
-               if public_owner == null then
+               if mgroup == null or mgroup.mproject.name == self.name then
                        return self.name
                else
-                       return "{public_owner.namespace}::{self.name}"
+                       return "{mgroup.mproject}::{self.name}"
                end
        end
 
@@ -408,7 +408,7 @@ redef class MModule
                # imported modules
                var imports = new Array[MModule]
                for mmodule in in_importation.direct_greaters.to_a do
-                       if not in_nesting.direct_greaters.has(mmodule) then imports.add(mmodule)
+                       imports.add(mmodule)
                end
                if not imports.is_empty then
                        sorter.sort(imports)
@@ -421,19 +421,6 @@ redef class MModule
                        end
                        pager.indent = pager.indent - 1
                end
-               # nested modules
-               var nested = in_nesting.direct_greaters.to_a
-               if not nested.is_empty then
-                       sorter.sort(nested)
-                       pager.add("")
-                       pager.add("== nested modules".bold)
-                       pager.indent = pager.indent + 1
-                       for mmodule in nested do
-                               pager.add("")
-                               mmodule.preview(index, pager)
-                       end
-                       pager.indent = pager.indent - 1
-               end
                # mclassdefs
                var csorter = new MClassDefNameSorter
                var intros = new Array[MClassDef]
index d0dd71d..629cdd4 100644 (file)
@@ -1700,6 +1700,7 @@ class SeparateRuntimeFunction
                        v.add("return {frame.returnvar.as(not null)};")
                end
                v.add("\}")
+               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
 end
 
@@ -1777,6 +1778,7 @@ class VirtualRuntimeFunction
                        v.add("return {frame.returnvar.as(not null)};")
                end
                v.add("\}")
+               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}--{mmethoddef.location.line_start})"
        end
 
        # TODO ?
diff --git a/src/test_phase.nit b/src/test_phase.nit
new file mode 100644 (file)
index 0000000..1b90355
--- /dev/null
@@ -0,0 +1,57 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Stub for loading a runing phases on a bunch of modules
+#
+# The point is to refine this module is executable that does things.
+# One simple way is to use the `-m` option of engines plug various phases.
+module test_phase
+
+import modelbuilder
+
+# Create a tool context to handle options and paths
+var toolcontext = new ToolContext
+# We do not add other options, so process them now!
+toolcontext.process_options
+
+# Get arguments
+var arguments = toolcontext.option_context.rest
+if arguments.is_empty or toolcontext.opt_help.value then
+       toolcontext.option_context.usage
+       return
+end
+
+# We need a model to collect stufs
+var model = new Model
+# An a model builder to parse files
+var modelbuilder = new ModelBuilder(model, toolcontext)
+
+# Here we load an process all modules passed on the command line
+var mmodules = modelbuilder.parse(arguments)
+modelbuilder.run_phases
+
+if mmodules.length == 0 then
+       return
+end
+
+var mainmodule: MModule
+if mmodules.length == 1 then
+       mainmodule = mmodules.first
+else
+       # We need a main module, so we build it by importing all modules
+       mainmodule = new MModule(model, null, "<main>", new Location(null, 0, 0, 0, 0))
+       mainmodule.set_imported_mmodules(mmodules)
+end
+
+toolcontext.run_global_phases(mainmodule)
index f984808..7b96e23 100644 (file)
@@ -1 +1 @@
-module_1.nit -d out/nitdoc_args1.write
+module_1.nit -d $WRITE
index f118275..3555fdb 100644 (file)
@@ -1 +1 @@
---all base_simple3.nit -d out/nitmetrics_args1.write
+--all base_simple3.nit -d $WRITE
index 2f1b55a..7572830 100644 (file)
@@ -1 +1 @@
-test_nitunit.nit --no-color -o out/nitunit_args1.write
+test_nitunit.nit --no-color -o $WRITE
index 095aae7..6df8be4 100644 (file)
@@ -1 +1 @@
-error_mod_unk.nit:17,8--11: Error: cannot find module dfgd from error_mod_unk. tried ./dfgd.nit, dfgd.nit, ../lib/standard/dfgd.nit, ../lib/standard/collection/dfgd.nit, alt/dfgd.nit, ../lib/dfgd.nit, ../lib/dfgd.nit
+error_mod_unk.nit:17,8--11: Error: cannot find module dfgd from error_mod_unk. tried dfgd.nit, ../lib/standard/dfgd.nit, ../lib/standard/collection/dfgd.nit, alt/dfgd.nit, ../lib/dfgd.nit, ../lib/dfgd.nit
index 55c09f5..7b0a116 100644 (file)
@@ -22,6 +22,7 @@
   --no-check-autocast         Disable implicit casts on unsafe expression usage (dangerous)
   --no-check-other            Disable implicit tests: unset attribute, null receiver (dangerous)
   --typing-test-metrics       Enable static and dynamic count of all type tests
+  --nit-stacktrace            Enables the use of gperf to bind C to Nit function names when encountering a Stack trace at runtime
   --separate                  Use separate compilation
   --no-inline-intern          Do not inline call to intern methods
   --no-union-attribute        Put primitive attibutes in a box instead of an union
index a773cd8..ebc4cab 100644 (file)
@@ -36,42 +36,6 @@ Distribution of direct smallers
  average value: 0.0
  distribution:
   <=0: sub-population=1 (100.00%); cumulated value=0 (na%)
-## Module nesting hierarchy
-Number of nodes: 1
-Number of edges: 1 (1.00 per node)
-Number of direct edges: 0 (0.0 per node)
-Distribution of greaters
- population: 1
- minimum value: 1
- maximum value: 1
- total value: 1
- average value: 1.00
- distribution:
-  <=1: sub-population=1 (100.00%); cumulated value=1 (100.00%)
-Distribution of direct greaters
- population: 1
- minimum value: 0
- maximum value: 0
- total value: 0
- average value: 0.0
- distribution:
-  <=0: sub-population=1 (100.00%); cumulated value=0 (na%)
-Distribution of smallers
- population: 1
- minimum value: 1
- maximum value: 1
- total value: 1
- average value: 1.00
- distribution:
-  <=1: sub-population=1 (100.00%); cumulated value=1 (100.00%)
-Distribution of direct smallers
- population: 1
- minimum value: 0
- maximum value: 0
- total value: 0
- average value: 0.0
- distribution:
-  <=0: sub-population=1 (100.00%); cumulated value=0 (na%)
 ## Classdef hierarchy
 Number of nodes: 8
 Number of edges: 22 (2.75 per node)
@@ -362,7 +326,10 @@ Number of live method definitions: 14
 Number of live runtime cast types (ie used in as and isa): 0
        
 Number of dead method definitions of live methods: 0
+generating out/nitmetrics_args1.write/project_hierarchy.dot
+generating out/nitmetrics_args1.write/module_hierarchy.dot
 class_hierarchy.dot
 classdef_hierarchy.dot
 model.html
 module_hierarchy.dot
+project_hierarchy.dot
index eb930af..b37f396 100644 (file)
@@ -1,10 +1,10 @@
-test_nitunit.nit:18,1--19,0: ERROR: test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit2.nit): Runtime error: Assert failed (.nitunit/test_nitunit2.nit:5)
+test_nitunit.nit:20,1--22,0: ERROR: test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit2.nit): Runtime error: Assert failed (.nitunit/test_nitunit2.nit:5)
 
-test_nitunit.nit:20,2--21,0: FAILURE: test_nitunit.test_nitunit::X.test_nitunit::X::toto (in .nitunit/test_nitunit3.nit): .nitunit/test_nitunit3.nit:5,9--28: Error: Method or variable 'undefined_identifier' unknown in Sys.
+test_nitunit.nit:23,2--25,0: FAILURE: test_nitunit.test_nitunit::X.test_nitunit::X::toto (in .nitunit/test_nitunit3.nit): .nitunit/test_nitunit3.nit:5,9--28: Error: Method or variable 'undefined_identifier' unknown in Sys.
 
-<testsuites><testsuite package="test_nitunit"><testcase classname="test_nitunit.&lt;module&gt;" name="&lt;module&gt;"><system-err/><system-out> assert true
-</system-out></testcase><testcase classname="test_nitunit.test_nitunit::X" name="&lt;class&gt;"><system-err/><system-out> assert false
+<testsuites><testsuite package="test_nitunit"><testcase classname="test_nitunit.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out> assert true
+</system-out></testcase><testcase classname="test_nitunit.test_nitunit::X" name="&lt;class&gt;"><system-err></system-err><system-out> assert false
 </system-out><error message="Runtime error: Assert failed (.nitunit/test_nitunit2.nit:5)
-"/></testcase><testcase classname="test_nitunit.test_nitunit::X" name="test_nitunit::X::toto"><system-err/><system-out> assert undefined_identifier
+"></error></testcase><testcase classname="test_nitunit.test_nitunit::X" name="test_nitunit::X::toto"><system-err></system-err><system-out> assert undefined_identifier
 </system-out><failure message=".nitunit/test_nitunit3.nit:5,9--28: Error: Method or variable 'undefined_identifier' unknown in Sys.
-"/></testcase></testsuite></testsuites>
\ No newline at end of file
+"></failure></testcase></testsuite></testsuites>
\ No newline at end of file
diff --git a/tests/sav/test_phase.res b/tests/sav/test_phase.res
new file mode 100644 (file)
index 0000000..8bc4233
--- /dev/null
@@ -0,0 +1,12 @@
+  -W, --warn              Show warnings
+  -q, --quiet             Do not show warnings
+  --stop-on-first-error   Stop on first error
+  --no-color              Do not use color to display errors and warnings
+  --log                   Generate various log files
+  --log-dir               Directory where to generate log files
+  -h, -?, --help          Show Help (This screen)
+  --version               Show version and exit
+  -v, --verbose           Verbose
+  -I, --path              Set include path for loaders (may be used more than once)
+  --only-parse            Only proceed to parse step of loaders
+  --only-metamodel        Stop after meta-model processing
diff --git a/tests/test_nitunit.nit b/tests/test_nitunit.nit
new file mode 100644 (file)
index 0000000..4afd56d
--- /dev/null
@@ -0,0 +1,26 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# a 'success' unit test (pass)
+#     assert true
+module test_nitunit
+# a 'error' unit test (do not pass)
+#     assert false
+class X
+       # a 'failure' unit test (does not compile)
+       #     assert undefined_identifier
+       fun toto do end
+end
index 91e45bd..a7af53c 100755 (executable)
@@ -6,4 +6,19 @@ do
        y="$x/$bx.nit"
        test -f $y && list="$list $y"
 done
-./tests.sh "$@" *.nit ../examples/*.nit ../examples/leapfrog/leapfrog.nit ../examples/shoot/src/shoot_logic.nit ../lib/*.nit $list ../src/nitdoc.nit ../src/test_parser.nit ../src/nit.nit ../src/nitmetrics.nit ../src/nitg.nit ../src/nitx.nit ../src/netdbg.nit ../src/nitlight.nit ../contrib/sort_downloads/src/sort_downloads.nit
+./tests.sh "$@" *.nit \
+       ../examples/*.nit \
+       ../examples/leapfrog/leapfrog.nit \
+       ../examples/shoot/src/shoot_logic.nit \
+       ../lib/*.nit $list \
+       ../src/nitdoc.nit \
+       ../src/test_parser.nit \
+       ../src/test_phase.nit \
+       ../src/nit.nit \
+       ../src/nitmetrics.nit \
+       ../src/nitg.nit \
+       ../src/nitx.nit \
+       ../src/nitunit.nit \
+       ../src/netdbg.nit \
+       ../src/nitlight.nit \
+       ../contrib/sort_downloads/src/sort_downloads.nit
index 57f0a37..8de440f 100755 (executable)
@@ -176,7 +176,7 @@ function process_result()
                fi
                echo >>$xml "<error message='fail out/$pattern.res $NSAV'/>"
                echo >>$xml "<system-out><![CDATA["
-               head >>$xml -n 50 out/$pattern.diff.sav.log
+               cat -v out/$pattern.diff.sav.log | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
@@ -188,7 +188,7 @@ function process_result()
                fi
                echo >>$xml "<error message='changed out/$pattern.res $NFIXME'/>"
                echo >>$xml "<system-out><![CDATA["
-               head >>$xml -n 50 out/$pattern.diff.sav.log
+               cat -v out/$pattern.diff.sav.log | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
@@ -200,13 +200,13 @@ function process_result()
                fi
                echo >>$xml "<skipped/>"
                echo >>$xml "<system-out><![CDATA["
-               cat  >>$xml out/$pattern.res
+               cat -v >>$xml out/$pattern.res
                echo >>$xml "]]></system-out>"
                nos="$nos $pattern"
        fi
        if test -s out/$pattern.cmp.err; then
                echo >>$xml "<system-err><![CDATA["
-               cat  >>$xml out/$pattern.cmp.err
+               cat -v >>$xml out/$pattern.cmp.err
                echo >>$xml "]]></system-err>"
        fi
        echo >>$xml "</testcase>"
@@ -456,7 +456,7 @@ END
                                        test -z "$tap" && echo -n "==> $name "
                                        echo "./$ff.bin $args" > "./$fff.bin"
                                        chmod +x "./$fff.bin"
-                                       sh -c "NIT_NO_STACK=1 $TIMEOUT ./$fff.bin < $inputs > $fff.res 2>$fff.err"
+                                       WRITE="$fff.write" sh -c "NIT_NO_STACK=1 $TIMEOUT ./$fff.bin < $inputs > $fff.res 2>$fff.err"
                                        if [ "x$verbose" = "xtrue" ]; then
                                                cat "$fff.res"
                                                cat >&2 "$fff.err"