Merge: Use new constructors
authorJean Privat <jean@pryen.org>
Thu, 6 Nov 2014 01:02:17 +0000 (20:02 -0500)
committerJean Privat <jean@pryen.org>
Thu, 6 Nov 2014 01:02:17 +0000 (20:02 -0500)
Since c_src is regenerated, new-style-constructors are available in lib and tools.

Most of the job was to just to remove useless `init` and sometime adding a `noinit` or a default value to attributes.

Pull-Request: #866
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

69 files changed:
Makefile
README
bin/nitc
c_src/Makefile
c_src/abstract_compiler.sep.1.c
c_src/nith.mk
contrib/neo_doxygen/Makefile [new file with mode: 0644]
contrib/neo_doxygen/gen-all.sh [new file with mode: 0755]
contrib/neo_doxygen/gen-one.sh [new file with mode: 0755]
contrib/neo_doxygen/sh-lib/errors.sh [new file with mode: 0644]
contrib/neo_doxygen/src/doxml/compounddef.nit [new file with mode: 0644]
contrib/neo_doxygen/src/doxml/doc.nit [new file with mode: 0644]
contrib/neo_doxygen/src/doxml/doxml.nit [new file with mode: 0644]
contrib/neo_doxygen/src/doxml/entitydef.nit [new file with mode: 0644]
contrib/neo_doxygen/src/doxml/language_specific.nit [new file with mode: 0644]
contrib/neo_doxygen/src/doxml/listener.nit [new file with mode: 0644]
contrib/neo_doxygen/src/doxml/memberdef.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/class_compound.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/graph.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/linked_text.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/location.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/member.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/model.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/module_compound.nit [new file with mode: 0644]
contrib/neo_doxygen/src/model/type_entity.nit [new file with mode: 0644]
contrib/neo_doxygen/src/neo_doxygen.nit [new file with mode: 0644]
contrib/online_ide/sources/nit/pnacl_nit.nit
doc/advanced_options [deleted file]
lib/console.nit
lib/more_collections.nit
lib/neo4j/curl_json.nit
lib/opts.nit
lib/standard/string_search.nit
share/man/Makefile
share/man/README.md
share/man/man1/nitc.1 [new symlink]
share/man/nitg.md
src/c_tools.nit
src/compiler/abstract_compiler.nit
src/compiler/android_platform.nit
src/compiler/compiler_ffi.nit
src/doc/doc_model.nit
src/doc/doc_pages.nit
src/doc/model_ext.nit [new file with mode: 0644]
src/ffi/c.nit
src/ffi/ffi.nit
src/ffi/pkgconfig.nit
src/modelbuilder.nit
src/modelize/modelize_property.nit
src/neo.nit
src/nit.nit
src/semantize/auto_super_init.nit
src/semantize/typing.nit
src/testing/testing_doc.nit
src/testing/testing_suite.nit
src/toolcontext.nit
tests/base_new.nit
tests/nit.args
tests/sav/base_error_new_abstract.res
tests/sav/base_error_new_interface.res
tests/sav/base_new.res
tests/sav/base_new_alt5.res
tests/sav/base_new_alt6.res
tests/sav/base_new_alt7.res
tests/sav/base_new_alt8.res
tests/sav/error_needed_method_alt2.res
tests/sav/test_toolcontext_args1.res
tests/sav/test_toolcontext_args2.res
tests/tests.sh

index 636e0ce..326d49b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -66,12 +66,16 @@ doc/nitc/index.html: bin/nitdoc bin/nitls
                --piwik-tracker "pratchett.info.uqam.ca/piwik/" \
                --piwik-site-id "3"
 
+man:
+       $(MAKE) -C share/man
+
 clean:
        rm -rf -- .nit_compile 2> /dev/null || true
        rm -rf -- doc/stdlib doc/nitc || true
        cd c_src; make clean
        cd src; make clean
        cd tests; make clean
+       cd share/man; make clean
        for m in $(PROGS); do \
                $(MAKE) clean -C "$$m"; \
                test -d $$m/.nit_compile && rm -r $$m/.nit_compile; \
diff --git a/README b/README
index 2b4e22e..adb80ee 100644 (file)
--- a/README
+++ b/README
@@ -17,6 +17,7 @@ Some Nit features:
 Requirement:
 
        * gcc           http://gcc.gnu.org/
+       * pkg-config    http://www.freedesktop.org/wiki/Software/pkg-config/
        * 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
@@ -24,7 +25,7 @@ Requirement:
 
 Those are available in most linux distributions
 
-    # sudo apt-get install build-essential ccache libgc-dev graphviz libunwind
+    # sudo apt-get install build-essential ccache libgc-dev graphviz libunwind pkg-config
 
 Important files and directory:
 
index cd4c670..42b4e3a 100755 (executable)
--- a/bin/nitc
+++ b/bin/nitc
@@ -1,4 +1,3 @@
 #!/bin/bash
-self=`readlink /proc/$$/fd/255`
-dir=`dirname "$self"`
+dir=`dirname "$BASH_SOURCE"`
 exec "$dir/nitg" "$@"
index db97fe4..b7e5ab8 100644 (file)
@@ -3,7 +3,7 @@ CXX = ccache c++
 CFLAGS = -g -O2 -Wno-unused-value -Wno-switch
 CINCL = 
 LDFLAGS ?= 
-LDLIBS  ?= -lm -lgc 
+LDLIBS  ?= -lm 
 
 NEED_LIBUNWIND := YesPlease
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
@@ -296,41 +296,49 @@ nith.types.4.o: nith.types.4.c
 nith.types.5.o: nith.types.5.c
        $(CC) $(CFLAGS) $(CINCL) -c -o nith.types.5.o nith.types.5.c
 
+# does pkg-config exists?
+ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
+$(error "Command `pkg-config` not found. Please install it")
+endif
+# Check for library bdw-gc
+ifneq ($(shell pkg-config --exists 'bdw-gc'; echo $$?), 0)
+$(error "pkg-config: package bdw-gc is not found.")
+endif
 time_nit.extern.o: time_nit.c
-       $(CC) $(CFLAGS)  -c -o time_nit.extern.o time_nit.c
+       $(CC) $(CFLAGS)   -c -o time_nit.extern.o time_nit.c
 
 string_nit.extern.o: string_nit.c
-       $(CC) $(CFLAGS)  -c -o string_nit.extern.o string_nit.c
+       $(CC) $(CFLAGS)   -c -o string_nit.extern.o string_nit.c
 
 file_nit.extern.o: file_nit.c
-       $(CC) $(CFLAGS)  -c -o file_nit.extern.o file_nit.c
+       $(CC) $(CFLAGS)   -c -o file_nit.extern.o file_nit.c
 
 exec_nit.extern.o: exec_nit.c
-       $(CC) $(CFLAGS)  -c -o exec_nit.extern.o exec_nit.c
+       $(CC) $(CFLAGS)   -c -o exec_nit.extern.o exec_nit.c
 
 tables_nit.extern.o: tables_nit.c
-       $(CC) $(CFLAGS)  -c -o tables_nit.extern.o tables_nit.c
+       $(CC) $(CFLAGS)   -c -o tables_nit.extern.o tables_nit.c
 
 c_functions_hash.extern.o: c_functions_hash.c
-       $(CC) $(CFLAGS)  -c -o c_functions_hash.extern.o c_functions_hash.c
+       $(CC) $(CFLAGS)   -c -o c_functions_hash.extern.o c_functions_hash.c
 
 gc_chooser.extern.o: gc_chooser.c
-       $(CC) $(CFLAGS) -DWITH_LIBGC -c -o gc_chooser.extern.o gc_chooser.c
+       $(CC) $(CFLAGS) -DWITH_LIBGC `pkg-config --cflags bdw-gc` -c -o gc_chooser.extern.o gc_chooser.c
 
 string._ffi.extern.o: string._ffi.c
-       $(CC) $(CFLAGS)  -c -o string._ffi.extern.o string._ffi.c
+       $(CC) $(CFLAGS)   -c -o string._ffi.extern.o string._ffi.c
 
 string._nitni.extern.o: string._nitni.c
-       $(CC) $(CFLAGS)  -c -o string._nitni.extern.o string._nitni.c
+       $(CC) $(CFLAGS)   -c -o string._nitni.extern.o string._nitni.c
 
 kernel._ffi.extern.o: kernel._ffi.c
-       $(CC) $(CFLAGS)  -c -o kernel._ffi.extern.o kernel._ffi.c
+       $(CC) $(CFLAGS)   -c -o kernel._ffi.extern.o kernel._ffi.c
 
 kernel._nitni.extern.o: kernel._nitni.c
-       $(CC) $(CFLAGS)  -c -o kernel._nitni.extern.o kernel._nitni.c
+       $(CC) $(CFLAGS)   -c -o kernel._nitni.extern.o kernel._nitni.c
 
 nitg: nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o
-       $(CC) $(LDFLAGS) -o nitg nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o $(LDLIBS)
+       $(CC) $(LDFLAGS) -o nitg nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o $(LDLIBS) `pkg-config --libs bdw-gc`
 
 clean:
        rm nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o 2>/dev/null
index da573c6..e9e6534 100644 (file)
@@ -3498,8 +3498,8 @@ abstract_compiler__AbstractCompiler__build_c_to_nit_bindings(var_compiler); /* D
 if (varonce26) {
 var27 = varonce26;
 } else {
-var28 = "-DWITH_LIBGC";
-var29 = 12;
+var28 = "-DWITH_LIBGC `pkg-config --cflags bdw-gc` ";
+var29 = 42;
 var30 = string__NativeString__to_s_with_length(var28, var29);
 var27 = var30;
 varonce26 = var27;
@@ -5985,8 +5985,8 @@ varonce65 = var66;
 if (varonce70) {
 var71 = varonce70;
 } else {
-var72 = "\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc ";
-var73 = 33;
+var72 = "\nLDFLAGS ?= \nLDLIBS  ?= -lm `pkg-config --libs bdw-gc`";
+var73 = 55;
 var74 = string__NativeString__to_s_with_length(var72, var73);
 var71 = var74;
 varonce70 = var71;
index db97fe4..b7e5ab8 100644 (file)
@@ -3,7 +3,7 @@ CXX = ccache c++
 CFLAGS = -g -O2 -Wno-unused-value -Wno-switch
 CINCL = 
 LDFLAGS ?= 
-LDLIBS  ?= -lm -lgc 
+LDLIBS  ?= -lm 
 
 NEED_LIBUNWIND := YesPlease
 uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
@@ -296,41 +296,49 @@ nith.types.4.o: nith.types.4.c
 nith.types.5.o: nith.types.5.c
        $(CC) $(CFLAGS) $(CINCL) -c -o nith.types.5.o nith.types.5.c
 
+# does pkg-config exists?
+ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
+$(error "Command `pkg-config` not found. Please install it")
+endif
+# Check for library bdw-gc
+ifneq ($(shell pkg-config --exists 'bdw-gc'; echo $$?), 0)
+$(error "pkg-config: package bdw-gc is not found.")
+endif
 time_nit.extern.o: time_nit.c
-       $(CC) $(CFLAGS)  -c -o time_nit.extern.o time_nit.c
+       $(CC) $(CFLAGS)   -c -o time_nit.extern.o time_nit.c
 
 string_nit.extern.o: string_nit.c
-       $(CC) $(CFLAGS)  -c -o string_nit.extern.o string_nit.c
+       $(CC) $(CFLAGS)   -c -o string_nit.extern.o string_nit.c
 
 file_nit.extern.o: file_nit.c
-       $(CC) $(CFLAGS)  -c -o file_nit.extern.o file_nit.c
+       $(CC) $(CFLAGS)   -c -o file_nit.extern.o file_nit.c
 
 exec_nit.extern.o: exec_nit.c
-       $(CC) $(CFLAGS)  -c -o exec_nit.extern.o exec_nit.c
+       $(CC) $(CFLAGS)   -c -o exec_nit.extern.o exec_nit.c
 
 tables_nit.extern.o: tables_nit.c
-       $(CC) $(CFLAGS)  -c -o tables_nit.extern.o tables_nit.c
+       $(CC) $(CFLAGS)   -c -o tables_nit.extern.o tables_nit.c
 
 c_functions_hash.extern.o: c_functions_hash.c
-       $(CC) $(CFLAGS)  -c -o c_functions_hash.extern.o c_functions_hash.c
+       $(CC) $(CFLAGS)   -c -o c_functions_hash.extern.o c_functions_hash.c
 
 gc_chooser.extern.o: gc_chooser.c
-       $(CC) $(CFLAGS) -DWITH_LIBGC -c -o gc_chooser.extern.o gc_chooser.c
+       $(CC) $(CFLAGS) -DWITH_LIBGC `pkg-config --cflags bdw-gc` -c -o gc_chooser.extern.o gc_chooser.c
 
 string._ffi.extern.o: string._ffi.c
-       $(CC) $(CFLAGS)  -c -o string._ffi.extern.o string._ffi.c
+       $(CC) $(CFLAGS)   -c -o string._ffi.extern.o string._ffi.c
 
 string._nitni.extern.o: string._nitni.c
-       $(CC) $(CFLAGS)  -c -o string._nitni.extern.o string._nitni.c
+       $(CC) $(CFLAGS)   -c -o string._nitni.extern.o string._nitni.c
 
 kernel._ffi.extern.o: kernel._ffi.c
-       $(CC) $(CFLAGS)  -c -o kernel._ffi.extern.o kernel._ffi.c
+       $(CC) $(CFLAGS)   -c -o kernel._ffi.extern.o kernel._ffi.c
 
 kernel._nitni.extern.o: kernel._nitni.c
-       $(CC) $(CFLAGS)  -c -o kernel._nitni.extern.o kernel._nitni.c
+       $(CC) $(CFLAGS)   -c -o kernel._nitni.extern.o kernel._nitni.c
 
 nitg: nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o
-       $(CC) $(LDFLAGS) -o nitg nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o $(LDLIBS)
+       $(CC) $(LDFLAGS) -o nitg nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o $(LDLIBS) `pkg-config --libs bdw-gc`
 
 clean:
        rm nith.classes.1.o nith.classes.2.o nith.classes.3.o nith.classes.4.o nith.classes.5.o nith.classes.6.o nith.classes.7.o nith.main.1.o nith.sep.1.o modelize_property.sep.1.o modelize_property.sep.2.o modelize_class.sep.1.o modelbuilder.sep.1.o model.sep.1.o model.sep.2.o mmodule.sep.1.o location.sep.1.o string.sep.1.o string.sep.2.o math.sep.1.o kernel.sep.1.o abstract_collection.sep.1.o list.sep.1.o array.sep.1.o sorter.sep.1.o hash_collection.sep.1.o environ.sep.1.o file.sep.1.o stream.sep.1.o string_search.sep.1.o time.sep.1.o exec.sep.1.o mproject.sep.1.o model_base.sep.1.o more_collections.sep.1.o poset.sep.1.o mdoc.sep.1.o phase.sep.1.o toolcontext.sep.1.o opts.sep.1.o version.sep.1.o template.sep.1.o parser.sep.1.o parser.sep.2.o parser.sep.3.o parser.sep.4.o parser.sep.5.o parser.sep.6.o parser_prod.sep.1.o parser_prod.sep.2.o parser_prod.sep.3.o parser_prod.sep.4.o parser_prod.sep.5.o lexer.sep.1.o parser_nodes.sep.1.o lexer_work.sep.1.o tables.sep.1.o parser_work.sep.1.o annotation.sep.1.o literal.sep.1.o transform.sep.1.o astbuilder.sep.1.o typing.sep.1.o typing.sep.2.o typing.sep.3.o local_var_init.sep.1.o flow.sep.1.o scope.sep.1.o astvalidation.sep.1.o auto_super_init.sep.1.o rapid_type_analysis.sep.1.o separate_erasure_compiler.sep.1.o separate_erasure_compiler.sep.2.o separate_compiler.sep.1.o separate_compiler.sep.2.o separate_compiler.sep.3.o separate_compiler.sep.4.o separate_compiler.sep.5.o abstract_compiler.sep.1.o abstract_compiler.sep.2.o abstract_compiler.sep.3.o abstract_compiler.sep.4.o abstract_compiler.sep.5.o platform.sep.1.o c_tools.sep.1.o mixin.sep.1.o coloring.sep.1.o nith.types.1.o nith.types.2.o nith.types.3.o nith.types.4.o nith.types.5.o time_nit.extern.o string_nit.extern.o file_nit.extern.o exec_nit.extern.o tables_nit.extern.o c_functions_hash.extern.o gc_chooser.extern.o string._ffi.extern.o string._nitni.extern.o kernel._ffi.extern.o kernel._nitni.extern.o 2>/dev/null
diff --git a/contrib/neo_doxygen/Makefile b/contrib/neo_doxygen/Makefile
new file mode 100644 (file)
index 0000000..ed82923
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+NITG=../../bin/nitg
+NITG_FLAGS=--dir bin
+NEO4J_DIR=/var/lib/neo4j
+
+.PHONY: bin reset-neo
+
+# Compile the tool.
+bin:
+       mkdir -p bin
+       ../../bin/nitg --dir bin src/neo_doxygen.nit
+
+# Reset the local graph.
+reset-neo:
+       sudo -u neo4j "${NEO4J_DIR}/bin/neo4j" stop \
+       && sudo -u neo4j rm -rf "${NEO4J_DIR}/data/graph.db" \
+       && sudo -u neo4j "${NEO4J_DIR}/bin/neo4j" start
diff --git a/contrib/neo_doxygen/gen-all.sh b/contrib/neo_doxygen/gen-all.sh
new file mode 100755 (executable)
index 0000000..b43ceab
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/bash
+
+# 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.
+
+# ./gen-all.sh <source_language> <directory>
+#
+# Document all projects in the specified directory.
+#
+# Projects are direct sub-directories of the specified directory.
+# Every project directory must contain a `.nx_config` file.
+# Also, every project must include the Doxygen XML output in its `doxygen/xml`
+# directory.
+
+NEO_DOXYGEN="${PWD}/bin/neo_doxygen"
+NX="${PWD}/../../bin/nx"
+
+for dir in "$2"/*; do
+       if [ -d "$dir" ]; then
+               if [ -f "$dir/.nx_config" ]; then
+                       # Note: gen-one.sh already prints errors.
+                       ./gen-one.sh "$1" "$dir" || exit
+               fi
+       fi
+done
diff --git a/contrib/neo_doxygen/gen-one.sh b/contrib/neo_doxygen/gen-one.sh
new file mode 100755 (executable)
index 0000000..2621c8d
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+
+# 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.
+
+# ./gen-one.sh <source_language> <directory>
+#
+# Document the project in the specified directory.
+#
+# The project name is the basename of the specified directory.
+# The project directory must contain a `.nx_config` file.
+# Also, the directory must include the Doxygen XML output in its `doxygen/xml`
+# subdirectory.
+
+NEO_DOXYGEN="${PWD}/bin/neo_doxygen"
+NX="${PWD}/../../bin/nx"
+dir=$2
+
+. sh-lib/errors.sh
+
+echo "$0: Documenting \"${dir##*/}\"..."
+pushd "$dir"
+try "$NEO_DOXYGEN" --src-lang "$1" --dest http://localhost:7474 -- "${dir##*/}" "$dir/doxygen/xml"
+try echo "$0: [done] neo_doxygen"
+try "$NX" neo doc "${dir##*/}"
+try echo "$0: [done] nx"
+popd
diff --git a/contrib/neo_doxygen/sh-lib/errors.sh b/contrib/neo_doxygen/sh-lib/errors.sh
new file mode 100644 (file)
index 0000000..dc5335b
--- /dev/null
@@ -0,0 +1,46 @@
+#! /bin/bash
+
+# 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.
+
+# Error handling.
+
+# The program’s name.
+prog_name=$0
+
+# Run the specified command and exit in case of error.
+function try {
+       "$@"
+       local status=$?
+       if [ $status -ne 0 ]; then
+               >&2 echo "${prog_name}: Error: \`$1\` failed with exit status ${status}."
+               trace
+               exit "$status"
+       fi
+       return 0
+}
+
+# Print the stack trace.
+function trace {
+       local frame=0
+       >&2 caller $frame
+       local has_next=$?
+       while [ $has_next = 0 ]; do
+               ((frame++));
+               >&2 caller $frame
+               has_next=$?
+       done
+       >&2 echo "---"
+       return 0
+}
diff --git a/contrib/neo_doxygen/src/doxml/compounddef.nit b/contrib/neo_doxygen/src/doxml/compounddef.nit
new file mode 100644 (file)
index 0000000..4cd42ca
--- /dev/null
@@ -0,0 +1,167 @@
+# 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.
+
+# `compounddef` element reading.
+module doxml::compounddef
+
+import memberdef
+import more_collections
+
+# Processes the content of a `compounddef` element.
+class CompoundDefListener
+       super EntityDefListener
+
+       var compound: Compound is writable, noinit
+       private var memberdef: MemberDefListener is noinit
+       private var param_listener: TypeParamListener is noinit
+
+       # Default attributes for members in the current section.
+       private var member_defaults: MemberDefaults is noinit
+
+       # For each section kind, default attributes for member in that section.
+       private var section_kinds: DefaultMap[String, MemberDefaults] is noinit
+
+
+       # Attributes of the current `<basecompoundref>` element.
+
+       private var refid = ""
+       private var prot = ""
+       private var virt = ""
+
+
+       init do
+               super
+               var defaults = new MemberDefaults("public", false, false)
+
+               memberdef = new MemberDefListener(reader, self)
+               param_listener = new TypeParamListener(reader, self)
+
+               member_defaults = defaults
+               section_kinds = new DefaultMap[String, MemberDefaults](defaults)
+
+               section_kinds["public-type"] = defaults
+               section_kinds["public-func"] = defaults
+               section_kinds["public-attrib"] = defaults
+               section_kinds["public-slot"] = defaults
+               defaults = new MemberDefaults("public", true, false)
+               section_kinds["public-static-func"] = defaults
+               section_kinds["public-static-attrib"] = defaults
+
+               defaults = new MemberDefaults("protected", false, false)
+               section_kinds["protected-type"] = defaults
+               section_kinds["protected-func"] = defaults
+               section_kinds["protected-attrib"] = defaults
+               section_kinds["protected-slot"] = defaults
+               defaults = new MemberDefaults("protected", true, false)
+               section_kinds["protected-static-func"] = defaults
+               section_kinds["protected-static-attrib"] = defaults
+
+               defaults = new MemberDefaults("package", false, false)
+               section_kinds["package-type"] = defaults
+               section_kinds["package-func"] = defaults
+               section_kinds["package-attrib"] = defaults
+               defaults = new MemberDefaults("package", true, false)
+               section_kinds["package-static-func"] = defaults
+               section_kinds["package-static-attrib"] = defaults
+
+               defaults = new MemberDefaults("private", false, false)
+               section_kinds["private-type"] = defaults
+               section_kinds["private-func"] = defaults
+               section_kinds["private-attrib"] = defaults
+               section_kinds["private-slot"] = defaults
+               defaults = new MemberDefaults("private", true, false)
+               section_kinds["private-static-func"] = defaults
+               section_kinds["private-static-attrib"] = defaults
+
+               defaults = new MemberDefaults("public", true, true)
+               section_kinds["related"] = defaults
+               section_kinds["user-defined"] = defaults
+       end
+
+       redef fun entity: Entity do return compound
+
+       redef fun start_dox_element(local_name: String, atts: Attributes) do
+               if ["compoundname", "innerclass", "innernamespace"].has(local_name) then
+                       text.listen_until(dox_uri, local_name)
+                       if ["innerclass", "innernamespace"].has(local_name) then
+                               refid = get_required(atts, "refid")
+                       end
+               else if "basecompoundref" == local_name then
+                       refid = get_optional(atts, "refid", "")
+                       prot = get_optional(atts, "prot", "")
+                       virt = get_optional(atts, "virt", "")
+                       text.listen_until(dox_uri, local_name)
+               else if "memberdef" == local_name then
+                       read_member(atts)
+               else if local_name == "sectiondef" then
+                       member_defaults = section_kinds[get_required(atts, "kind")]
+                       if member_defaults.is_special then
+                               super # TODO
+                       end
+               else if "param" == local_name then
+                       param_listener.listen_until(dox_uri, local_name)
+               else if "templateparamlist" != local_name then
+                       super
+               end
+       end
+
+       redef fun end_dox_element(local_name: String) do
+               if local_name == "compoundname" then
+                       compound.full_name = text.to_s
+               else if local_name == "innerclass" then
+                       compound.declare_class(refid, text.to_s)
+               else if local_name == "innernamespace" then
+                       compound.declare_namespace(refid, text.to_s)
+               else if "memberdef" == local_name then
+                       if not (memberdef.member isa UnknownMember) then
+                               compound.declare_member(memberdef.member)
+                       end
+               else if local_name == "basecompoundref" then
+                       compound.declare_super(refid, text.to_s, prot, virt)
+               else if "param" == local_name and compound isa ClassCompound then
+                       compound.as(ClassCompound).add_type_parameter(param_listener.parameter)
+               else
+                       super
+               end
+       end
+
+       private fun read_member(atts: Attributes) do
+               var kind = get_required(atts, "kind")
+
+               create_member(kind)
+               memberdef.member.model_id = get_required(atts, "id")
+               memberdef.member.visibility = get_optional(atts, "prot",
+                               member_defaults.visibility)
+       end
+
+       private fun create_member(kind: String) do
+               if kind == "variable" then
+                       memberdef.member = new Attribute(compound.graph)
+               else if kind == "function" then
+                       memberdef.member = new Method(compound.graph)
+               else
+                       memberdef.member = new UnknownMember(compound.graph)
+                       noop.listen_until(dox_uri, "memberdef")
+                       return
+               end
+               memberdef.listen_until(dox_uri, "memberdef")
+       end
+end
+
+# Default attributes for members in the current section.
+private class MemberDefaults
+       var visibility: String
+       var is_static: Bool
+       var is_special: Bool
+end
diff --git a/contrib/neo_doxygen/src/doxml/doc.nit b/contrib/neo_doxygen/src/doxml/doc.nit
new file mode 100644 (file)
index 0000000..84a880d
--- /dev/null
@@ -0,0 +1,30 @@
+# 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.
+
+# Documentation reading.
+module doxml::doc
+
+import listener
+
+# Processes documentation.
+class DocListener
+       super TextListener
+
+       var doc: JsonArray = new JsonArray is writable
+
+       redef fun end_listening do
+               super
+               doc.add(to_s)
+       end
+end
diff --git a/contrib/neo_doxygen/src/doxml/doxml.nit b/contrib/neo_doxygen/src/doxml/doxml.nit
new file mode 100644 (file)
index 0000000..280334a
--- /dev/null
@@ -0,0 +1,89 @@
+# 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.
+
+# Doxygen’s XML documents reading.
+module doxml
+
+import compounddef
+
+# Reader for XML documents whose the schema is `compound.xsd`.
+class CompoundFileReader
+       super DoxmlListener
+
+       # The project graph.
+       var model: ProjectGraph
+
+       # The language-specific strategies to use.
+       redef var source_language: SourceLanguage
+
+       private var reader: XMLReader = new XophonReader
+       private var compounddef: CompoundDefListener is noinit
+       private var noop: NoopListener is noinit
+
+       init do
+               compounddef = new CompoundDefListener(reader, self)
+               noop = new NoopListener(reader, self)
+       end
+
+       redef fun graph do return model
+
+       # Read the document at the specified path.
+       fun read(path: String) do
+               reader.content_handler = self
+               reader.parse_file(path)
+               compounddef.compound = new UnknownCompound(model)
+       end
+
+       redef fun start_dox_element(local_name: String, atts: Attributes) do
+               if local_name == "compounddef" then
+                       read_compound(atts)
+               else if "doxygen" != local_name then
+                       noop.listen_until(dox_uri, local_name)
+               end
+       end
+
+       private fun read_compound(atts: Attributes) do
+               var kind = get_required(atts, "kind")
+
+               create_compound(kind)
+               # TODO Make all values of `kind` and `visibility` compatible with the Nit meta-model.
+               if get_bool(atts, "final") then
+                       kind = "final {kind}"
+               end
+               if get_bool(atts, "sealed") then
+                       kind = "sealed {kind}"
+               end
+               if get_bool(atts, "abstract") then
+                       kind = "abstract {kind}"
+               end
+               compounddef.compound.kind = kind
+               compounddef.compound.model_id = get_required(atts, "id")
+               compounddef.compound.visibility = get_optional(atts, "prot", "")
+       end
+
+       private fun create_compound(kind: String) do
+               if kind == "file" then
+                       compounddef.compound = new FileCompound(model)
+               else if kind == "namespace" then
+                       compounddef.compound = new Namespace(model)
+               else if kind == "class" or kind == "interface" or kind == "enum" then
+                       compounddef.compound = new ClassCompound(model)
+               else
+                       compounddef.compound = new UnknownCompound(model)
+                       noop.listen_until(dox_uri, "compounddef")
+                       return
+               end
+               compounddef.listen_until(dox_uri, "compounddef")
+       end
+end
diff --git a/contrib/neo_doxygen/src/doxml/entitydef.nit b/contrib/neo_doxygen/src/doxml/entitydef.nit
new file mode 100644 (file)
index 0000000..6c11a6c
--- /dev/null
@@ -0,0 +1,129 @@
+# 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.
+
+# Common SAX listeners for entity definitions.
+module doxml::entitydef
+
+import doc
+
+# Processes the content of an entity definition.
+abstract class EntityDefListener
+       super StackableListener
+
+       # The inner `TextListener`.
+       protected var text: TextListener is noinit
+
+       # The inner `DocListener`.
+       protected var doc: DocListener is noinit
+
+       # The inner `NoopListener`.
+       protected var noop: NoopListener is noinit
+
+       init do
+               super
+               text = new TextListener(reader, self)
+               doc = new DocListener(reader, self)
+               noop = new NoopListener(reader, self)
+       end
+
+       # The current entity.
+       protected fun entity: Entity is abstract
+
+       redef fun start_dox_element(local_name: String, atts: Attributes) do
+               if ["briefdescription", "detaileddescription", "inbodydescription"].has(local_name) then
+                       doc.doc = entity.doc
+                       doc.listen_until(dox_uri, local_name)
+               else if "location" == local_name then
+                       entity.location = get_location(atts)
+               else
+                       noop.listen_until(dox_uri, local_name)
+               end
+       end
+
+       redef fun end_listening do
+               super
+               entity.put_in_graph
+       end
+
+       # Parse the attributes of a `location` element.
+       protected fun get_location(atts: Attributes): Location do
+               var location = new Location
+
+               location.path = atts.value_ns("", "bodyfile") or else atts.value_ns("", "file")
+               # Doxygen may indicate `[generated]`.
+               if "[generated]" == location.path then location.path = null
+               var line_start = atts.value_ns("", "bodystart") or else atts.value_ns("", "line") or else null
+               if line_start != null then location.line_start = line_start.to_i
+               var line_end = atts.value_ns("", "bodyend")
+               if line_end != null then location.line_end = line_end.to_i
+               var column_start = atts.value_ns("", "column")
+               if column_start != null then location.column_start = column_start.to_i
+               if location.line_start == location.line_end then
+                       location.column_end = location.column_start
+               end
+               return location
+       end
+end
+
+# Processes the content of a `<param>` element.
+abstract class ParamListener[T: Parameter]
+       super EntityDefListener
+
+       # The current parameter.
+       var parameter: T is noinit
+
+       private var type_listener: TypeListener is noinit
+
+       init do
+               super
+               type_listener = new TypeListener(reader, self)
+       end
+
+       redef fun entity do return parameter
+
+       redef fun listen_until(uri, local_name) do
+               super
+               parameter = create_parameter
+       end
+
+       # Create a new parameter.
+       protected fun create_parameter: T is abstract
+
+       redef fun start_dox_element(local_name: String, atts: Attributes) do
+               if "declname" == local_name then
+                       text.listen_until(dox_uri, local_name)
+               else if "type" == local_name then
+                       type_listener.listen_until(dox_uri, local_name)
+               else
+                       super
+               end
+       end
+
+       redef fun end_dox_element(local_name: String) do
+               if "declname" == local_name then
+                       parameter.name = text.to_s
+               else if "type" == local_name then
+                       source_language.apply_parameter_type(parameter, type_listener.linked_text)
+               else
+                       super
+               end
+       end
+end
+
+# Processes the content of a `<param>` element in a `<templateparamlist>` element.
+class TypeParamListener
+       super ParamListener[TypeParameter]
+
+       redef fun create_parameter do return new TypeParameter(graph)
+end
diff --git a/contrib/neo_doxygen/src/doxml/language_specific.nit b/contrib/neo_doxygen/src/doxml/language_specific.nit
new file mode 100644 (file)
index 0000000..bbc6410
--- /dev/null
@@ -0,0 +1,180 @@
+# 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.
+
+# Handle language-specific parts of the importation.
+module doxml::language_specific
+
+import model
+
+# Various importation logics that depend on the project’s language.
+abstract class SourceLanguage
+
+       # Apply the information deduced from `type_text` to `member`.
+       #
+       # `type_text` is the content of the `<type>` element.
+       fun apply_member_type(member: Member, type_text: RawType) do
+               if type_text["text"] != null then
+                       member.static_type = type_text
+               end
+       end
+
+       # Apply the information deduced from `type_text` to `parameter`.
+       #
+       # `type_text` is the content of the `<type>` element.
+       fun apply_parameter_type(parameter: Parameter, type_text: RawType) do
+               if type_text["text"] != null then
+                       parameter.static_type = type_text
+               end
+       end
+
+       # Extract the specified keyword at the beginning of the specified text.
+       #
+       # If the keyword is at the beginning of the specified text, return `true`
+       # and remove the keyword. Else, return false.
+       #
+       # Used to extract some keywords that Doxygen puts in the type.
+       #
+       #     class DummySource
+       #       super JavaSource
+       #     #
+       #       fun test(text: LinkedText, keyword: String): Bool do
+       #               return extract_keyword(text, keyword)
+       #       end
+       #     end
+       #     #
+       #     var text = new RawType(new ProjectGraph(""))
+       #     var dummy = new DummySource
+       #     var res: Bool
+       #     #
+       #     text.add_part("abstract final", "")
+       #     res = dummy.test(text, "static")
+       #     assert not res
+       #     res = dummy.test(text, "abstract")
+       #     assert res
+       #     assert "final" == text["text"].as(JsonArray).first
+       #     res = dummy.test(text, "final")
+       #     assert res
+       #     assert text["text"] == null
+       #     res = dummy.test(text, "abstract")
+       #     assert not res
+       protected fun extract_keyword(text: LinkedText, keyword: String): Bool do
+               var text_array = text["text"]
+               if text_array == null then return false
+               assert text_array isa JsonArray
+               if text_array.is_empty then return false
+
+               var content = text_array.first.as(String).l_trim
+               var link = text.links.first
+               var found = false
+
+               if link == null and content.has_prefix(keyword) then
+                       if keyword.length == content.length then
+                               content = ""
+                               found = true
+                       else if content.chars[keyword.length] <= ' ' then
+                               content = content.substring_from(keyword.length).l_trim
+                               found = true
+                       end
+                       if "" == content then
+                               text.shift_part
+                       else if found then
+                               text.set_part(0, content, "")
+                       end
+               end
+               return found
+       end
+
+       # Extract the specified suffix in the specified text.
+       #
+       # If the suffix is at the end of the specified text, return `true`
+       # and remove the suffix. Else, return false.
+       #
+       # Used to extract stuff like `...` that Doxygen puts in the type.
+       #
+       #     class DummySource
+       #       super JavaSource
+       #     #
+       #       fun test(text: LinkedText, s: String): Bool do
+       #               return extract_suffix(text, s)
+       #       end
+       #     end
+       #     #
+       #     var text = new RawType(new ProjectGraph(""))
+       #     var dummy = new DummySource
+       #     var res: Bool
+       #     #
+       #     text.add_part("Object...+++", "")
+       #     res = dummy.test(text, "...")
+       #     assert not res
+       #     res = dummy.test(text, "+++")
+       #     assert res
+       #     assert "Object..." == text["text"].as(JsonArray).first
+       #     res = dummy.test(text, "...")
+       #     assert res
+       #     assert "Object" == text["text"].as(JsonArray).first
+       protected fun extract_suffix(text: LinkedText, suffix: String): Bool do
+               var text_array = text["text"]
+               if text_array == null then return false
+               assert text_array isa JsonArray
+               if text_array.is_empty then return false
+
+               var content = text_array.last.as(String).r_trim
+               var link = text.links.first
+               var found = false
+
+               if link == null and content.has_suffix(suffix) then
+                       content = content.substring(0, content.length - suffix.length).r_trim
+                       if "" == content then
+                               text.pop_part
+                       else
+                               text.set_part(0, content, "")
+                       end
+                       return true
+               else
+                       return false
+               end
+       end
+end
+
+# The default importation logics.
+#
+# Do nothing special.
+class DefaultSource
+       super SourceLanguage
+end
+
+# Importation logics for Java.
+class JavaSource
+       super SourceLanguage
+
+       redef fun apply_member_type(member, type_text) do
+               # For abstract members, Doxygen put `abstract` at the beginning of the type.
+               # We assume that Doxygen do not put annotations in the type (it seems to
+               # be the case).
+               member.is_abstract = extract_keyword(type_text, "abstract")
+               # TODO final
+               # TODO void
+               # TODO Avoid using `RawType` when possible. Only use `RawType` as a fallback.
+               super
+       end
+
+       redef fun apply_parameter_type(parmeter, type_text) do
+               # We assume that Doxygen do not put annotations in the type (it seems to
+               # be the case).
+               # TODO final
+               # TODO Avoid using `RawType` when possible. Only use `RawType` as a fallback.
+               parmeter.is_vararg = extract_suffix(type_text, "...")
+               super
+       end
+end
diff --git a/contrib/neo_doxygen/src/doxml/listener.nit b/contrib/neo_doxygen/src/doxml/listener.nit
new file mode 100644 (file)
index 0000000..26de0d2
--- /dev/null
@@ -0,0 +1,281 @@
+# 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.
+
+# Basic SAX listeners.
+module doxml::listener
+
+import saxophonit
+import model
+import language_specific
+
+# Common abstractions for SAX listeners reading XML documents generated by Doxygen.
+abstract class DoxmlListener
+       super ContentHandler
+
+       # The locator setted by calling `document_locator=`.
+       protected var locator: nullable SAXLocator = null
+
+       # The project graph.
+       fun graph: ProjectGraph is abstract
+
+       # The language-specific strategies to use.
+       fun source_language: SourceLanguage is abstract
+
+       redef fun document_locator=(locator: SAXLocator) do
+               self.locator = locator
+       end
+
+       protected fun dox_uri: String do return ""
+
+       redef fun start_element(uri: String, local_name: String, qname: String,
+                       atts: Attributes) do
+               super
+               if uri != dox_uri then return # None of our business.
+               start_dox_element(local_name, atts)
+       end
+
+       # Process the start of an element in the Doxygen’s namespace.
+       #
+       # See `ContentHandler.start_element` for the description of the parameters.
+       protected fun start_dox_element(local_name: String, atts: Attributes) do end
+
+       redef fun end_element(uri: String, local_name: String, qname: String) do
+               super
+               if uri != dox_uri then return # None of our business.
+               end_dox_element(local_name)
+       end
+
+       # Process the end of an element in the Doxygen’s namespace.
+       #
+       # See `ContentHandler.start_element` for the description of the parameters.
+       protected fun end_dox_element(local_name: String) do end
+
+       protected fun get_bool(atts: Attributes, local_name: String): Bool do
+               return get_optional(atts, local_name, "no") == "yes"
+       end
+
+       # Get the value of an optional attribute.
+       #
+       # Parameters:
+       #
+       # * `atts`: attribute list.
+       # * `local_name`: local name of the attribute.
+       # * `default`: value to return when the specified attribute is not found.
+       protected fun get_optional(atts: Attributes, local_name: String,
+                       default: String): String do
+               return atts.value_ns(dox_uri, local_name) or else default
+       end
+
+       # Get the value of an required attribute.
+       #
+       # Parameters:
+       #
+       # * `atts`: attribute list.
+       # * `local_name`: local name of the attribute.
+       protected fun get_required(atts: Attributes, local_name: String): String do
+               var value = atts.value_ns(dox_uri, local_name)
+               if value == null then
+                       throw_error("The `{local_name}` attribute is required.")
+                       return ""
+               else
+                       return value
+               end
+       end
+
+       redef fun end_document do
+               locator = null
+       end
+
+       # Throw an error with the specified message by prepending the current location.
+       protected fun throw_error(message: String) do
+               var e: SAXParseException
+
+               if locator != null then
+                       e = new SAXParseException.with_locator(message, locator.as(not null))
+               else
+                       e = new SAXParseException(message)
+               end
+               e.throw
+       end
+end
+
+# A `DoxmlListener` that read only a part of a document.
+#
+# Temporary redirect events to itself until it ends processing its part.
+abstract class StackableListener
+       super DoxmlListener
+
+       # The associated reader.
+       var reader: XMLReader
+
+       # The parent listener.
+       var parent: DoxmlListener
+
+       # Namespace’s IRI of the element at the root of the part to process.
+       private var root_uri: String = ""
+
+       # Local name of the element at the root of the part to process.
+       private var root_local_name: String = ""
+
+       # The number of open element of the same type than the root of the part to process.
+       private var depth = 0
+
+       # The project graph.
+       private var p_graph: ProjectGraph is noinit
+
+       # The language-specific strategies to use.
+       private var p_source: SourceLanguage is noinit
+
+
+       init do
+               super
+               p_graph = parent.graph
+               p_source = parent.source_language
+       end
+
+       redef fun graph do return p_graph
+       redef fun source_language do return p_source
+
+       # Temporary redirect events to itself until the end of the specified element.
+       fun listen_until(uri: String, local_name: String) do
+               root_uri = uri
+               root_local_name = local_name
+               depth = 1
+               reader.content_handler = self
+               locator = parent.locator
+       end
+
+       redef fun start_element(uri: String, local_name: String, qname: String,
+                       atts: Attributes) do
+               super
+               if uri == root_uri and local_name == root_local_name then
+                       depth += 1
+               end
+       end
+
+       redef fun end_element(uri: String, local_name: String, qname: String) do
+               super
+               if uri == root_uri and local_name == root_local_name then
+                       depth -= 1
+                       if depth <= 0 then
+                               end_listening
+                               parent.end_element(uri, local_name, qname)
+                       end
+               end
+       end
+
+       # Reset the reader’s listener to the parent.
+       fun end_listening do
+               reader.content_handler = parent
+               locator = null
+       end
+
+       redef fun end_document do
+               end_listening
+       end
+end
+
+# A SAX listener that skips any event except the end of the part to process.
+#
+# Used to skip an entire element.
+class NoopListener
+       super StackableListener
+end
+
+# Concatenates any text node found.
+class TextListener
+       super StackableListener
+
+       protected var buffer: Buffer = new FlatBuffer
+       private var sp: Bool = false
+
+       redef fun listen_until(uri: String, local_name: String) do
+               buffer.clear
+               sp = false
+               super
+       end
+
+       redef fun characters(str: String) do
+               if sp then
+                       if buffer.length > 0 then buffer.append(" ")
+                       sp = false
+               end
+               buffer.append(str)
+       end
+
+       redef fun ignorable_whitespace(str: String) do
+               sp = true
+       end
+
+       # Flush the buffer.
+       protected fun flush_buffer: String do
+               var s = buffer.to_s
+
+               buffer.clear
+               sp = false
+               return s
+       end
+
+       redef fun to_s do return buffer.to_s
+end
+
+# Processes a content of type `linkedTextType`.
+abstract class LinkedTextListener[T: LinkedText]
+       super TextListener
+
+       # The read text.
+       var linked_text: T is noinit
+
+       private var refid = ""
+
+       # Create a new instance of `T`.
+       protected fun create_linked_text: T is abstract
+
+       redef fun listen_until(uri: String, local_name: String) do
+               linked_text = create_linked_text
+               refid = ""
+               super
+       end
+
+       redef fun start_dox_element(local_name: String, atts: Attributes) do
+               super
+               push_part
+               if "ref" == local_name then refid = get_required(atts, "refid")
+       end
+
+       redef fun end_dox_element(local_name: String) do
+               super
+               push_part
+               if "ref" == local_name then refid = ""
+       end
+
+       private fun push_part do
+               var s = flush_buffer
+
+               if not s.is_empty then
+                       linked_text.add_part(s, refid)
+               end
+       end
+
+       redef fun to_s do return linked_text.to_s
+end
+
+# Processes the content of a `<type>` element.
+class TypeListener
+       super LinkedTextListener[RawType]
+
+       private var raw_type: RawType is noinit
+
+       redef fun create_linked_text do return new RawType(graph)
+end
diff --git a/contrib/neo_doxygen/src/doxml/memberdef.nit b/contrib/neo_doxygen/src/doxml/memberdef.nit
new file mode 100644 (file)
index 0000000..5249404
--- /dev/null
@@ -0,0 +1,70 @@
+# 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.
+
+# `memberdef` element reading.
+module doxml::memberdef
+
+import entitydef
+
+# Processes the content of a `<memberdef>` element.
+class MemberDefListener
+       super EntityDefListener
+
+       # The current member.
+       var member: Member is writable, noinit
+
+       private var type_listener: TypeListener is noinit
+       private var param_listener: MemberParamListener is noinit
+
+       init do
+               super
+               type_listener = new TypeListener(reader, self)
+               param_listener = new MemberParamListener(reader, self)
+       end
+
+       redef fun entity do return member
+
+       redef fun start_dox_element(local_name: String, atts: Attributes) do
+               if "name" == local_name then
+                       text.listen_until(dox_uri, local_name)
+               else if "reimplements" == local_name then
+                       member.reimplement(get_required(atts, "refid"))
+               else if "type" == local_name then
+                       type_listener.listen_until(dox_uri, local_name)
+               else if "param" == local_name then
+                       param_listener.listen_until(dox_uri, local_name)
+               else
+                       super
+               end
+       end
+
+       redef fun end_dox_element(local_name: String) do
+               if "name" == local_name then
+                       member.name = text.to_s
+               else if "type" == local_name then
+                       source_language.apply_member_type(member, type_listener.linked_text)
+               else if "param" == local_name then
+                       member.add_parameter(param_listener.parameter)
+               else
+                       super
+               end
+       end
+end
+
+# Processes the content of a `<param>` element in a `<memberdef>` element.
+class MemberParamListener
+       super ParamListener[MemberParameter]
+
+       redef fun create_parameter do return new MemberParameter(graph)
+end
diff --git a/contrib/neo_doxygen/src/model/class_compound.nit b/contrib/neo_doxygen/src/model/class_compound.nit
new file mode 100644 (file)
index 0000000..7663c0a
--- /dev/null
@@ -0,0 +1,230 @@
+# 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.
+
+# Nodes for classes.
+module model::class_compound
+
+import graph
+import member
+import type_entity
+
+# A class.
+class ClassCompound
+       super Compound
+
+       # The corresponding type.
+       #
+       # In the case of a generic class, defines bounds for type parameters.
+       var class_type: ClassType is noinit
+
+       # The definition.
+       var class_def: ClassDef is noinit
+
+       init do
+               super
+               class_type = new ClassType(graph)
+               class_type.class_compound = self
+               class_def = new ClassDef(graph, self)
+               self.labels.add("MClass")
+               kind = "class"
+               visibility = "public"
+       end
+
+       # Return the number of type parameters.
+       fun arity: Int do return class_type.arity
+
+       redef fun name=(name: String) do
+               super
+               class_type.name = name
+               class_def.name = name
+       end
+
+       redef fun full_name=(full_name: String) do
+               super
+               class_type.full_name = full_name
+               class_def.full_name = full_name
+       end
+
+       redef fun parent_name=(parent_name: String) do
+               super
+               class_type.parent_name = parent_name
+               class_def.parent_name = parent_name
+       end
+
+       redef fun location=(location: nullable Location) do
+               super
+               class_def.location = location
+       end
+
+       redef fun set_mdoc do
+               super
+               class_def["mdoc"] = doc
+       end
+
+       redef fun declare_super(id: String, name: String, prot: String, virt: String) do
+               class_def.declare_super(id, name, prot, virt)
+       end
+
+       redef fun declare_member(member: Member) do
+               class_def.declare_member(member)
+       end
+
+       # Append the specified type parameter.
+       fun add_type_parameter(parameter: TypeParameter) do
+               class_type.arguments.add(parameter)
+       end
+
+       redef fun put_in_graph do
+               super
+               class_type.put_in_graph
+               class_def.put_in_graph
+       end
+
+       redef fun put_edges do
+               super
+               graph.add_edge(self, "CLASSTYPE", class_type)
+               if arity > 0 then
+                       var names = new JsonArray
+
+                       for p in class_type.arguments do
+                               names.add(p.name)
+                       end
+                       self["parameter_names"] = names
+               end
+       end
+end
+
+# The `MClassDef` node of a class.
+class ClassDef
+       super CodeBlock
+
+       var class_compound: ClassCompound
+       var supers: SimpleCollection[String] = new Array[String]
+       var members: SimpleCollection[Member] = new Array[Member]
+
+       init do
+               super
+               self.labels.add("MClassDef")
+               self["is_intro"] = true
+       end
+
+       fun declare_super(id: String, name: String, prot: String, virt: String) do
+               # TODO prot, virt, name
+               if "" != id then
+                       supers.add(id)
+               end
+       end
+
+       fun declare_member(member: Member) do
+               var full_name = self["full_name"]
+
+               if full_name != null then
+                       member.parent_name = full_name.to_s
+               end
+               members.add(member)
+       end
+
+       redef fun full_name=(full_name: String) do
+               super
+               for m in members do
+                       m.parent_name = full_name
+               end
+       end
+
+       redef fun parent_name=(parent_name: String) do
+               super
+               for m in members do
+                       m.parent_name = full_name
+               end
+       end
+
+       redef fun put_edges do
+               super
+               graph.add_edge(self, "BOUNDTYPE", class_compound.class_type)
+               graph.add_edge(self, "MCLASS", class_compound)
+               for s in supers do
+                       graph.add_edge(self, "INHERITS", graph.by_id[s].as(ClassCompound).class_type)
+               end
+               for m in members do
+                       if m.is_intro then
+                               var intro = m.introducer.as(not null)
+                               graph.add_edge(self, "INTRODUCES", intro)
+                               graph.add_edge(intro, "INTRO_CLASSDEF", self)
+                       end
+                       graph.add_edge(self, "DECLARES", m)
+               end
+       end
+end
+
+# A type defined by a class.
+class ClassType
+       super TypeEntity
+
+       # The associated class.
+       #
+       # You may use this attribute or `class_compound_id` to specify the class.
+       var class_compound: nullable ClassCompound = null is writable
+
+       # The `model_id` of the associated class.
+       #
+       # You may use this attribute or `class_compound` to specify the class.
+       var class_compound_id: String = "" is writable
+
+       # The type arguments or the type parameters.
+       var arguments = new Array[TypeEntity]
+
+       init do
+               super
+               self.labels.add("MClassType")
+       end
+
+       # Return the number of arguments.
+       fun arity: Int do return arguments.length
+
+       fun is_generic: Bool do return arity > 0
+
+       redef fun put_in_graph do
+               super
+               if is_generic then
+                       self.labels.add("MGenericType")
+               else
+                       var i = self.labels.index_of("MGenericType")
+                       if i >= 0 then self.labels.remove_at(i)
+               end
+       end
+
+       redef fun put_edges do
+               var cls = class_compound
+
+               if cls == null and class_compound_id != "" then
+                       cls = graph.by_id[class_compound_id].as(ClassCompound)
+               end
+               assert cls != null
+
+               super
+               graph.add_edge(self, "CLASS", cls)
+               assert cls.arity == self.arity
+               for i in [0..arguments.length[ do
+                       var a = arguments[i]
+                       if cls.class_type != self then
+                               a.name = cls.class_type.arguments[i].name
+                       end
+                       if a isa TypeParameter then
+                               a.rank = i
+                               graph.add_edge(a, "CLASS", cls)
+                       end
+                       graph.add_edge(self, "ARGUMENT", a)
+               end
+       end
+end
diff --git a/contrib/neo_doxygen/src/model/graph.nit b/contrib/neo_doxygen/src/model/graph.nit
new file mode 100644 (file)
index 0000000..e14d817
--- /dev/null
@@ -0,0 +1,293 @@
+# 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.
+
+# Graphs and basic entities.
+module model::graph
+
+import neo4j
+import location
+
+# A Neo4j graph.
+class NeoGraph
+       var all_nodes: SimpleCollection[NeoNode] = new Array[NeoNode]
+       var all_edges: SimpleCollection[NeoEdge] = new Array[NeoEdge]
+
+       # Add a relationship between two nodes.
+       #
+       # Parameters are the same than for the constructor of `NeoEdge`.
+       fun add_edge(from: NeoNode, rel_type: String, to: NeoNode) do
+               all_edges.add(new NeoEdge(from, rel_type, to))
+       end
+end
+
+# The project’s graph.
+class ProjectGraph
+       super NeoGraph
+
+       # The node reperesenting the project.
+       #
+       # Once the project’s graph is initialized, this node must not be edited.
+       var project: NeoNode = new NeoNode
+
+       # Entities by `model_id`.
+       var by_id: Map[String, Entity] = new HashMap[String, Entity]
+
+       # Initialize a new project graph using the specified project name.
+       #
+       # The specified name will label all nodes of the project’s graph.
+       init(name: String) do
+               project.labels.add(name)
+               project.labels.add("MEntity")
+               project.labels.add("MProject")
+               project["name"] = name
+               all_nodes.add(project)
+
+               var root = new RootNamespace(self)
+               root.put_in_graph
+               by_id[""] = root
+       end
+
+       # Request to all nodes in the graph to add their related edges.
+       fun put_edges do
+               add_edge(project, "ROOT", by_id[""])
+               for n in all_nodes do
+                       if n isa Entity then
+                               n.put_edges
+                       end
+               end
+       end
+end
+
+# A model’s entity.
+#
+# In practice, this is the base class of every node in a `ProjectGraph`.
+abstract class Entity
+       super NeoNode
+
+       # Graph that will embed the entity.
+       var graph: ProjectGraph
+
+       # ID of the entity in the model.
+       #
+       # Is empty for entities without an ID.
+       var model_id: String = "" is writable
+
+       # Associated documentation.
+       var doc: JsonArray = new JsonArray is writable
+
+       init do
+               self.labels.add(graph.project["name"].to_s)
+               self.labels.add("MEntity")
+       end
+
+       # The short (unqualified) name.
+       #
+       # May be also set by `full_name=`.
+       fun name=(name: String) do
+               self["name"] = name
+       end
+
+       # The short (unqualified) name.
+       fun name: String do
+               var name = self["name"]
+               assert name isa String
+               return name
+       end
+
+       # Include the documentation of `self` in the graph.
+       protected fun set_mdoc do
+               self["mdoc"] = doc
+       end
+
+       # The namespace separator of Nit/C++.
+       fun ns_separator: String do return "::"
+
+       # The name separator used when calling `full_name=`.
+       fun name_separator: String do return ns_separator
+
+       # The full (qualified) name.
+       #
+       # Also set `name` using `name_separator`.
+       fun full_name=(full_name: String) do
+               var m: nullable Match = full_name.search_last(name_separator)
+
+               self["full_name"] = full_name
+               if m == null then
+                       name = full_name
+               else
+                       name = full_name.substring_from(m.after)
+               end
+       end
+
+       # The full (qualified) name.
+       fun full_name: String do
+               var full_name = self["full_name"]
+               assert full_name isa String
+               return full_name
+       end
+
+       # Set the full name using the current name and the specified parent name.
+       fun parent_name=(parent_name: String) do
+               self["full_name"] = parent_name + name_separator + self["name"].as(not null).to_s
+       end
+
+       # Set the location of the entity in the source code.
+       fun location=(location: nullable Location) do
+               self["location"] = location
+       end
+
+       # Put the entity in the graph.
+       #
+       # Called by the loader when it has finished to read the entity.
+       fun put_in_graph do
+               if doc.length > 0 then
+                       set_mdoc
+               end
+               graph.all_nodes.add(self)
+               if model_id != "" then graph.by_id[model_id] = self
+       end
+
+       # Put the related edges in the graph.
+       #
+       # This method is called on each node by `ProjectGraph.put_edges`.
+       #
+       # Note: Even at this step, the entity may modify its own attributes and
+       # inner entities’ ones because some values are only known once the entity
+       # know its relationships with the rest of the graph.
+       fun put_edges do end
+end
+
+# An entity whose the location is mandatory.
+abstract class CodeBlock
+       super Entity
+
+       init do
+               self["location"] = new Location
+       end
+
+       redef fun location=(location: nullable Location) do
+               if location == null then
+                       super(new Location)
+               else
+                       super
+               end
+       end
+end
+
+# A compound.
+#
+# Usually corresponds to a `<compounddef>` element in of the XML output of
+# Doxygen.
+abstract class Compound
+       super Entity
+
+       # Set the declared visibility (the proctection) of the compound.
+       fun visibility=(visibility: String) do
+               self["visibility"] = visibility
+       end
+
+       # Set the specific kind of the compound.
+       fun kind=(kind: String) do
+               self["kind"] = kind
+       end
+
+       # Declare an inner namespace.
+       #
+       # Parameters:
+       #
+       # * `id`: `model_id` of the inner namespace. May be empty.
+       # * `name`: string identifying the inner namespace. May be empty.
+       fun declare_namespace(id: String, name: String) do end
+
+       # Declare an inner class.
+       #
+       # Parameters:
+       #
+       # * `id`: `model_id` of the inner class. May be empty.
+       # * `name`: string identifying the inner class. May be empty.
+       fun declare_class(id: String, name: String) do end
+
+       # Declare a base compound (usually, a base class).
+       #
+       # Parameters:
+       #
+       # * `id`: `model_id` of the base compound. May be empty.
+       # * `name`: string identifying the base compound. May be empty.
+       # * `prot`: visibility (proctection) of the relationship.
+       # * `virt`: level of virtuality of the relationship.
+       fun declare_super(id: String, name: String, prot: String, virt: String) do end
+end
+
+# An unrecognized compound.
+#
+# Used to simplify the handling of ignored entities.
+class UnknownCompound
+       super Compound
+
+       redef fun put_in_graph do end
+       redef fun put_edges do end
+end
+
+# A namespace.
+#
+# Corresponds to a group in Nit.
+class Namespace
+       super Compound
+
+       # Inner namespaces (IDs).
+       #
+       # Left empty for the root namespace.
+       var inner_namespaces: SimpleCollection[String] = new Array[String]
+
+       init do
+               super
+               self.labels.add("MGroup")
+       end
+
+       redef fun declare_namespace(id: String, name: String) do
+               inner_namespaces.add(id)
+       end
+
+       redef fun put_edges do
+               super
+               graph.add_edge(self, "PROJECT", graph.project)
+               if self["name"] == self["full_name"] and self["full_name"] != "" then
+                       # The root namespace does not know its children.
+                       var root = graph.by_id[""]
+                       graph.add_edge(self, "PARENT", root)
+                       graph.add_edge(root, "NESTS", self)
+               end
+               for ns in inner_namespaces do
+                       var node = graph.by_id[ns]
+                       graph.add_edge(node, "PARENT", self)
+                       graph.add_edge(self, "NESTS", node)
+               end
+       end
+end
+
+# The root namespace of a `ProjectGraph`.
+#
+# This the only entity in the graph whose `model_id` is really `""`.
+# Added automatically at the initialization of a `ProjectGraph`.
+class RootNamespace
+       super Namespace
+
+       init do
+               super
+               self["full_name"] = ""
+               self["name"] = graph.project["name"]
+       end
+
+       redef fun declare_namespace(id: String, name: String) do end
+end
diff --git a/contrib/neo_doxygen/src/model/linked_text.nit b/contrib/neo_doxygen/src/model/linked_text.nit
new file mode 100644 (file)
index 0000000..bfdf6a5
--- /dev/null
@@ -0,0 +1,164 @@
+# 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.
+
+# A text with links.
+module model::linked_text
+
+import graph
+
+# A text with links.
+abstract class LinkedText
+       super Entity
+
+       # All link in the text.
+       #
+       # Do not edit directly.
+       var links: Sequence[nullable Link] = new Array[nullable Link]
+
+       # Remove all the parts.
+       fun clear_parts do
+               self["text"] = null
+               links.clear
+       end
+
+       # Remove the first part.
+       fun shift_part do
+               var text = self["text"]
+               assert text isa JsonArray
+               text.shift
+               links.shift
+               if text.is_empty then
+                       self["text"] = null
+               end
+       end
+
+       # Remove the last part.
+       fun pop_part do
+               var text = self["text"]
+               assert text isa JsonArray
+               text.pop
+               links.pop
+               if text.is_empty then
+                       self["text"] = null
+               end
+       end
+
+       # Remove the part at the specified index.
+       fun remove_part_at(index: Int) do
+               var text = self["text"]
+               assert text isa JsonArray
+               text.remove_at(index)
+               links.remove_at(index)
+               if text.is_empty then
+                       self["text"] = null
+               end
+       end
+
+       # Change a part of text.
+       #
+       # Parameters:
+       #
+       # * `index` : the index of the part.
+       # * `content` : textual content.
+       # * `refid` : `model_id` of the linked entity or `""`.
+       fun set_part(index: Int, content: String, refid: String) do
+               var text = self["text"]
+               assert text isa JsonArray
+               text[index] = content
+               if not refid.is_empty then
+                       links[index] = create_link(links.length, refid)
+               else
+                       links[index] = null
+               end
+       end
+
+       # Append a part of text.
+       #
+       # Parameters:
+       #
+       # * `content` : textual content.
+       # * `refid` : `model_id` of the linked entity or `""`.
+       fun add_part(content: String, refid: String) do
+               var text = self["text"]
+
+               if text == null then
+                       text = new JsonArray
+                       self["text"] = text
+               end
+               assert text isa JsonArray
+               text.add(content)
+               if not refid.is_empty then
+                       links.add(create_link(links.length, refid))
+               else
+                       links.add(null)
+               end
+       end
+
+       # Create a link to the specified entity.
+       protected fun create_link(rank:Int, refid: String): Link is abstract
+
+       redef fun to_s do
+               var text = self["text"]
+
+               if text isa JsonArray then
+                       return text.join("")
+               else
+                       return "UNDEFINED"
+               end
+       end
+
+       redef fun put_in_graph do
+               super
+               for link in links do
+                       if link isa Link then
+                               link.put_in_graph
+                       end
+               end
+       end
+
+       redef fun put_edges do
+               super
+               for i in [0..links.length[ do
+                       var link = links[i]
+                       if link isa Link then
+                               link["rank"] = i
+                               graph.add_edge(self, "LINK", link)
+                       end
+               end
+       end
+end
+
+# A link.
+abstract class Link
+       super Entity
+
+       # * `refid` : `model_id` of the linked entity.
+       var refid: String
+
+       init do
+               super
+               self["rank"] = -1
+       end
+
+       redef fun put_edges do
+               graph.add_edge(self, "TARGET", graph.by_id[refid])
+       end
+
+       # Specify the rank (index) of the parameter in the signature.
+       #
+       # Called by `LinkedText.put_edges`.
+       private fun rank=(rank: Int) do
+               self["rank"] = rank
+       end
+end
diff --git a/contrib/neo_doxygen/src/model/location.nit b/contrib/neo_doxygen/src/model/location.nit
new file mode 100644 (file)
index 0000000..8652577
--- /dev/null
@@ -0,0 +1,37 @@
+# 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.
+
+# This module is used to model locations in source files.
+module location
+
+import neo4j
+
+# A location inside a source file.
+class Location
+       super Jsonable
+
+       var path: nullable String = null is writable
+       var line_start: Int = 1 is writable
+       var line_end: Int = 1 is writable
+       var column_start: Int = 1 is writable
+       var column_end: Int = 1 is writable
+
+       redef fun to_s: String do
+               var file_part = "/dev/null:"
+               if path != null and path.length > 0 then file_part = "{path.as(not null)}:"
+               return "{file_part}{line_start},{column_start}--{line_end},{column_end}"
+       end
+
+       redef fun to_json do return to_s.to_json
+end
diff --git a/contrib/neo_doxygen/src/model/member.nit b/contrib/neo_doxygen/src/model/member.nit
new file mode 100644 (file)
index 0000000..f29892e
--- /dev/null
@@ -0,0 +1,288 @@
+# 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.
+
+# Members.
+module model::member
+
+import graph
+import type_entity
+
+# A member.
+abstract class Member
+       super CodeBlock
+
+       # The node used to represent the `MProperty` node.
+       #
+       # Only defined if `self` is at the root of a reimplementation graph, and
+       # only once `put_in_graph` is called.
+       var introducer: nullable MemberIntroducer = null
+
+       # Members that this member redefines/reimplements.
+       var reimplemented: SimpleCollection[String] = new Array[String]
+
+       init do
+               super
+               self.labels.add("MPropDef")
+       end
+
+       # Set the static type.
+       fun static_type=(static_type: nullable TypeEntity) is abstract
+
+       # Get the static type.
+       fun static_type: nullable TypeEntity is abstract
+
+       # Append the specified parameter to the signature.
+       fun add_parameter(parameter: MemberParameter) do end
+
+       # Append a member that is reimplemeneted by `self`.
+       fun reimplement(parent: String) do
+               reimplemented.add(parent)
+       end
+
+       # Does this member introduce the property?
+       fun is_intro: Bool do
+               return reimplemented.length <= 0
+       end
+
+       redef fun put_in_graph do
+               super
+               self["is_intro"] = is_intro
+               if is_intro then
+                       var visibility = self["visibility"]
+                       var full_name = self["full_name"]
+                       var name = self["name"]
+
+                       introducer = create_introducer
+                       if full_name isa String then
+                               introducer.full_name = full_name
+                       else if name isa String then
+                               introducer.name = name
+                       end
+                       if visibility isa String then
+                               introducer.visibility = visibility
+                       end
+                       introducer.put_in_graph
+               end
+       end
+
+       redef fun put_edges do
+               super
+               var intro = resolve_introducer
+
+               assert intro != null
+               graph.add_edge(self, "DEFINES", intro)
+       end
+
+       # Set the visibility.
+       fun visibility=(visibility: String) do
+               self["visibility"] = visibility
+               if introducer != null then
+                       introducer.as(not null).visibility = visibility
+               end
+       end
+
+       redef fun name=(name: String) do
+               super
+               if introducer != null then
+                       introducer.as(not null).name = name
+               end
+       end
+
+       redef fun full_name=(full_name: String) do
+               super
+               if introducer != null then
+                       introducer.as(not null).full_name = full_name
+               end
+       end
+
+       redef fun parent_name=(parent_name: String) do
+               super
+               if introducer != null then
+                       introducer.as(not null).parent_name = parent_name
+               end
+       end
+
+       # Is the member abstract?
+       fun is_abstract=(is_abstract: Bool) do
+               self["is_abstract"] = is_abstract
+       end
+
+       # Create an instance of `MemberIntroducer` that will be linked to `self`.
+       protected fun create_introducer: MemberIntroducer is abstract
+
+       # Find the nearest reimplementation root.
+       #
+       #     var g = new ProjectGraph("foo")
+       #     var m1 = new Attribute(g)
+       #     var m2 = new Attribute(g)
+       #     var m3 = new Attribute(g)
+       #     #
+       #     m1.model_id = "1"
+       #     m1.put_in_graph
+       #     m2.reimplement("1")
+       #     m2.put_in_graph
+       #     assert m1.resolve_introducer == m1.introducer
+       #     assert m2.resolve_introducer == m1.introducer
+       #     #
+       #     m3.model_id = "3"
+       #     m3.reimplement("3")
+       #     m3.put_in_graph
+       #     assert m3.resolve_introducer == null
+       fun resolve_introducer: nullable MemberIntroducer do
+               if introducer == null then
+                       var member_queue = new List[String]
+                       var visited = new HashSet[Member]
+                       var member: Member
+
+                       member_queue.add_all(reimplemented)
+                       while not member_queue.is_empty do
+                               member = graph.by_id[member_queue.shift].as(Member)
+                               if visited.has(member) then
+                                       return null
+                               else if member.is_intro then
+                                       return member.introducer
+                               else
+                                       visited.add(member)
+                                       member_queue.add_all(member.reimplemented)
+                               end
+                       end
+                       return null
+               else
+                       return introducer
+               end
+       end
+end
+
+# An unrecognized member.
+#
+# Used to simplify the handling of ignored entities.
+class UnknownMember
+       super Member
+
+       redef fun put_in_graph do end
+       redef fun put_edges do end
+end
+
+class Method
+       super Member
+
+       # The method’s signature.
+       var signature: Signature is noinit, writable
+
+       init do
+               super
+               self.labels.add("MMethodDef")
+               self["is_intern"] = false # TODO
+               self["is_extern"] = false # TODO
+               signature = new Signature(graph)
+               is_abstract = false
+       end
+
+       # Set the return type.
+       redef fun static_type=(static_type: nullable TypeEntity) do
+               signature.return_type = static_type
+       end
+
+       # Get the return type.
+       redef fun static_type: nullable TypeEntity do return signature.return_type
+
+       redef fun add_parameter(parameter: MemberParameter) do
+               signature.parameters.add(parameter)
+       end
+
+       redef fun create_introducer: MemberIntroducer do
+               return new MethodIntroducer(graph)
+       end
+
+       redef fun put_in_graph do
+               super
+               signature.put_in_graph
+       end
+
+       redef fun put_edges do
+               super
+               graph.add_edge(self, "SIGNATURE", signature)
+       end
+end
+
+class Attribute
+       super Member
+
+       # The declared type.
+       redef var static_type: nullable TypeEntity = null is writable
+
+       init do
+               super
+               self.labels.add("MAttributeDef")
+       end
+
+       redef fun create_introducer: MemberIntroducer do
+               return new AttributeIntroducer(graph)
+       end
+
+       redef fun put_in_graph do
+               super
+               if static_type != null then
+                       static_type.as(not null).put_in_graph
+               end
+       end
+
+       redef fun put_edges do
+               super
+               if static_type != null then
+                       graph.add_edge(self, "TYPE", static_type.as(not null))
+               end
+       end
+end
+
+# The `MProperty` node of a root of a reimplementation graph.
+abstract class MemberIntroducer
+       super Entity
+
+       init do
+               super
+               self.labels.add("MProperty")
+               self["visibility"] = "public"
+       end
+
+       fun visibility=(visibility: String) do
+               self["visibility"] = visibility
+       end
+end
+
+# A `MProperty` node for a method.
+class MethodIntroducer
+       super MemberIntroducer
+
+       init do
+               super
+               self.labels.add("MMethod")
+               self["is_init"] = false # TODO
+       end
+end
+
+# A `MProperty` node for an attribute.
+class AttributeIntroducer
+       super MemberIntroducer
+
+       init do
+               super
+               self.labels.add("MAttribute")
+       end
+end
+
+redef class Compound
+       # Append the specified member.
+       fun declare_member(member: Member) do end
+end
diff --git a/contrib/neo_doxygen/src/model/model.nit b/contrib/neo_doxygen/src/model/model.nit
new file mode 100644 (file)
index 0000000..d3c2af9
--- /dev/null
@@ -0,0 +1,23 @@
+# 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.
+
+# The model used to populate the Neo4j graph.
+module model
+
+import location
+import linked_text
+import graph
+import class_compound
+import module_compound
+import member
diff --git a/contrib/neo_doxygen/src/model/module_compound.nit b/contrib/neo_doxygen/src/model/module_compound.nit
new file mode 100644 (file)
index 0000000..6629cc7
--- /dev/null
@@ -0,0 +1,139 @@
+# 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.
+
+# Nodes for modules and files.
+module model::module_compound
+
+import graph
+import class_compound
+
+# A source file.
+#
+# Creates one modules by inner namespace. The full name of the modules begin
+# with the namespace’s full name, and end with the unqualified name of the file,
+# without the extension.
+class FileCompound
+       super Compound
+       super CodeBlock
+
+       # Mapping between inner namespace’s names and corresponding modules.
+       private var inner_namespaces: Map[String, Module] = new HashMap[String, Module]
+
+       # The last component of the path, without the extension.
+       #
+       # Used as the unqualified name of the modules.
+       private var basename: String = ""
+
+       init do
+               super
+       end
+
+       redef fun name_separator: String do return "/"
+
+       redef fun location=(location: nullable Location) do
+               super
+               if location != null and location.path != null then
+                       full_name = location.path.as(not null)
+               end
+               for m in inner_namespaces.values do m.location = location
+       end
+
+       redef fun name=(name: String) do
+               # Example: `MyClass.java`
+               super
+               var match = name.search_last(".")
+
+               if match == null then
+                       basename = name
+               else
+                       basename = name.substring(0, match.from)
+               end
+               # Update the modules’ name.
+               for ns, m in inner_namespaces do
+                       m.full_name = "{ns}{ns_separator}{basename}"
+               end
+       end
+
+       redef fun declare_namespace(id: String, name: String) do
+               var m: Module
+
+               if inner_namespaces.keys.has(name) then
+                       m = inner_namespaces[name]
+                       if id != "" then m.parent = id
+               else
+                       m = new Module(graph)
+                       m.full_name = "{name}{ns_separator}{basename}"
+                       m.parent = id
+                       m.location = self["location"].as(nullable Location)
+                       inner_namespaces[name] = m
+               end
+       end
+
+       redef fun declare_class(id: String, name: String) do
+               var match = name.search_last(ns_separator)
+               var ns_name: String
+               var m: Module
+
+               if match == null then
+                       ns_name = ""
+               else
+                       ns_name = name.substring(0, match.from)
+               end
+               if inner_namespaces.keys.has(ns_name) then
+                       m = inner_namespaces[ns_name]
+               else
+                       declare_namespace("", ns_name)
+                       m = inner_namespaces[ns_name]
+               end
+               m.declare_class(id, name)
+       end
+
+       redef fun put_in_graph do
+               # Do not add `self` to the Neo4j graph...
+               # ... but add its modules...
+               for m in inner_namespaces.values do m.put_in_graph
+               # ... and add `self` to the index.
+               if model_id != "" then graph.by_id[model_id] = self
+       end
+end
+
+# A module.
+class Module
+       super Compound
+       super CodeBlock
+
+       # The `model_id` of the parent namespace.
+       var parent: String = "" is writable
+
+       # The classes defined in the module.
+       var inner_classes: SimpleCollection[String] = new Array[String]
+
+       init do
+               super
+               self.labels.add("MModule")
+       end
+
+       redef fun declare_class(id: String, name: String) do
+               inner_classes.add(id)
+       end
+
+       redef fun put_edges do
+               graph.add_edge(graph.by_id[parent], "DECLARES", self)
+               for c in inner_classes do
+                       var node = graph.by_id[c].as(ClassCompound)
+                       graph.add_edge(self, "INTRODUCES", node)
+                       graph.add_edge(self, "DEFINES", node.class_def)
+               end
+       end
+end
diff --git a/contrib/neo_doxygen/src/model/type_entity.nit b/contrib/neo_doxygen/src/model/type_entity.nit
new file mode 100644 (file)
index 0000000..d8ea8c0
--- /dev/null
@@ -0,0 +1,166 @@
+# 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.
+
+# Typing and parameters.
+module model::type_entity
+
+import graph
+import linked_text
+
+# Base class of all types and signatures.
+abstract class TypeEntity
+       super Entity
+
+       init do
+               super
+               self.labels.add("MType")
+       end
+end
+
+# A type parameter or a type argument.
+#
+# Note : The class relationship and the rank are set by `MClassType.put_edges`.
+class TypeParameter
+       super TypeEntity
+       super Parameter
+
+       init do
+               super
+               self.labels.add("MParameterType")
+       end
+end
+
+
+# A type described by a text.
+class RawType
+       super TypeEntity
+       super LinkedText
+
+       init do
+               super
+               self.labels.add("MRawType")
+       end
+
+       redef fun create_link(rank, refid) do return new TypeLink(graph, refid)
+end
+
+# A link in a `RawType`.
+class TypeLink
+       super Link
+
+       init do
+               super
+               self.labels.add("MTypePart")
+       end
+end
+
+
+# A signature of a method.
+class Signature
+       super TypeEntity
+
+       # The parameters.
+       var parameters = new Array[MemberParameter]
+
+       # The static type of the returned value.
+       var return_type: nullable TypeEntity = null is writable
+
+       init do
+               super
+               self.labels.add("MSignature")
+       end
+
+       redef fun put_in_graph do
+               super
+               if return_type isa TypeEntity then
+                       return_type.as(TypeEntity).put_in_graph
+               end
+               for p in parameters do
+                       p.put_in_graph
+               end
+       end
+
+       redef fun put_edges do
+               super
+               if parameters.length > 0 then
+                       var names = new JsonArray
+
+                       for i in [0..parameters.length[ do
+                               var p = parameters[i]
+                               p.rank = i
+                               names.add(p.name)
+                               graph.add_edge(self, "PARAMETER", p)
+                       end
+                       self["parameter_names"] = names
+               end
+               if return_type != null then
+                       graph.add_edge(self, "RETURNTYPE", return_type.as(not null))
+               end
+       end
+end
+
+# A parameter or an argument.
+abstract class Parameter
+       super Entity
+
+       # The static type of the parameter.
+       var static_type: nullable TypeEntity = null is writable
+
+       init do
+               super
+               self["is_vararg"] = false
+               self["rank"] = -1
+       end
+
+       # Is the parameter a “vararg”?
+       fun is_vararg=(is_vararg: Bool) do
+               self["is_vararg"] = is_vararg
+       end
+
+       # Is the parameter a “vararg”?
+       fun is_vararg: Bool do
+               var value = self["is_vararg"]
+               assert value isa Bool
+               return value
+       end
+
+       # Set the rank (index) of the parameter in the signature.
+       fun rank=(rank: Int) do
+               self["rank"] = rank
+       end
+
+       redef fun put_in_graph do
+               super
+               if static_type != null then
+                       static_type.as(not null).put_in_graph
+               end
+       end
+
+       redef fun put_edges do
+               super
+               graph.add_edge(self, "TYPE", static_type.as(not null))
+       end
+end
+
+# A parameter of a member.
+#
+# Note : The rank are set by `Signature.put_edges`.
+class MemberParameter
+       super Parameter
+
+       init do
+               super
+               self.labels.add("MParameter")
+       end
+end
diff --git a/contrib/neo_doxygen/src/neo_doxygen.nit b/contrib/neo_doxygen/src/neo_doxygen.nit
new file mode 100644 (file)
index 0000000..f26dfc2
--- /dev/null
@@ -0,0 +1,306 @@
+# 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.
+
+# Doxygen XML to Neo4j.
+#
+# Converts a Doxygen XML output into a model in Neo4j that is readable by the
+# `nx` tool.
+module neo_doxygen
+
+import model
+import doxml
+import console
+import opts
+
+# An importation task.
+class NeoDoxygenJob
+       var client: Neo4jClient
+       var model: ProjectGraph is noinit
+
+       # How many operation can be executed in one batch?
+       private var batch_max_size = 1000
+
+       private var save_cursor: String = (new TermSaveCursor).to_s
+
+       # Escape control sequence to reset the current line.
+       private var reset_line: String = "{new TermRestoreCursor}{new TermEraseDisplayDown}"
+
+       # Generate a graph from the specified project model.
+       #
+       # Parameters:
+       #
+       # * `name`: project name.
+       # * `dir`: Doxygen XML output directory path.
+       # * `source`: The language-specific logics to use.
+       fun load_project(name: String, dir: String, source: SourceLanguage) do
+               check_name name
+               model = new ProjectGraph(name)
+               # TODO Let the user select the language.
+               var reader = new CompoundFileReader(model, source)
+               # Queue for sub-directories.
+               var directories = new Array[String]
+
+               if dir.length > 1 and dir.chars.last == "/" then
+                       dir = dir.substring(0, dir.length - 1)
+               end
+               sys.stdout.write save_cursor
+               loop
+                       for f in dir.files do
+                               var path = dir/f
+                               if path.file_stat.is_dir then
+                                       directories.push(path)
+                               else if f.has_suffix(".xml") and f != "index.xml" then
+                                       print "{reset_line}Reading {path}..."
+                                       reader.read(path)
+                               end
+                       end
+                       if directories.length <= 0 then break
+                       dir = directories.pop
+               end
+               print "{reset_line}Reading... Done."
+       end
+
+       # Check the project’s name.
+       private fun check_name(name: String) do
+               assert name_valid: not name.chars.first.is_upper else
+                       sys.stderr.write("{sys.program_name}: The project’s name must not" +
+                                       " begin with an upper case letter. Got `{name}`.\n")
+               end
+               var query = new CypherQuery.from_string("match n where \{name\} in labels(n) return count(n)")
+               query.params["name"] = name
+               var data = client.cypher(query).as(JsonObject)["data"]
+               var result = data.as(JsonArray).first.as(JsonArray).first.as(Int)
+               assert name_unused: result == 0 else
+                       sys.stderr.write("{sys.program_name}: The label `{name}` is already" +
+                       " used in the specified graph.\n")
+               end
+       end
+
+       # Save the graph.
+       fun save do
+               print "Linking nodes...{save_cursor}"
+               model.put_edges
+               print "{reset_line} Done."
+               var nodes = model.all_nodes
+               print "Saving {nodes.length} nodes...{save_cursor}"
+               push_all(nodes)
+               var edges = model.all_edges
+               print "Saving {edges.length} edges...{save_cursor}"
+               push_all(edges)
+       end
+
+       # Save `neo_entities` in the database using batch mode.
+       private fun push_all(neo_entities: Collection[NeoEntity]) do
+               var batch = new NeoBatch(client)
+               var len = neo_entities.length
+               var sum = 0
+               var i = 1
+
+               for nentity in neo_entities do
+                       batch.save_entity(nentity)
+                       if i == batch_max_size then
+                               do_batch(batch)
+                               sum += batch_max_size
+                               print("{reset_line} {sum * 100 / len}%")
+                               batch = new NeoBatch(client)
+                               i = 1
+                       else
+                               i += 1
+                       end
+               end
+               do_batch(batch)
+               print("{reset_line} Done.")
+       end
+
+       # Execute `batch` and check for errors.
+       #
+       # Abort if `batch.execute` returns errors.
+       private fun do_batch(batch: NeoBatch) do
+               var errors = batch.execute
+               if not errors.is_empty then
+                       for e in errors do sys.stderr.write("{sys.program_name}: {e}\n")
+                       exit(1)
+               end
+       end
+end
+
+# The main class.
+class NeoDoxygenCommand
+
+       # Invalid arguments
+       var e_usage = 64
+
+       # Available options for `--src-lang`.
+       var sources = new HashMap[String, SourceLanguage]
+
+       # The synopsis.
+       var synopsis: String = "[--dest <url>] [--src-lang <lang>]\n" +
+                       "    [--] <project_name> <doxml_dir>"
+
+       # The synopsis for the help page.
+       var help_synopsis = "[-h|--help]"
+
+       # The default destination.
+       var default_dest = "http://localhost:7474"
+
+       # Processes the options.
+       var option_context = new OptionContext
+
+       # The `--src-lang` option.
+       var opt_src_lang: OptionEnum is noinit
+
+       # The `--dest` option.
+       var opt_dest: OptionString is noinit
+
+       # The `-h|--help` option.
+       var opt_help: OptionBool is noinit
+
+       init do
+               sources["any"] = new DefaultSource
+               sources["java"] = new JavaSource
+
+               var prefix = new OptionText("""
+{{{"NAME".bold}}}
+  {{{sys.program_name}}} — Doxygen XML to Neo4j.
+
+{{{"SYNOPSIS".bold}}}
+  {{{sys.program_name}}} {{{synopsis}}}
+  {{{sys.program_name}}} {{{help_synopsis}}}
+
+{{{"DESCRIPTION".bold}}}
+  Convert a Doxygen XML output into a model in Neo4j that is readable by the
+  `nx` tool.
+
+{{{"ARGUMENTS".bold}}}
+  <project_name>  The internal name of the project. Must the same name as the
+                  one specified to the `nx` tool. Must not begin by an upper
+                  case letter.
+
+  <doxml_dir>     The directory where the XML documents generated by Doxygen are
+                  located.
+
+{{{"OPTIONS".bold}}}
+""")
+               option_context.add_option(prefix)
+
+               opt_dest = new OptionString("The URL of the destination graph. `{default_dest}` by default.",
+                               "--dest")
+               opt_dest.default_value = default_dest
+               option_context.add_option(opt_dest)
+
+               var keys = new Array[String].from(sources.keys)
+               opt_src_lang = new OptionEnum(keys,
+                               "The programming language to assume when processing chunk in the declarations left as-is by Doxygen. Use `any` (the default) to disable any language-specific processing.",
+                               keys.index_of("any"), "--src-lang")
+               option_context.add_option(opt_src_lang)
+
+               opt_help = new OptionBool("Show the help (this page).",
+                               "-h", "--help")
+               option_context.add_option(opt_help)
+       end
+
+       # Start the application.
+       fun main: Int do
+               if args.is_empty then
+                       show_help
+                       return e_usage
+               end
+               option_context.parse(args)
+
+               var errors = option_context.get_errors
+               var rest = option_context.rest
+
+               if errors.is_empty and not opt_help.value and rest.length != 2 then
+                       errors.add "Unexpected number of additional arguments. Expecting 2; got {rest.length}."
+               end
+               if not errors.is_empty then
+                       for e in errors do print_error(e)
+                       show_usage
+                       return e_usage
+               end
+               if opt_help.value then
+                       show_help
+                       return 0
+               end
+
+               var source = sources[opt_src_lang.value_name]
+               var dest = opt_dest.value
+               var project_name = rest[0]
+               var dir = rest[1]
+               var neo = new NeoDoxygenJob(new Neo4jClient(dest or else default_dest))
+
+               neo.load_project(project_name, dir, source)
+               neo.save
+               return 0
+       end
+
+       # Show the help.
+       fun show_help do
+               option_context.usage
+       end
+
+       # Show the usage.
+       fun show_usage do
+               sys.stderr.write "Usage: {sys.program_name} {synopsis}\n"
+               sys.stderr.write "For details, run `{sys.program_name} --help`.\n"
+       end
+
+       # Print an error.
+       fun print_error(e: String) do
+               sys.stderr.write "{sys.program_name}: {e}\n"
+       end
+end
+
+# Add handling of multi-line descriptions.
+#
+# Note: The algorithm is naive and do not handle internationalisation and
+# escape sequences.
+redef class Option
+
+       redef fun pretty(off) do
+               var s = super
+
+               if s.length > 80 and off < 80 then
+                       var column_length = 80 - off
+                       var left = 0
+                       var right = 80
+                       var buf = new FlatBuffer
+                       var prefix = "\n{" " * off}"
+
+                       loop
+                               while right > left and s.chars[right] != ' ' do
+                                       right -= 1
+                               end
+                               if left == right then
+                                       buf.append s.substring(left, column_length)
+                                       right += column_length
+                               else
+                                       buf.append s.substring(left, right - left)
+                                       right += 1
+                               end
+                               buf.append prefix
+                               left = right
+                               right += column_length
+                               if right >= s.length then break
+                       end
+                       buf.append s.substring_from(left)
+                       buf.append "\n"
+                       return buf.to_s
+               else
+                       return "{s}\n"
+               end
+       end
+end
+
+exit((new NeoDoxygenCommand).main)
index 477c79f..d50b84a 100644 (file)
@@ -57,7 +57,7 @@ end
 
 redef class ToolContext
        # We don't need 'the compute_nit_dir'.
-       redef fun compute_nit_dir: nullable String
+       redef fun compute_nit_dir
        do
                return "/pnacl"
        end
diff --git a/doc/advanced_options b/doc/advanced_options
deleted file mode 100644 (file)
index 814f81b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-* GC **************************************************************************
-
-Nit programs can dynamically select a GC at runtime. Just set NIT_GC_OPTION envvar.
-Set it to "help" to list available GC at the begin of the program.
-
index 04dcb6e..e922a84 100644 (file)
@@ -19,9 +19,162 @@ module console
 abstract class TermEscape
        # The US-ASCII ESC character.
        protected fun esc: Char do return 27.ascii
+
+       # The Control Sequence Introducer (CSI).
+       protected fun csi: String do return "{esc}["
+end
+
+# Abstract class of the ANSI/VT100 escape sequences for directional moves.
+abstract class TermDirectionalMove
+       super TermEscape
+
+       # The length of the move.
+       var magnitude: Int = 1 is protected writable
+
+       redef fun to_s do
+               if magnitude == 1 then return "{csi}{code}"
+               return "{csi}{magnitude}{code}"
+       end
+
+       # The code of the command.
+       protected fun code: String is abstract
+end
+
+# ANSI/VT100 code to move the cursor up by `magnitude` rows (CUU).
+class TermMoveUp
+       super TermDirectionalMove
+
+       init do end
+
+       # Move by the specified number of cells.
+       init by(magnitude: Int) do self.magnitude = magnitude
+
+       redef fun code do return "A"
+end
+
+# ANSI/VT100 code to move the cursor down by `magnitude` rows (CUD).
+class TermMoveDown
+       super TermDirectionalMove
+
+       init do end
+
+       # Move by the specified number of cells.
+       init by(magnitude: Int) do self.magnitude = magnitude
+
+       redef fun code do return "B"
+end
+
+# ANSI/VT100 code to move the cursor foward by `magnitude` columns (CUF).
+class TermMoveFoward
+       super TermDirectionalMove
+
+       init do end
+
+       # Move by the specified number of cells.
+       init by(magnitude: Int) do self.magnitude = magnitude
+
+       redef fun code do return "C"
+end
+
+# ANSI/VT100 code to move the cursor backward by `magnitude` columns (CUB).
+class TermMoveBackward
+       super TermDirectionalMove
+
+       init do end
+
+       # Move by the specified number of cells.
+       init by(magnitude: Int) do self.magnitude = magnitude
+
+       redef fun code do return "D"
+end
+
+# ANSI/VT100 code to move the cursor at the specified position (CUP).
+class TermMove
+       super TermEscape
+
+       # Vertical position.
+       #
+       # 1 is the top.
+       var row: Int = 1
+
+       # Horizontal position.
+       #
+       # 1 is the left.
+       var column: Int = 1
+
+       init do end
+
+       # Move at the specified position.
+       #
+       # (1, 1) is the top-left corner of the display.
+       init at(row: Int, column: Int) do
+               self.row = row
+               self.column = column
+       end
+
+       redef fun to_s do
+               if row == 1 then
+                       if column == 1 then return "{csi}H"
+                       return "{csi};{column}H"
+               else
+                       if column == 1 then return "{csi}{row}H"
+                       return "{csi}{row};{column}H"
+               end
+       end
+end
+
+# ANSI/VT100 code to clear from the cursor to the end of the screen (ED 0).
+class TermEraseDisplayDown
+       super TermEscape
+       redef fun to_s do return "{csi}J"
+end
+
+# ANSI/VT100 code to clear from the cursor to the beginning of the screen (ED 1).
+class TermEraseDisplayUp
+       super TermEscape
+       redef fun to_s do return "{csi}1J"
+end
+
+# ANSI/VT100 code to clear the entire display and move the cursor to the top left of screen (ED 2).
+#
+# Note: Some terminals always move the cursor when the screen is cleared. So we
+# force this behaviour to ensure interoperability of the code.
+class TermClearDisplay
+       super TermEscape
+       redef fun to_s do return "{csi}2J{csi}H"
+end
+
+# ANSI/VT100 code to erase anything after the cursor in the line (EL 0).
+class TermEraseLineFoward
+       super TermEscape
+       redef fun to_s do return "{csi}K"
+end
+
+# ANSI/VT100 code to erase anything before the cursor in the line (EL 1).
+class TermEraseLineBackward
+       super TermEscape
+       redef fun to_s do return "{csi}1K"
+end
+
+# ANSI/VT100 code to clear everything in the current line (EL 2).
+class TermClearLine
+       super TermEscape
+       redef fun to_s do return "{csi}2K"
+end
+
+# ANSI/VT100 code to save the current cursor position (SCP).
+class TermSaveCursor
+       super TermEscape
+       redef fun to_s do return "{csi}s"
+end
+
+# ANSI/VT100 code to restore the current cursor position (RCP).
+class TermRestoreCursor
+       super TermEscape
+       redef fun to_s do return "{csi}u"
 end
 
-# ANSI/VT100 code to switch character attributes (SGR).
+# ANSI/VT100 code to change character look (SGR).
 #
 # By default, resets everything to the terminal’s defaults.
 #
@@ -46,7 +199,7 @@ class TermCharFormat
                attributes.add_all(format.attributes)
        end
 
-       redef fun to_s: String do return "{esc}[{attributes.join(";")}m"
+       redef fun to_s: String do return "{csi}{attributes.join(";")}m"
 
        # Apply the specified SGR and return `self`.
        private fun apply(sgr: String): TermCharFormat do
index 50117cf..bd441a1 100644 (file)
@@ -110,3 +110,13 @@ class HashMap3[K1: Object, K2: Object, K3: Object, V]
                level2[k2, k3] = v
        end
 end
+
+# A map with a default value.
+class DefaultMap[K: Object, V]
+       super HashMap[K, V]
+
+       # The default value.
+       var default: V
+
+       redef fun provide_default_value(key) do return default
+end
index f1a1da8..dc5c8ac 100644 (file)
@@ -50,6 +50,7 @@ abstract class JsonCurlRequest
                headers = new HeaderMap
                headers["Accept"] = "application/json; charset=UTF-8"
                headers["Transfer-Encoding"] = "chunked"
+               headers["X-Stream"] = "true"
                if auth != null then
                        headers["Authorization"] = "token {auth.to_s}"
                end
@@ -95,7 +96,7 @@ abstract class JsonCurlRequest
                end
 
                var err_hook = execute_hook
-           if err_hook != null then return err_hook
+               if err_hook != null then return err_hook
 
                var err_resp = perform
                if err_resp != null then return err_resp
index c088325..5e36142 100644 (file)
@@ -282,6 +282,14 @@ class OptionContext
                parse_intern(it)
        end
 
+       # Must all option be given before the first argument?
+       #
+       # When set to `false` (the default), options of the command line are
+       # all parsed until the end of the list of arguments or until "--" is met (in this case "--" is discarded).
+       #
+       # When set to `true` options are parsed until the first non-option is met.
+       var options_before_rest = false is writable
+
        # Parse the command line
        protected fun parse_intern(it: Iterator[String])
        do
@@ -319,6 +327,10 @@ class OptionContext
                                        else
                                                rest.add(it.item)
                                                it.next
+                                               if options_before_rest then
+                                                       rest.add_all(it.to_a)
+                                                       parseargs = false
+                                               end
                                        end
                                end
                        end
index fcf14c7..9b1fb8a 100644 (file)
@@ -327,6 +327,37 @@ redef class Text
        #     assert "I say hello to the world!".search_from("hello",7)       == null
        fun search_from(p: Pattern, from: Int): nullable Match do return p.search_in(self, from)
 
+       # Search the last occurence of the text `t`.
+       #
+       #     assert "bob".search_last("b").from == 2
+       #     assert "bob".search_last("bo").from == 0
+       #     assert "bob".search_last("ob").from == 1
+       #     assert "bobob".search_last("ob").from == 3
+       #     assert "bobbob".search_last("bb").from == 2
+       #     assert "bobbob".search_last("bob").from == 3
+       #     assert "bob".search_last("z") == null
+       #     assert "".search_last("b") == null
+       fun search_last(t: Text): nullable Match do
+               return search_last_up_to(t, length)
+       end
+
+       # Search the last occurence of the text `t` before `up_to`.
+       #
+       #     assert "bobbob".search_last_up_to("b", 3).from == 2
+       #     assert "bobbob".search_last_up_to("b", 6).from == 5
+       #     assert "bobbob".search_last_up_to("b", 0) == null
+       fun search_last_up_to(t: Text, up_to: Int): nullable Match do
+               var i = up_to - t.length
+
+               while i >= 0 do
+                       if substring(i, t.length) == t then
+                               return new Match(self.to_s, i, t.length)
+                       end
+                       i -= 1
+               end
+               return null
+       end
+
        # Search all occurrences of p into self.
        #
        #     var a = new Array[Int]
index 3eeeb03..c87e2fb 100644 (file)
@@ -18,4 +18,7 @@ OUT=$(patsubst %.md,man1/%.1,$(IN))
 all: $(OUT)
 
 man1/%.1: %.md
+       mkdir -p man1
        pandoc $< -t man -s -o $@
+
+clean:
index a167029..71df3ee 100644 (file)
@@ -13,9 +13,9 @@ man -l man1/nitg.1
 ~~~
 
 For global access, one can set the `MANPATH` environment variable to this `man` directory (not the `man1` subdirectory).
-Do not forget to append a trailing column (`:`) to keep existing manpages accessible.
+Do not forget to append `$MANPATH` to keep existing manpages accessible.
 
 ~~~
-export MANPATH=/path/to/nit/share/man:
+export MANPATH=/path/to/nit/share/man:$MANPATH
 man nitg
 ~~~
diff --git a/share/man/man1/nitc.1 b/share/man/man1/nitc.1
new file mode 120000 (symlink)
index 0000000..e213084
--- /dev/null
@@ -0,0 +1 @@
+nitg.1
\ No newline at end of file
index 1c45f1d..9ff9963 100644 (file)
 
 # NAME
 
-Compiles Nit programs.
+nitg --- compiles Nit programs.
+
 
 # SYNOPSYS
 
-nitg [*options*]...
+nitg [*options*] FILE...
+
+
+# DESCRIPTION
+
+nitg is the current official Nit compiler.
+It takes the main module of a Nit program as argument and produces an executable file.
+
+By default, the generated executables are produced in the current directory.
+(see `--dir` for details.)
+
+Internally, nitg rely on the presence of a C compiler. Usually gcc (but nitg was successfully tested with clang).
+A compilation directory is therefore created and (re-)used.
+By default, the compilation directory is named `.nit_compile`.
+(see `--compile-dir` for details.)
+
+Currently, because Nit is still in heavy development, the compilation directory is not cleaned after the compilation.
+
+By default, the compilation process tries to have a good trade-off between the compilation time and the performance of produced executables.
+To produce more optimized executables, the current best option is `--semi-global`.
+
+To improve the compilation time and simplify the compilation of multiple programs, more than one file can be given.
+Each one will be compiled into a distinct executable.
+
+To combine files into a single program, use the `-m` option.
+
+nitg can produces executables for various platforms when specific modules are used.
+Currently, android, pnacl and emscripten are supported.
+See the documentation of these specific modules for details.
+
 
 # OPTIONS
 
+## MESSAGES
+
 `-W`, `--warn`
-:   Show more warnings
+:   Show additional warnings (advices).
+
+    By default, only important warnings are displayed.
+    May be overridden by `-w`.
+
+    Important warnings are displayed by default. A warning is considered important when:
+
+     * There is a simple correction.
+     * There is no reason to let the code this way.
+     * There is always a real issue (no false positive).
+
+    Other warnings, called advices, are not displayed by default to avoid filling the terminal with
+    unwanted information.
+    A warning is considered an advice when:
+
+     * The correction could be complex. e.g. require a refactorisation or an API change.
+     * The correction cannot be done. e.g. Code that use a deprecated API for some compatibility reason.
+     * There is not a real issue (false positive). Note that this should be unlikely.
+     * Transitional: While a real important warning, it fires a lot in current code, so a transition is needed
+       in order to let people fix them before promoting the advice to an important warning.
 
 `-w`, `--warning`
-:   Show/hide a specific warning
+:   Show/hide a specific warning.
+
+    Each type of warning can be individually displayed or hidden.
+    The `-w` option takes the name of a warning (displayed at the end of the warning message, between parentheses) to activate it;
+    and "no-{name}" to disable it.
+    It has precedence over -q and -W.
+    Multiple `-w` can be given.
+
+    To show only `missing-doc` warnings in standard"
+
+        $ nitg -q -w missing-doc standard
+
+    To show all warnings and advices, except `missing-doc`:
+
+        $ nitg -W -w no-missing-doc standard
+
+    To show important warnings except `useless-type-test`, but not advice except `missing-doc`:
+
+        $ nitg -w missing-doc -w no-useless-type-test standard
 
 `-q`, `--quiet`
-:   Do not show warnings
+:   Do not show warnings.
+    May be overridden by `-w`
 
 `--stop-on-first-error`
-:   Stop on first error
+:   Just display the first encountered error then stop.
+
+    By default, nitg tries to detect and display more than one error before aborting the compilation.
 
 `--no-color`
-:   Do not use color to display errors and warnings
+:   Do not use color to display errors and warnings.
+
+    Also, do not echo the line.
+    This options is mainly used by scripts and tools that need parsable error messages.
+
+`-v`, `--verbose`
+:   Additional messages from the tool.
+    Multiple `-v` can be given to improve the verbosity.
+
+    With one `-v`, there is constant number of lines.
+    With two `-v`, the number of lines is proportional to the number of modules.
+    With three `-v`, the number of lines is proportional to the number of definition of properties.
 
 `--log`
-:   Generate various log files
+:   Generate various log files.
+    Currently unused.
 
 `--log-dir`
-:   Directory where to generate log files
+:   Directory where to generate log files.
+    Currently unused.
+
 
 `-h`, `-?`, `--help`
-:   Show Help (This screen)
+:   Show Help (the list of options).
 
 `--version`
-:   Show version and exit
+:   Show version and exit.
 
-`--set-dummy-tool`
-:   Set toolname and version to DUMMY. Useful for testing
 
-`-v`, `--verbose`
-:   Verbose
+## PATHS
 
-`--bash-completion`
-:   Generate bash_completion file for this program
+`-I`, `--path`
+:   Add an include path.
 
-`--stub-man`
-:   Generate a stub manpage in pandoc markdown format
+    This option is used to indicate an additional path of a directory containing Nit libraries.
 
-`--disable-phase`
-:   DEBUG: Disable a specific phase; use `list` to get the list.
+    The path added with `-I` are searched before those added by the environment variable `NIT_PATH`.
 
-`-I`, `--path`
-:   Set include path for loaders (may be used more than once)
+    May be used more than once.
 
-`--only-parse`
-:   Only proceed to parse step of loaders
+`-o`, `--output`
+:   Output executable name.
 
-`--only-metamodel`
-:   Stop after meta-model processing
+    Indicates the path and name of the produced executable.
 
-`--ignore-visibility`
-:   Do not check, and produce errors, on visibility issues.
+    Note: it is better to use `--dir` if only the directory is important.
+    This way, the platform extension will be correctly set.
 
-`-o`, `--output`
-:   Output file
+    `-o` is not available if multiple programs are compiled at once.
 
 `--dir`
-:   Output directory
+:   Output directory.
 
-`--no-cc`
-:   Do not invoke C compiler
+    Produce the executables in the given directory instead of the current directory.
 
-`--no-main`
-:   Do not generate main entry point
 
-`--make-flags`
-:   Additional options to make
+## COMPILATION
 
 `--compile-dir`
-:   Directory used to generate temporary files
+:   Directory used to generate temporary files.
 
-`--hardening`
-:   Generate contracts in the C code against bugs in the compiler
+    By default, it is named `.nit_compile`.
 
-`--no-shortcut-range`
-:   Always insantiate a range and its iterator on 'for' loops
+`--no-cc`
+:   Do not invoke the C compiler.
 
-`--no-check-covariance`
-:   Disable type tests of covariant parameters (dangerous)
+    Files in the compilation directory are generated but the C compiler is not invoked.
 
-`--no-check-attr-isset`
-:   Disable isset tests before each attribute access (dangerous)
+    This option is mainly used to produce C files distributable then compilable on system that do not have a Nit compiler (e.g. embedded system).
+    In this case, it is suggested to also use the options `--dir`, `--compile-dir` and `--semi-global`.
 
-`--no-check-assert`
-:   Disable the evaluation of explicit 'assert' and 'as' (dangerous)
+        $ nitg examples/hello_world.nit --no-cc --dir hello --compile-dir hello --semi-global
 
-`--no-check-autocast`
-:   Disable implicit casts on unsafe expression usage (dangerous)
+    Will produce a `hello` directory that contains the required C files to finish the compilation.
+    Only the C files required for the program are generated.
+    The final binary will be generated in the same directory.
 
-`--no-check-null`
-:   Disable tests of null receiver (dangerous)
+`-m`
+:   Additional module to mix-in.
 
-`--no-check-all`
-:   Disable all tests (dangerous)
+    Additional modules are imported and refine the main module of the program.
+    This has basically the same effect than implementing a specific module that imports the main module of the program then each one of the mix-in modules.
+    May be used more than once.
 
-`--typing-test-metrics`
-:   Enable static and dynamic count of all type tests
+    This is option is used to weave additional behaviors to existing programs.
+    Modules designated to bring features to programs by refining basic or specialized services, without any intervention of the main program, are good candidates to be used with the `-m` option.
+    E.g. `hash_debug`.
 
-`--invocation-metrics`
-:   Enable static and dynamic count of all method invocations
+    An other usage of the `-m` option is to compile program to a specific platform. E.g. `emscripten`  or `android`.
 
-`--isset-checks-metrics`
-:   Enable static and dynamic count of isset checks before attributes access
+    A last usage is to develop programs as product lines with a main basic module (vanilla) and specific distinct features as flavor modules, then to combine them at compile-time.
 
-`--stacktrace`
-:   Control the generation of stack traces
+        $ nitg prog_vanilla.nit -m feature_chocolate.nit -m feature_cherry.nit
 
-`--no-gcc-directive`
-:   Disable a advanced gcc directives for optimization
+`-D`, `--define`
+:   Define a specific property.
+
+    The `-D` option allows to refine a top-level method at compile-time.
+    This has basically the same effect than implementing a specific module that imports the main module of the program and refines the designated methods.
+
+    The designated method must be top-level function with no parameters that returns a Bool, an Int or a String.
+
+    The argument of the `-D` option is "{name}={value}".
+    For Bool, the argument can also be just "{name}", in this case, the value is considered to be `true`.
+
+        $ nitg foo.nit -D prefix=/opt/foo -D port=8080 -D with_ssl
 
 `--release`
-:   Compile in release mode and finalize application
+:   Compile in release mode and finalize application.
 
-`--global`
-:   Use global compilation
+    Currently, this only affect the android platform.
+
+## COMPILATION MODES
+
+`nitg` includes distinct compilation modes.
 
 `--separate`
-:   Use separate compilation
+:   Use separate compilation (default mode).
 
-`--no-inline-intern`
-:   Do not inline call to intern methods
+    In separate compilation, modules are compiled independently of their programs.
+    This makes the recompilation of programs faster since only the modified files need to be recompiled.
 
-`--no-union-attribute`
-:   Put primitive attibutes in a box instead of an union
+`--global`
+:   Use global compilation.
 
-`--no-shortcut-equal`
-:   Always call == in a polymorphic way
+    The produced executables may become huge and the compilation time is prohibitive.
+    But sometime, they are faster.
+
+    In practice, `--semi-global` produces nearly as fast but smaller executables.
+
+`--erasure`
+:   Erase generic types.
+
+    Like `--separate` but use an erasure dynamic typing policy for generics and virtual types.
+    Usually you do not need this, even if you understand the previous sentence.
+
+
+## SEMI-GLOBAL OPTIMIZATIONS
+
+In `--separate` and in `--erasure` modes, some optimization can be gained by relaxing the constraint about
+the independence on programs.
+
+Therefore, with these options, the produced executables may be faster and smaller but the recompilation time
+will increase.
+
+`--semi-global`
+:   Enable all semi-global optimizations.
+
+`--rta`
+:   Activate RTA (Rapid Type Analysis).
+
+    This option only make sense in `--erasure` to enable some semi-global optimizations.
+
+    RTA is implicitly enabled in `--separate` and `--global`.
 
 `--inline-coloring-numbers`
-:   Inline colors and ids (semi-global)
+:   Inline colors and ids (semi-global).
 
 `--inline-some-methods`
-:   Allow the separate compiler to inline some methods (semi-global)
+:   Allow the separate compiler to inline some methods (semi-global).
+    Need `--rta`.
 
 `--direct-call-monomorph`
-:   Allow the separate compiler to direct call monomorph sites (semi-global)
+:   Allow the separate compiler to direct call monomorphic sites (semi-global).
+    Need `--rta`.
 
 `--skip-dead-methods`
 :   Do not compile dead methods (semi-global)
+    Need `--rta`.
 
-`--semi-global`
-:   Enable all semi-global optimizations
+
+## DANGEROUS OPTIMIZATIONS
+
+The following optimizations disable runtime checks.
+It means that correct (non-buggy) programs may be slightly faster.
+It also means that incorrect (buggy) programs may have unspecified behaviors
+(e.g. formatting your hard drive or killing your cat).
+
+In fact, these options are mainly used to bench the compilation results.
+
+`--no-check-all`
+:   Disable all tests (dangerous).
+
+`--no-check-covariance`
+:   Disable type tests of covariant parameters (dangerous).
+
+`--no-check-attr-isset`
+:   Disable isset tests before each attribute access (dangerous).
+
+`--no-check-assert`
+:   Disable the evaluation of explicit `assert` and `as` (dangerous).
+
+`--no-check-autocast`
+:   Disable implicit casts on unsafe expression usage (dangerous).
+
+`--no-check-null`
+:   Disable tests of null receiver (dangerous).
+
+`--no-check-erasure-cast`
+:   Disable implicit casts on unsafe return with erasure-typing policy (dangerous).
+
+
+## UNOPTIMIZATIONS
+
+These options are used to debug or to bench the compilation results.
+Usually you do not need them since they make the generated code slower.
+
+`--hardening`
+:   Generate contracts in the C code against bugs in the compiler.
+
+`--no-shortcut-range`
+:   Always instantiate a range and its iterator on 'for' loops.
+
+`--no-union-attribute`
+:   Put primitive attributes in a box instead of an union.
+
+`--no-shortcut-equal`
+:   Always call == in a polymorphic way.
+
+`--no-inline-intern`
+:   Do not inline call to intern methods.
 
 `--colo-dead-methods`
-:   Force colorization of dead methods
+:   Force colorization of dead methods.
+
+`--no-gcc-directive`
+:   Disable advanced gcc directives for optimization.
+
+
+## INTERNAL OPTIONS
+
+These options can be used to control the fine behavior of the tool.
+They are useless for a normal user.
+
+`--disable-phase`
+:   Disable a specific phase; use `list` to get the list.
+
+`--only-parse`
+:   Only proceed to parse files.
+
+`--only-metamodel`
+:   Stop after meta-model processing.
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`--no-main`
+:   Do not generate main entry point.
+
+`--stacktrace`
+:   Control the generation of stack traces.
+
+`--max-c-lines`
+:   Maximum number of lines in generated C files. Use 0 for unlimited.
+
+`--group-c-files`
+:   Group all generated code in the same series of files.
+
+`--make-flags`
+:   Additional options to the `make` command.
+
+        $ nitg foo.nit --make-flags 'CC=clang' --make-flags 'CFLAGS="-O0 -g"'
+
+`--typing-test-metrics`
+:   Enable static and dynamic count of all type tests.
+
+`--invocation-metrics`
+:   Enable static and dynamic count of all method invocations.
+
+`--isset-checks-metrics`
+:   Enable static and dynamic count of isset checks before attributes access.
 
 `--tables-metrics`
-:   Enable static size measuring of tables used for vft, typing and resolution
+:   Enable static size measuring of tables used for vft, typing and resolution.
 
-`--erasure`
-:   Erase generic types
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing.
 
-`--no-check-erasure-cast`
-:   Disable implicit casts on unsafe return with erasure-typing policy (dangerous)
+`--bash-completion`
+:   Generate bash_completion file for this program.
 
-`--rta`
-:   Activate RTA (implicit with --global and --separate)
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format.
 
-`-m`
-:   Additionals module to min-in
+
+# ENVIRONMENT VARIABLES
+
+`NIT_DIR`
+:   Nit install directory.
+
+    When the `NIT_DIR` environment variable is set then it specifies the path of the Nit install directory.
+
+    This directory is used to locate binaries, shared files and the common libraries.
+
+    When unset, the directory is guessed according to some heuristic.
+
+`NIT_PATH`
+:   Additional include paths.
+
+    The `NIT_PATH` environment variable contains paths of directories containing Nit libraries.
+    Each path is separated with a column (`:`).
+
+    The `-I` option also add additional paths.
+
+`NIT_GC_OPTION`
+:   Runtime control of the garbage collector.
+
+    The behavior of the GC of the executables produced by nitg can be tuned with this environment variable.
+
+    The environment variable is used when programs are executed, not when they are compiled.
+    Thus, you do not need to recompile programs in order to tweak their GC options.
+
+    Available values are:
+
+    * boehm: use the Boehm-Demers-Weiser's conservative garbage collector (default).
+    * malloc: disable the GC and just use `malloc` without doing any `free`.
+    * large: disable the GC and just allocate a large memory area to use for all instantiation.
+    * help: show the list of available options.
 
 # SEE ALSO
 
index e265a2e..b56f947 100644 (file)
@@ -115,13 +115,17 @@ class ExternFile
        fun compiles_to_o_file: Bool do return false
 
        fun add_to_jar: Bool do return false
+
+       # Additional libraries needed for the compilation
+       # Will be used with pkg-config
+       var pkgconfigs = new Array[String]
 end
 
 # An extern C file to compile
 class ExternCFile
        super ExternFile
 
-       # Additionnal specific CC compiler -c flags
+       # Additional specific CC compiler -c flags
        var cflags: String
 
        redef fun hash do return filename.hash
@@ -136,7 +140,11 @@ class ExternCFile
        redef fun makefile_rule_content do
                var ff = filename.basename("")
                var o = makefile_rule_name
-               return "$(CC) $(CFLAGS) {self.cflags} -c -o {o} {ff}"
+               var pkg = ""
+               if not pkgconfigs.is_empty then
+                       pkg = "`pkg-config --cflags {pkgconfigs.join(" ")}`"
+               end
+               return "$(CC) $(CFLAGS) {self.cflags} {pkg} -c -o {o} {ff}"
        end
 
        redef fun compiles_to_o_file do return true
index 62c0987..733a56e 100644 (file)
@@ -34,8 +34,6 @@ redef class ToolContext
        var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
        # --no-main
        var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
-       # --cc-paths
-       var opt_cc_path = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
        # --make-flags
        var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
        # --max-c-lines
@@ -81,6 +79,8 @@ redef class ToolContext
                self.option_context.add_option(self.opt_no_gcc_directive)
                self.option_context.add_option(self.opt_release)
                self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
+
+               opt_no_main.hidden = true
        end
 
        redef fun process_options(args)
@@ -151,43 +151,9 @@ end
 
 class MakefileToolchain
        super Toolchain
-       # The list of directories to search for included C headers (-I for C compilers)
-       # The list is initially set with :
-       #   * the toolcontext --cc-path option
-       #   * the NIT_CC_PATH environment variable
-       #   * `toolcontext.nit_dir`
-       # Path can be added (or removed) by the client
-       var cc_paths = new Array[String]
-
-       # The clib directory of Nit
-       # Used to found some common runtime
-       var clib: String is noinit
-
-       protected fun gather_cc_paths
-       do
-               # Look for the the Nit clib path
-               var path_env = toolcontext.nit_dir
-               if path_env != null then
-                       var libname = "{path_env}/clib"
-                       if not libname.file_exists then
-                               toolcontext.fatal_error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.")
-                       end
-                       clib = libname
-               end
-
-               # Add user defined cc_paths
-               cc_paths.append(toolcontext.opt_cc_path.value)
-
-               path_env = "NIT_CC_PATH".environ
-               if not path_env.is_empty then
-                       cc_paths.append(path_env.split_with(':'))
-               end
-       end
 
        redef fun write_and_make(compiler)
        do
-               gather_cc_paths
-
                var compile_dir = compile_dir
 
                # Generate the .h and .c files
@@ -230,7 +196,9 @@ class MakefileToolchain
 
                # Add gc_choser.h to aditionnal bodies
                var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
+               if cc_opt_with_libgc != "" then gc_chooser.pkgconfigs.add "bdw-gc"
                compiler.extern_bodies.add(gc_chooser)
+               var clib = toolcontext.nit_dir / "clib"
                compiler.files_to_copy.add "{clib}/gc_chooser.c"
                compiler.files_to_copy.add "{clib}/gc_chooser.h"
 
@@ -352,18 +320,13 @@ class MakefileToolchain
                var makepath = "{compile_dir}/{makename}"
                var makefile = new OFStream.open(makepath)
 
-               var cc_includes = ""
-               for p in cc_paths do
-                       cc_includes += " -I \"" + p + "\""
-               end
-
                var linker_options = new HashSet[String]
                for m in mainmodule.in_importation.greaters do
                        var libs = m.collect_linker_libs
                        if libs != null then linker_options.add_all(libs)
                end
 
-               makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS  ?= -lm -lgc {linker_options.join(" ")}\n\n")
+               makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch\nCINCL =\nLDFLAGS ?= \nLDLIBS  ?= -lm {linker_options.join(" ")}\n\n")
 
                var ost = toolcontext.opt_stacktrace.value
                if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
@@ -399,6 +362,28 @@ class MakefileToolchain
 
                var java_files = new Array[ExternFile]
 
+               var pkgconfigs = new Array[String]
+               for f in compiler.extern_bodies do
+                       pkgconfigs.add_all f.pkgconfigs
+               end
+               # Protect pkg-config
+               if not pkgconfigs.is_empty then
+                       makefile.write """
+# does pkg-config exists?
+ifneq ($(shell which pkg-config >/dev/null; echo $$?), 0)
+$(error "Command `pkg-config` not found. Please install it")
+endif
+"""
+                       for p in pkgconfigs do
+                               makefile.write """
+# Check for library {{{p}}}
+ifneq ($(shell pkg-config --exists '{{{p}}}'; echo $$?), 0)
+$(error "pkg-config: package {{{p}}} is not found.")
+endif
+"""
+                       end
+               end
+
                # Compile each required extern body into a specific .o
                for f in compiler.extern_bodies do
                        var o = f.makefile_rule_name
@@ -424,7 +409,11 @@ class MakefileToolchain
                end
 
                # Link edition
-               makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS)\n\n")
+               var pkg = ""
+               if not pkgconfigs.is_empty then
+                       pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
+               end
+               makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
                # Clean
                makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
                makefile.close
index 6fec966..55468fd 100644 (file)
@@ -205,8 +205,8 @@ $(call import-module,android/native_app_glue)
                # libpng is not available on Android NDK
                # FIXME make obtionnal when we have alternatives to mnit
                var nit_dir = toolcontext.nit_dir
-               var share_dir =  "{nit_dir or else ""}/share/"
-               if nit_dir == null or not share_dir.file_exists then
+               var share_dir =  nit_dir/"share/"
+               if not share_dir.file_exists then
                        print "Android project error: Nit share directory not found, please use the environment variable NIT_DIR"
                        exit 1
                end
index e9c740a..6a36b2b 100644 (file)
@@ -53,7 +53,9 @@ extern void nitni_global_ref_decr(void*);
                nitni_ccu.write_as_nitni(self, v.compiler.modelbuilder.compile_dir)
 
                for file in nitni_ccu.files do
-                       v.compiler.extern_bodies.add(new ExternCFile(file, c_compiler_options))
+                       var f = new ExternCFile(file, c_compiler_options)
+                       f.pkgconfigs.add_all pkgconfigs
+                       v.compiler.extern_bodies.add(f)
                end
 
                # reset FFI things so the next compilation job, if any, starts with a clean context
index 41386d2..4141f75 100644 (file)
@@ -19,6 +19,11 @@ import model_utils
 import markdown
 import doc_templates
 import ordered_tree
+import model_ext
+
+
+################################################################################
+# Additions to Nit entities.
 
 redef class MDoc
        # Comment synopsys HTML escaped
@@ -714,3 +719,22 @@ redef class ConcernsTree
                li.append lst
        end
 end
+
+
+################################################################################
+# Additions to `model_ext`.
+
+redef class MRawType
+       redef fun tpl_signature do
+               var tpl = new Template
+
+               for part in parts do
+                       if part.target != null then
+                               tpl.add part.target.as(not null).tpl_link
+                       else
+                               tpl.add part.text.html_escape
+                       end
+               end
+               return tpl
+       end
+end
index 844e79e..78d42fb 100644 (file)
@@ -109,11 +109,7 @@ class Nitdoc
                var sharedir = ctx.opt_sharedir.value
                if sharedir == null then
                        var dir = ctx.nit_dir
-                       if dir == null then
-                               print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
-                               abort
-                       end
-                       sharedir = "{dir}/share/nitdoc"
+                       sharedir = dir/"share/nitdoc"
                        if not sharedir.file_exists then
                                print "Error: Cannot locate nitdoc share files. Uses --sharedir or envvar NIT_DIR"
                                abort
diff --git a/src/doc/model_ext.nit b/src/doc/model_ext.nit
new file mode 100644 (file)
index 0000000..fe74325
--- /dev/null
@@ -0,0 +1,98 @@
+# 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.
+
+# Extensions to the Nit model for foreign languages.
+module doc::model_ext
+
+intrude import model
+intrude import model::model_base
+
+# A type described by a text annoted with links.
+#
+# For use with Nitdoc only.
+class MRawType
+       super MType
+
+       redef var model: Model
+
+       # The parts that contitute the description of the type.
+       var parts: Sequence[MTypePart] = new Array[MTypePart]
+
+       redef fun as_nullable do
+               not_available
+               return self
+       end
+       redef fun need_anchor do
+               not_available
+               return false
+       end
+       redef fun resolve_for(mtype, anchor, mmodule, cleanup_virtual) do
+               not_available
+               return self
+       end
+       redef fun can_resolve_for(mtype, anchor, mmodule) do
+               not_available
+               return true
+       end
+       redef fun collect_mclassdefs(mmodule) do
+               not_available
+               return new HashSet[MClassDef]
+       end
+       redef fun collect_mclasses(mmodule) do
+               not_available
+               return new HashSet[MClass]
+       end
+       redef fun collect_mtypes(mmodule) do
+               not_available
+               return new HashSet[MClassType]
+       end
+
+       redef fun to_s do return parts.to_s
+
+       private fun not_available do
+               assert false else
+                       sys.stderr.write "A `MRawType` is for documentation-purpose only so the requested operation is not available.\n"
+               end
+       end
+end
+
+# A part of a `RawType`.
+class MTypePart
+       super MEntity
+
+       redef var model: Model
+
+       # The textual content.
+       var text: String
+
+       # If the part links to another entity, the targeted entity.
+       var target: nullable MEntity
+
+       redef fun name do return text
+       redef fun to_s do return text
+
+       # Return a version of `self` that links to the specified entity.
+       fun link_to(target: nullable MEntity): MTypePart do
+               return new MTypePart(model, text, target)
+       end
+end
+
+# The “package” visiblity.
+#
+# Any visibility roughly equivalent to the default visibility of Java, that is
+# private for a collection of modules.
+fun package_visibility: MVisibility do return once new MVisibility("package", 2)
+
+# A class kind with no equivalent semantic in Nit.
+fun raw_kind(s: String): MClassKind do return new MClassKind(s, false)
index 48d4343..6fba61a 100644 (file)
@@ -74,6 +74,10 @@ end
 redef class MModule
        var c_compiler_options = "" is writable
        var c_linker_options = "" is writable
+
+       # Additional libraries needed for the compilation
+       # Will be used with pkg-config
+       var pkgconfigs = new Array[String]
 end
 
 class ForeignCType
index a418616..a2dda26 100644 (file)
@@ -60,7 +60,11 @@ redef class MModule
                end
 
                ffi_ccu.write_as_impl(self, compdir)
-               for filename in ffi_ccu.files do ffi_files.add(new ExternCFile(filename, c_compiler_options))
+               for filename in ffi_ccu.files do
+                       var f = new ExternCFile(filename, c_compiler_options)
+                       f.pkgconfigs.add_all pkgconfigs
+                       ffi_files.add(f)
+               end
        end
 
        # Avoid the compile a ffi propdef more than once
index 335d8d1..dca4fab 100644 (file)
@@ -87,15 +87,7 @@ class PkgconfigPhase
                                return
                        end
 
-                       # compiler
-                       var proc = new IProcess("pkg-config", "--cflags", pkg)
-                       var compiler_opts = proc.read_all
-                       mmodule.c_compiler_options = "{mmodule.c_compiler_options} {compiler_opts.replace("\n", " ")}"
-
-                       # linker
-                       proc = new IProcess("pkg-config", "--libs", pkg)
-                       var linker_opts = proc.read_all
-                       mmodule.c_linker_options = "{mmodule.c_linker_options} {linker_opts.replace("\n", " ")}"
+                       mmodule.pkgconfigs.add pkg
                end
 
        end
index 76a6305..83f5e0e 100644 (file)
@@ -139,10 +139,8 @@ class ModelBuilder
                end
 
                var nit_dir = toolcontext.nit_dir
-               if nit_dir != null then
-                       var libname = "{nit_dir}/lib"
-                       if libname.file_exists then paths.add(libname)
-               end
+               var libname = "{nit_dir}/lib"
+               if libname.file_exists then paths.add(libname)
        end
 
        # Load a bunch of modules.
index dd9d06d..cb56d38 100644 (file)
@@ -587,9 +587,9 @@ redef class AMethPropdef
                self.mpropdef = mpropdef
                modelbuilder.mpropdef2npropdef[mpropdef] = self
                if mpropdef.is_intro then
-                       modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 3)
+                       modelbuilder.toolcontext.info("{mpropdef} introduces new method {mprop.full_name}", 4)
                else
-                       modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 3)
+                       modelbuilder.toolcontext.info("{mpropdef} redefines method {mprop.full_name}", 4)
                end
        end
 
index df2792c..a566f31 100644 (file)
 # `MType`
 #
 # * labels: `MType`, `model_name` and `MEntity`. Must also have `MClassType`,
-# `MNullableType`, `MVirtualType` or `MSignature`, depending on the class of
-# the represented entity.
+# `MNullableType`, `MVirtualType`, `MRawType` or `MSignature`, depending on the
+# class of the represented entity.
 #
 # Additional label and relationships for `MClassType`:
 #
 #
 # * `(:MNullableType)-[:TYPE]->(:MType)`: base type of the nullable type.
 #
+# Additional attribute and relationship for `MRawType`:
+#
+# * `text`: JSON array of the parts’ text.
+# * `(:MRawType)-[:LINK]->(:MTypePart)`: the parts that link to another entity.
+#
 # Additional attribute and relationships for `MSignature`:
 #
 # * `parameter_names`: JSON array representing the list of the parameter names.
 #
 # MParameters are also ranked by their position in the corresponding signature.
 # Rank 0 for the first parameter, 1 for the next one and etc.
+#
+# `MTypePart`
+#
+# * labels: `MTypePart`, `model_name` and `MEntity`.
+# * `rank`: position in the `MRawType`.
+# * `(:MTypePart)-[:TARGET]->(:MEntity)`: the target of the link.
 module neo
 
-import model
+import doc::model_ext
 import neo4j
 import toolcontext
 
@@ -213,27 +224,27 @@ class NeoModel
 
        # Fill `model` using base pointed by `client`.
        fun load(model: Model): Model do
-               toolcontext.info("Locate all mentities...", 1)
-               var nodes = client.nodes_with_label(model_name)
-
-               toolcontext.info("Preload nodes...", 1)
-               pull_all_nodes(nodes)
-               toolcontext.info("Preload edges...", 1)
-               pull_all_edges(nodes)
+               var nodes: Array[NeoNode]
 
-               toolcontext.info("Build model...", 1)
+               toolcontext.info("Loading project node...", 1)
                nodes = client.nodes_with_labels([model_name, "MProject"])
                for node in nodes do to_mproject(model, node)
+               toolcontext.info("Loading groups...", 1)
                nodes = client.nodes_with_labels([model_name, "MGroup"])
                for node in nodes do to_mgroup(model, node)
+               toolcontext.info("Loading modules...", 1)
                nodes = client.nodes_with_labels([model_name, "MModule"])
                for node in nodes do to_mmodule(model, node)
+               toolcontext.info("Loading classes...", 1)
                nodes = client.nodes_with_labels([model_name, "MClass"])
                for node in nodes do to_mclass(model, node)
+               toolcontext.info("Loading class definitions...", 1)
                nodes = client.nodes_with_labels([model_name, "MClassDef"])
                for node in nodes do to_mclassdef(model, node)
+               toolcontext.info("Loading properties...", 1)
                nodes = client.nodes_with_labels([model_name, "MProperty"])
                for node in nodes do to_mproperty(model, node)
+               toolcontext.info("Loading property definitions...", 1)
                nodes = client.nodes_with_labels([model_name, "MPropDef"])
                for node in nodes do to_mpropdef(model, node)
                return model
@@ -270,52 +281,6 @@ class NeoModel
                do_batch(batch)
        end
 
-       # Load content for all `nodes` from base.
-       #
-       # Content corresponds to properties and labels that are loaded in batch mode.
-       private fun pull_all_nodes(nodes: Collection[NeoNode]) do
-               var batch = new NeoBatch(client)
-               var len = nodes.length
-               var sum = 0
-               var i = 1
-               for node in nodes do
-                       batch.load_node(node)
-                       if i == batch_max_size then
-                               do_batch(batch)
-                               sum += batch_max_size
-                               toolcontext.info(" {sum * 100 / len}% done", 1)
-                               batch = new NeoBatch(client)
-                               i = 1
-                       else
-                               i += 1
-                       end
-               end
-               do_batch(batch)
-       end
-
-       # Load all edges from base linked to `nodes`.
-       #
-       # Edges are loaded in batch mode.
-       private fun pull_all_edges(nodes: Collection[NeoNode]) do
-               var batch = new NeoBatch(client)
-               var len = nodes.length
-               var sum = 0
-               var i = 1
-               for node in nodes do
-                       batch.load_node_edges(node)
-                       if i == batch_max_size then
-                               do_batch(batch)
-                               sum += batch_max_size
-                               toolcontext.info(" {sum * 100 / len}% done", 1)
-                               batch = new NeoBatch(client)
-                               i = 1
-                       else
-                               i += 1
-                       end
-               end
-               do_batch(batch)
-       end
-
        # How many operation can be executed in one batch?
        private var batch_max_size = 1000
 
@@ -349,7 +314,9 @@ class NeoModel
        end
 
        # Mentities associated to nodes.
-       private var mentities = new HashMap[NeoNode, MEntity]
+       #
+       # The key is the node’s id.
+       private var mentities = new HashMap[Int, MEntity]
 
        # Nodes associated with MEntities.
        private var nodes = new HashMap[MEntity, NeoNode]
@@ -370,6 +337,20 @@ class NeoModel
                abort
        end
 
+       # Get the `MEntity` associated with `node`.
+       fun to_mentity(model: Model, node: NeoNode): MEntity do
+               if node.labels.has("MProject") then return to_mproject(model, node)
+               if node.labels.has("MGroup") then return to_mgroup(model, node)
+               if node.labels.has("MModule") then return to_mmodule(model, node)
+               if node.labels.has("MClass") then return to_mclass(model, node)
+               if node.labels.has("MClassDef") then return to_mclassdef(model, node)
+               if node.labels.has("MProperty") then return to_mproperty(model, node)
+               if node.labels.has("MPropDef") then return to_mpropdef(model, node)
+               if node.labels.has("MType") then return to_mtype(model, node)
+               if node.labels.has("MParameter") then return to_mparameter(model, node)
+               abort
+       end
+
        # Make a new `NeoNode` based on `mentity`.
        private fun make_node(mentity: MEntity): NeoNode do
                var node = new NeoNode
@@ -396,10 +377,12 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MProject")`
        private fun to_mproject(model: Model, node: NeoNode): MProject do
-               if mentities.has_key(node) then return mentities[node].as(MProject)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MProject then return m
+
                assert node.labels.has("MProject")
                var mproject = new MProject(node["name"].to_s, model)
-               mentities[node] = mproject
+               mentities[node.id.as(Int)] = mproject
                set_doc(node, mproject)
                mproject.root = to_mgroup(model, node.out_nodes("ROOT").first)
                return mproject
@@ -428,7 +411,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MGroup")`
        private fun to_mgroup(model: Model, node: NeoNode): MGroup do
-               if mentities.has_key(node) then return mentities[node].as(MGroup)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MGroup then return m
+
                assert node.labels.has("MGroup")
                var mproject = to_mproject(model, node.out_nodes("PROJECT").first)
                var parent: nullable MGroup = null
@@ -437,7 +422,7 @@ class NeoModel
                        parent = to_mgroup(model, out.first)
                end
                var mgroup = new MGroup(node["name"].to_s, mproject, parent)
-               mentities[node] = mgroup
+               mentities[node.id.as(Int)] = mgroup
                set_doc(node, mgroup)
                return mgroup
        end
@@ -464,7 +449,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MModule")`
        private fun to_mmodule(model: Model, node: NeoNode): MModule do
-               if mentities.has_key(node) then return mentities[node].as(MModule)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MModule then return m
+
                assert node.labels.has("MModule")
                var ins = node.in_nodes("DECLARES")
                var mgroup: nullable MGroup = null
@@ -474,7 +461,7 @@ class NeoModel
                var name = node["name"].to_s
                var location = to_location(node["location"].to_s)
                var mmodule = new MModule(model, mgroup, name, location)
-               mentities[node] = mmodule
+               mentities[node.id.as(Int)] = mmodule
                set_doc(node, mmodule)
                var imported_mmodules = new Array[MModule]
                for smod in node.out_nodes("IMPORTS") do
@@ -504,7 +491,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MClass")`
        private fun to_mclass(model: Model, node: NeoNode): MClass do
-               if mentities.has_key(node) then return mentities[node].as(MClass)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MClass then return m
+
                assert node.labels.has("MClass")
                var mmodule = to_mmodule(model, node.in_nodes("INTRODUCES").first)
                var name = node["name"].to_s
@@ -517,7 +506,7 @@ class NeoModel
                        end
                end
                var mclass = new MClass(mmodule, name, parameter_names, kind, visibility)
-               mentities[node] = mclass
+               mentities[node.id.as(Int)] = mclass
                set_doc(node, mclass)
                return mclass
        end
@@ -545,13 +534,15 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MClassDef")`
        private fun to_mclassdef(model: Model, node: NeoNode): MClassDef do
-               if mentities.has_key(node) then return mentities[node].as(MClassDef)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MClassDef then return m
+
                assert node.labels.has("MClassDef")
                var mmodule = to_mmodule(model, node.in_nodes("DEFINES").first)
                var mtype = to_mtype(model, node.out_nodes("BOUNDTYPE").first).as(MClassType)
                var location = to_location(node["location"].to_s)
                var mclassdef = new MClassDef(mmodule, mtype, location)
-               mentities[node] = mclassdef
+               mentities[node.id.as(Int)] = mclassdef
                set_doc(node, mclassdef)
                var supertypes = new Array[MClassType]
                for sup in node.out_nodes("INHERITS") do
@@ -584,7 +575,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MProperty")`
        private fun to_mproperty(model: Model, node: NeoNode): MProperty do
-               if mentities.has_key(node) then return mentities[node].as(MProperty)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MProperty then return m
+
                assert node.labels.has("MProperty")
                var intro_mclassdef = to_mclassdef(model, node.out_nodes("INTRO_CLASSDEF").first)
                var name = node["name"].to_s
@@ -602,7 +595,7 @@ class NeoModel
                        print "not yet implemented to_mproperty for {node.labels.join(",")}"
                        abort
                end
-               mentities[node] = mprop
+               mentities[node.id.as(Int)] = mprop
                set_doc(node, mprop)
                return mprop
        end
@@ -642,7 +635,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MPropDef")`
        private fun to_mpropdef(model: Model, node: NeoNode): MPropDef do
-               if mentities.has_key(node) then return mentities[node].as(MPropDef)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MPropDef then return m
+
                assert node.labels.has("MPropDef")
                var mclassdef = to_mclassdef(model, node.in_nodes("DECLARES").first)
                var mproperty = to_mproperty(model, node.out_nodes("DEFINES").first)
@@ -653,16 +648,16 @@ class NeoModel
                        mpropdef.is_abstract = node["is_abstract"].as(Bool)
                        mpropdef.is_intern = node["is_intern"].as(Bool)
                        mpropdef.is_extern = node["is_extern"].as(Bool)
-                       mentities[node] = mpropdef
+                       mentities[node.id.as(Int)] = mpropdef
                        mpropdef.msignature = to_mtype(model, node.out_nodes("SIGNATURE").first).as(MSignature)
                else if node.labels.has("MAttributeDef") then
                        mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
-                       mentities[node] = mpropdef
+                       mentities[node.id.as(Int)] = mpropdef
                        var static_mtype = node.out_nodes("TYPE")
                        if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first)
                else if node.labels.has("MVirtualTypeDef") then
                        mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
-                       mentities[node] = mpropdef
+                       mentities[node.id.as(Int)] = mpropdef
                        var bound = node.out_nodes("BOUND")
                        if not bound.is_empty then mpropdef.bound = to_mtype(model, bound.first)
                end
@@ -703,15 +698,41 @@ class NeoModel
                        var rank = 0
                        for mparameter in mtype.mparameters do
                                names.add mparameter.name
-                               var pnode = to_node(mparameter)
+                               var pnode = mparameter_node(mparameter)
                                pnode["rank"] = rank
                                node.out_edges.add(new NeoEdge(node, "PARAMETER", pnode))
+                               rank += 1
                        end
                        if not names.is_empty then node["parameter_names"] = names
                        var return_mtype = mtype.return_mtype
                        if return_mtype != null then
                                node.out_edges.add(new NeoEdge(node, "RETURNTYPE", to_node(return_mtype)))
                        end
+               else if mtype isa MRawType then
+                       node.labels.add "MRawType"
+                       var text = new JsonArray
+                       var rank = 0
+                       for part in mtype.parts do
+                               text.add part.text
+                               if part.target != null then
+                                       var pnode = mtypepart_node(part)
+                                       pnode["rank"] = rank
+                                       node.out_edges.add(new NeoEdge(node, "LINK", pnode))
+                               end
+                               rank += 1
+                       end
+                       if not text.is_empty then node["text"] = text
+               end
+               return node
+       end
+
+       # Build a `NeoNode` representing `mtypepart`.
+       private fun mtypepart_node(mtypepart: MTypePart): NeoNode do
+               var node = make_node(mtypepart)
+               node.labels.add "MTypePart"
+               if mtypepart.target != null then
+                       var target_node = to_node(mtypepart.target.as(not null))
+                       node.out_edges.add(new NeoEdge(node, "TARGET", target_node))
                end
                return node
        end
@@ -720,7 +741,9 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MType")`
        private fun to_mtype(model: Model, node: NeoNode): MType do
-               if mentities.has_key(node) then return mentities[node].as(MType)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MType then return m
+
                assert node.labels.has("MType")
                if node.labels.has("MClassType") then
                        var mclass = to_mclass(model, node.out_nodes("CLASS").first)
@@ -729,24 +752,24 @@ class NeoModel
                                args.add to_mtype(model, narg)
                        end
                        var mtype = mclass.get_mtype(args)
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                else if node.labels.has("MParameterType") then
                        var mclass = to_mclass(model, node.out_nodes("CLASS").first)
                        var rank = node["rank"].to_s.to_i
                        var mtype = mclass.mparameters[rank]
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return  mtype
                else if node.labels.has("MNullableType") then
                        var intype = to_mtype(model, node.out_nodes("TYPE").first)
                        var mtype = intype.as_nullable
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                else if node.labels.has("MVirtualType") then
                        var mproperty = to_mproperty(model, node.out_nodes("PROPERTY").first)
                        assert mproperty isa MVirtualTypeProp
                        var mtype = mproperty.mvirtualtype
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                else if node.labels.has("MSignature") then
                        # Get all param nodes
@@ -770,7 +793,26 @@ class NeoModel
                                return_mtype = to_mtype(model, ret_nodes.first)
                        end
                        var mtype = new MSignature(mparameters, return_mtype)
-                       mentities[node] = mtype
+                       mentities[node.id.as(Int)] = mtype
+                       return mtype
+               else if node.labels.has("MRawType") then
+                       var mtype = new MRawType(model)
+                       var parts = node["text"]
+                       if parts isa JsonArray then
+                               for p in parts do
+                                       mtype.parts.add(new MTypePart(model, p.to_s, null))
+                               end
+                               for pnode in node.out_nodes("LINK") do
+                                       assert pnode.labels.has("MTypePart")
+                                       if not pnode.out_nodes("TARGET").is_empty then
+                                               var rank = pnode["rank"]
+                                               var target = to_mentity(model, pnode.out_nodes("TARGET").first)
+                                               assert rank isa Int
+                                               mtype.parts[rank] = mtype.parts[rank].link_to(target)
+                                       end
+                               end
+                       end
+                       mentities[node.id.as(Int)] = mtype
                        return mtype
                end
                print "not yet implemented to_mtype for {node.labels.join(",")}"
@@ -791,13 +833,15 @@ class NeoModel
        #
        # REQUIRE `node.labels.has("MParameter")`
        private fun to_mparameter(model: Model, node: NeoNode): MParameter do
-               if mentities.has_key(node) then return mentities[node].as(MParameter)
+               var m = mentities.get_or_null(node.id.as(Int))
+               if m isa MParameter then return m
+
                assert node.labels.has("MParameter")
                var name = node["name"].to_s
                var mtype = to_mtype(model, node.out_nodes("TYPE").first)
                var is_vararg = node["is_vararg"].as(Bool)
                var mparameter = new MParameter(name, mtype, is_vararg)
-               mentities[node] = mparameter
+               mentities[node.id.as(Int)] = mparameter
                return mparameter
        end
 
@@ -827,6 +871,8 @@ class NeoModel
                        return protected_visibility
                else if vis == private_visibility.to_s then
                        return private_visibility
+               else if vis == package_visibility.to_s then
+                       return package_visibility
                else
                        return none_visibility
                end
@@ -844,8 +890,9 @@ class NeoModel
                        return enum_kind
                else if kind == extern_kind.to_s then
                        return extern_kind
+               else
+                       return raw_kind(kind)
                end
-               abort
        end
 
        # Extract the `MDoc` from `node` and link it to `mentity`.
index 9a95194..3785ff5 100644 (file)
@@ -23,6 +23,7 @@ import parser_util
 
 # Create a tool context to handle options and paths
 var toolcontext = new ToolContext
+toolcontext.option_context.options_before_rest = true
 toolcontext.tooldescription = "Usage: nit [OPTION]... <file.nit>...\nInterprets and debugs Nit programs."
 # Add an option "-o" to enable compatibilit with the tests.sh script
 var opt = new OptionString("compatibility (does noting)", "-o")
index 31a508a..ac7763e 100644 (file)
@@ -65,7 +65,7 @@ redef class AMethPropdef
                var nosuper = get_single_annotation("nosuper", modelbuilder)
 
                # Collect only for constructors
-               if not mpropdef.mproperty.is_init then
+               if not mpropdef.mproperty.is_init or mpropdef.mproperty.is_new then
                        if nosuper != null then modelbuilder.error(nosuper, "Error: nosuper only in `init`")
                        return
                end
index 0d9c296..3140de5 100644 (file)
@@ -1724,7 +1724,7 @@ redef class ANewExpr
                if not callsite.mproperty.is_new then
                        var kind = recvtype.mclass.kind
                        if kind != concrete_kind then
-                               v.error(self, "Cannot instantiate {kind} {recvtype}.")
+                               v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.")
                                return
                        end
                        self.mtype = recvtype
index 42d2b0c..f393950 100644 (file)
@@ -118,8 +118,8 @@ class NitUnitExecutor
                if toolcontext.opt_noact.value then return
 
                var nit_dir = toolcontext.nit_dir
-               var nitg = "{nit_dir or else ""}/bin/nitg"
-               if nit_dir == null or not nitg.file_exists then
+               var nitg = nit_dir/"bin/nitg"
+               if not nitg.file_exists then
                        toolcontext.error(null, "Cannot find nitg. Set envvar NIT_DIR.")
                        toolcontext.check_errors
                end
index 5936038..1a3006d 100644 (file)
@@ -214,8 +214,8 @@ class TestCase
        fun compile do
                # find nitg
                var nit_dir = toolcontext.nit_dir
-               var nitg = "{nit_dir or else ""}/bin/nitg"
-               if nit_dir == null or not nitg.file_exists then
+               var nitg = nit_dir/"bin/nitg"
+               if not nitg.file_exists then
                        toolcontext.error(null, "Cannot find nitg. Set envvar NIT_DIR.")
                        toolcontext.check_errors
                end
index fb3aeaa..ec91130 100644 (file)
@@ -246,6 +246,9 @@ class ToolContext
        # Option --log-dir
        var opt_log_dir = new OptionString("Directory where to generate log files", "--log-dir")
 
+       # Option --nit-dir
+       var opt_nit_dir = new OptionString("Base directory of the Nit installation", "--nit-dir")
+
        # Option --help
        var opt_help = new OptionBool("Show Help (This screen)", "-h", "-?", "--help")
 
@@ -275,7 +278,12 @@ class ToolContext
 
        init
        do
-               option_context.add_option(opt_warn, opt_warning, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion, opt_stub_man)
+               option_context.add_option(opt_warn, opt_warning, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_nit_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion, opt_stub_man)
+
+               # Hide some internal options
+               opt_stub_man.hidden = true
+               opt_bash_completion.hidden = true
+               opt_set_dummy_tool.hidden = true
        end
 
        # Name, usage and synopsis of the tool.
@@ -365,6 +373,8 @@ The Nit language documentation and the source code of its tools and libraries ma
                        exit 1
                end
 
+               nit_dir = compute_nit_dir
+
                if option_context.rest.is_empty and not accept_no_arguments then
                        print tooldescription
                        print "Use --help for help"
@@ -382,7 +392,6 @@ The Nit language documentation and the source code of its tools and libraries ma
                        log_directory.mkdir
                end
 
-               nit_dir = compute_nit_dir
        end
 
        # Get the current `nit_version` or "DUMMY_VERSION" if `--set-dummy-tool` is set.
@@ -402,27 +411,54 @@ The Nit language documentation and the source code of its tools and libraries ma
        end
 
        # The identified root directory of the Nit project
-       var nit_dir: nullable String = null
+       var nit_dir: String is noinit
 
-       private fun compute_nit_dir: nullable String
+       private fun compute_nit_dir: String
        do
-               # a environ variable has precedence
-               var res = "NIT_DIR".environ
-               if not res.is_empty then return res
+               # the option has precedence
+               var res = opt_nit_dir.value
+               if res != null then
+                       if not check_nit_dir(res) then
+                               fatal_error(null, "Fatal Error: the value of --nit-dir does not seem to be a valid base Nit directory: {res}")
+                       end
+                       return res
+               end
+
+               # then the environ variable has precedence
+               res = "NIT_DIR".environ
+               if not res.is_empty then
+                       if not check_nit_dir(res) then
+                               fatal_error(null, "Fatal Error: the value of NIT_DIR does not seem to be a valid base Nit directory: {res}")
+                       end
+                       return res
+               end
 
                # find the runpath of the program from argv[0]
                res = "{sys.program_name.dirname}/.."
-               if res.file_exists and "{res}/src/nit.nit".file_exists then return res.simplify_path
+               if check_nit_dir(res) then return res.simplify_path
 
                # find the runpath of the process from /proc
                var exe = "/proc/self/exe"
                if exe.file_exists then
                        res = exe.realpath
                        res = res.dirname.join_path("..")
-                       if res.file_exists and "{res}/src/nit.nit".file_exists then return res.simplify_path
+                       if check_nit_dir(res) then return res.simplify_path
+               end
+
+               # search in the PATH
+               var ps = "PATH".environ.split(":")
+               for p in ps do
+                       res = p/".."
+                       if check_nit_dir(res) then return res.simplify_path
                end
 
-               return null
+               fatal_error(null, "Fatal Error: Cannot locate a valid base nit directory. It is quite unexpected. Try to set the environment variable `NIT_DIR` or to use the `--nit-dir` option.")
+               abort
+       end
+
+       private fun check_nit_dir(res: String): Bool
+       do
+               return res.file_exists and "{res}/src/nit.nit".file_exists
        end
 end
 
index 8fdf39a..d36a68b 100644 (file)
@@ -49,6 +49,11 @@ class C
        end
 end
 
+class D
+       super C
+       new(z: Bool): B do return new C(1111)
+end
+
 redef class Int
        new z do return 0
        new a: A do return new A
@@ -79,6 +84,10 @@ end
 
 '\n'.output
 
+(new D(true)).output
+
+'\n'.output
+
 #alt8#(new Int).output
 (new Int.z).output
 (new Int.a).output
index 0c14c1c..8a91562 100644 (file)
@@ -1,4 +1,4 @@
 --log --log-dir out/test_nitc_logs ../examples/hello_world.nit
 base_simple3.nit
 -m test_mixin.nit ../examples/hello_world.nit
-test_define.nit -D text=hello -D num=42 -D flag
+-D text=hello -D num=42 -D flag test_define.nit
index e23154d..a3c8000 100644 (file)
@@ -1 +1 @@
-base_error_new_abstract.nit:21,9--13: Cannot instantiate abstract class A.
+base_error_new_abstract.nit:21,9--13: Type Error: Cannot instantiate abstract class A.
index 0e64d98..c0c2c1b 100644 (file)
@@ -1 +1 @@
-base_error_new_interface.nit:21,9--13: Cannot instantiate interface A.
+base_error_new_interface.nit:21,9--13: Type Error: Cannot instantiate interface A.
index fe346a1..510c2e4 100644 (file)
@@ -13,5 +13,7 @@ B11
 
 B111
 
+B1111
+
 0
 B1
index 15d625a..e9c98c9 100644 (file)
@@ -1 +1 @@
-alt/base_new_alt5.nit:58,1--9: Error: Method 'i' doesn't exists in A.
+alt/base_new_alt5.nit:63,1--9: Error: Method 'i' doesn't exists in A.
index 53c4091..3ac7e2a 100644 (file)
@@ -1 +1 @@
-alt/base_new_alt6.nit:60,1--12: Error: Method 'i' doesn't exists in A.
+alt/base_new_alt6.nit:65,1--12: Error: Method 'i' doesn't exists in A.
index 698bfbf..99ce3e1 100644 (file)
@@ -1 +1 @@
-alt/base_new_alt7.nit:78,2--9: Error: Method 'n2' doesn't exists in C.
+alt/base_new_alt7.nit:83,2--9: Error: Method 'n2' doesn't exists in C.
index 1eed641..51ee855 100644 (file)
@@ -1 +1 @@
-alt/base_new_alt8.nit:82,2--8: Cannot instantiate enum Int.
+alt/base_new_alt8.nit:91,2--8: Type Error: Cannot instantiate enum Int.
index e95a0c5..14ced87 100644 (file)
@@ -1 +1 @@
-alt/error_needed_method_alt2.nit:47,10--27: Cannot instantiate interface Collection[Int].
+alt/error_needed_method_alt2.nit:47,10--27: Type Error: Cannot instantiate interface Collection[Int].
index 800327a..6e0ca35 100644 (file)
@@ -5,7 +5,7 @@ _DUMMY_TOOL()
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD-1]}"
-       opts="--warn --warning --quiet --stop-on-first-error --no-color --log --log-dir --help --version --set-dummy-tool --verbose --bash-completion --stub-man --option-a --option-b"
+       opts="--warn --warning --quiet --stop-on-first-error --no-color --log --log-dir --nit-dir --help --version --set-dummy-tool --verbose --bash-completion --stub-man --option-a --option-b"
        if [[ ${cur} == -* ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
                return 0
index 91fa2fa..b14caf6 100644 (file)
@@ -7,12 +7,10 @@ Test for ToolContext, try --bash-completion.
   --no-color              Do not use color to display errors and warnings
   --log                   Generate various log files
   --log-dir               Directory where to generate log files
+  --nit-dir               Base directory of the Nit installation
   -h, -?, --help          Show Help (This screen)
   --version               Show version and exit
-  --set-dummy-tool        Set toolname and version to DUMMY. Useful for testing
   -v, --verbose           Verbose
-  --bash-completion       Generate bash_completion file for this program
-  --stub-man              Generate a stub manpage in pandoc markdown format
   -a, --option-a          option a, do nothing
   -b, --option-b          option b, do nothing
   -c                      option c, do nothing
index 14be6d4..8179c9c 100755 (executable)
@@ -74,8 +74,8 @@ saferun()
        if test -n "$TIME"; then
                $TIME -o "$o" $a $TIMEOUT "$@"
        else
-               $TIMEOUT "$@"
                if test -n "$a"; then echo 0 >> "$o"; else echo 0 > "$o"; fi
+               $TIMEOUT "$@"
        fi
 }
 
@@ -505,7 +505,7 @@ for ii in "$@"; do
 
                if [ -n "$isinterpret" ]; then
                        cat > "$ff.bin" <<END
-exec $NITC --no-color $OPT "$i" $includes -- "\$@"
+exec $NITC --no-color $OPT $includes -- "$i" "\$@"
 END
                        chmod +x "$ff.bin"
                        > "$ff.cmp.err"
@@ -521,10 +521,10 @@ END
                        # Compile
                        if [ "x$verbose" = "xtrue" ]; then
                                echo ""
-                               echo $NITC --no-color $OPT -o "$ffout" "$i" "$includes" $nocc
+                               echo $NITC --no-color $OPT -o "$ffout" "$includes" $nocc "$i"
                        fi
                        NIT_NO_STACK=1 JNI_LIB_PATH=$JNI_LIB_PATH JAVA_HOME=$JAVA_HOME \
-                               saferun -o "$ff.time.out" $NITC --no-color $OPT -o "$ffout" "$i" $includes $nocc 2> "$ff.cmp.err" > "$ff.compile.log"
+                               saferun -o "$ff.time.out" $NITC --no-color $OPT -o "$ffout" $includes $nocc "$i" 2> "$ff.cmp.err" > "$ff.compile.log"
                        ERR=$?
                        if [ "x$verbose" = "xtrue" ]; then
                                cat "$ff.compile.log"