Merge with 'origin/master' to resolve conflicts with #932 (stream)
authorJean Privat <jean@pryen.org>
Sat, 13 Dec 2014 15:59:16 +0000 (10:59 -0500)
committerJean Privat <jean@pryen.org>
Sun, 14 Dec 2014 02:24:09 +0000 (21:24 -0500)
Conflicts:
lib/standard/stream.nit
lib/standard/string.nit
src/nitls.nit
tests/sav/test_exec.res

217 files changed:
.gitignore
README
benchmarks/.gitignore
benchmarks/bench_engines.sh
benchmarks/bench_languages.sh
benchmarks/bench_strings.sh
bin/nitc [deleted file]
bin/nitg [new file with mode: 0755]
contrib/benitlux/Makefile
contrib/brainfuck/README.md
contrib/friendz/Makefile
contrib/github_merge.nit
contrib/github_search_for_jni/Makefile
contrib/github_search_for_jni/src/github_search_for_jni.nit
contrib/inkscape_tools/Makefile
contrib/inkscape_tools/tests/app/Makefile
contrib/jwrapper/Makefile
contrib/neo_doxygen/Makefile
contrib/neo_doxygen/README.md
contrib/nitcc/README.md
contrib/nitcc/examples/minilang.nit
contrib/nitcc/src/Makefile
contrib/nitcc/src/autom.nit
contrib/nitcc/src/grammar.nit
contrib/nitcc/src/nitcc_lexer0.nit
contrib/nitcc/src/nitcc_semantic.nit
contrib/nitcc/src/re2nfa.nit
contrib/nitester/Makefile
contrib/nitester/src/nitester.nit
contrib/online_ide/Makefile
contrib/online_ide/sources/nit/pnacl_nit.nit
contrib/opportunity/Makefile
contrib/pep8analysis/Makefile
contrib/sort_downloads/Makefile
contrib/tnitter/Makefile
examples/calculator/Makefile
examples/emscripten/fibonacci/Makefile
examples/emscripten/hello_world/Makefile
examples/mnit_ballz/Makefile
examples/mnit_dino/Makefile
examples/mnit_moles/Makefile
examples/mnit_simple/Makefile
examples/mnit_simple/src/test_assets_and_resources.nit
examples/mpi/Makefile
examples/nitcorn/Makefile
examples/nitcorn/src/nitcorn_hello_world.nit
examples/pnacl/converter/Makefile
examples/pnacl/converter/README
examples/rosettacode/hailstone.nit [new file with mode: 0644]
examples/rosettacode/hamming_number.nit [new file with mode: 0644]
lib/a_star.nit
lib/ai/ai.nit
lib/ai/search.nit
lib/android/examples/Makefile
lib/base64.nit
lib/bucketed_game.nit
lib/buffered_ropes.nit
lib/c.nit
lib/cocoa/app_kit.nit [new file with mode: 0644]
lib/cocoa/cocoa.nit [new file with mode: 0644]
lib/cocoa/examples/cocoa_extern_types.nit [new file with mode: 0644]
lib/cocoa/examples/cocoa_message_box.nit [new file with mode: 0644]
lib/cocoa/examples/hello_cocoa.nit [new file with mode: 0644]
lib/cocoa/foundation.nit [new file with mode: 0644]
lib/combinations.nit
lib/console.nit
lib/crypto.nit
lib/csv.nit [deleted file]
lib/csv/csv.nit [new file with mode: 0644]
lib/csv/test_csv.nit [new file with mode: 0644]
lib/dummy_array.nit
lib/egl.nit
lib/emscripten.nit
lib/github/github.nit [new file with mode: 0644]
lib/github/github_curl.nit [moved from lib/github_api.nit with 93% similarity]
lib/github/test_github_curl.nit [new file with mode: 0644]
lib/glesv2/glesv2.nit
lib/html/html.nit
lib/io/push_back_reader.nit
lib/json_serialization.nit
lib/linux/linux_data_store.nit
lib/mpd.nit
lib/mpi.nit
lib/nitcorn/README.md
lib/nitcorn/reactor.nit
lib/nitcorn/server_config.nit
lib/nitcorn/vararg_routes.nit [new file with mode: 0644]
lib/pnacl.nit
lib/poset.nit
lib/realtime.nit
lib/sax/helpers/attributes_impl.nit
lib/sax/helpers/test_attributes_impl.nit
lib/saxophonit/lexer.nit
lib/saxophonit/saxophonit.nit
lib/saxophonit/testing.nit
lib/sdl.nit
lib/sdl2/examples/minimal/Makefile
lib/standard/collection/array.nit
lib/standard/exec.nit
lib/standard/exec_nit.c
lib/standard/file.nit
lib/standard/file_nit.c
lib/standard/file_nit.h
lib/standard/ropes.nit
lib/standard/stream.nit
lib/standard/stream_nit.c [deleted file]
lib/standard/stream_nit.h [deleted file]
lib/standard/time.nit
lib/template/macro.nit
lib/trees/bintree.nit
lib/xdg_basedir/xdg_basedir.nit
misc/bash_completion/nit
misc/jenkins/nitester-wrapper.sh
misc/jenkins/unitrun.sh
misc/vim/syntax/nit.vim
share/man/README.md
share/man/man1/nitc.1 [deleted symlink]
share/man/nit.md
share/man/nitc.md [moved from share/man/nitg.md with 93% similarity]
share/man/nitls.md
share/man/nitpretty.md
share/nitdoc/js/plugins/quicksearch.js
src/Makefile
src/compiler/abstract_compiler.nit
src/doc/doc_model.nit
src/doc/doc_pages.nit
src/doc/doc_templates.nit
src/ffi/ffi.nit
src/ffi/objc.nit [new file with mode: 0644]
src/frontend/serialization_phase.nit
src/full_boostrap
src/interpreter/naive_interpreter.nit
src/interpreter/primitive_types.nit [new file with mode: 0644]
src/loader.nit [new file with mode: 0644]
src/metrics/mendel_metrics.nit
src/metrics/metrics_base.nit
src/mkcsrc
src/model/mmodule.nit
src/model/model.nit
src/model_utils.nit
src/modelbuilder.nit
src/modelbuilder_base.nit [new file with mode: 0644]
src/modelize/modelize_property.nit
src/ncall.sh [new file with mode: 0755]
src/ngall.sh [deleted file]
src/nit.nit
src/nitc.nit [moved from src/nitg.nit with 96% similarity]
src/nitls.nit
src/nitni/nitni_base.nit
src/nitpretty.nit
src/nitx.nit
src/pretty.nit [new file with mode: 0644]
src/rapid_type_analysis.nit
src/semantize/typing.nit
src/testing/testing_suite.nit
src/vm.nit
tests/Linux.skip [new file with mode: 0644]
tests/Makefile
tests/README.md
tests/base_empty_module2.nit [new file with mode: 0644]
tests/nitc.args [moved from tests/nitg.args with 100% similarity]
tests/nitg-g.skip
tests/niti.skip
tests/nitvm.skip
tests/sav/base_as_notnull2.res
tests/sav/base_as_notnull2_alt1.res
tests/sav/base_as_notnull2_alt2.res
tests/sav/base_as_notnull2_alt3.res
tests/sav/base_gen_variance2_alt1.res
tests/sav/base_gen_variance2_alt2.res
tests/sav/base_gen_variance3_alt1.res
tests/sav/base_gen_variance_int_alt1.res
tests/sav/cocoa_extern_types.res [new file with mode: 0644]
tests/sav/cocoa_message_box.res [new file with mode: 0644]
tests/sav/hailstone.res [new file with mode: 0644]
tests/sav/hamming_number.res [new file with mode: 0644]
tests/sav/hello_cocoa.res [new file with mode: 0644]
tests/sav/nitc.res [moved from tests/sav/nitg.res with 54% similarity]
tests/sav/nitc_args1.res [moved from tests/sav/nitg_args1.res with 100% similarity]
tests/sav/nitc_args2.res [moved from tests/sav/nitg_args2.res with 100% similarity]
tests/sav/nitc_args3.res [moved from tests/sav/nitg_args3.res with 100% similarity]
tests/sav/nitc_args4.res [moved from tests/sav/nitg_args4.res with 100% similarity]
tests/sav/nitc_args5.res [moved from tests/sav/nitg_args5.res with 100% similarity]
tests/sav/nitc_args6.res [moved from tests/sav/nitg_args6.res with 100% similarity]
tests/sav/nitc_args7.res [moved from tests/sav/nitg_args7.res with 100% similarity]
tests/sav/nitc_args8.res [moved from tests/sav/nitg_args8.res with 100% similarity]
tests/sav/nith.res
tests/sav/nitls.res
tests/sav/nitls_args1.res
tests/sav/nitls_args2.res
tests/sav/nitls_args3.res
tests/sav/nitls_args4.res
tests/sav/nitserial_args1.res
tests/sav/nitunit_args1.res
tests/sav/test_c_alt1.res
tests/sav/test_fdstream.res
tests/sav/test_ffi_java_callbacks.res
tests/sav/test_ffi_java_generics.res
tests/sav/test_ffi_java_string.res
tests/sav/test_ffi_java_use_module.res
tests/sav/test_ffi_objc_types_and_callbacks.res [new file with mode: 0644]
tests/sav/test_file_open_fail.res [new file with mode: 0644]
tests/sav/test_jvm.res
tests/sav/test_ropes_buffer_add_overflow.res [new file with mode: 0644]
tests/sav/test_ropes_buffer_reverse.res [new file with mode: 0644]
tests/sav/test_ropes_buffer_to_s.res [new file with mode: 0644]
tests/sav/test_sqlite3_nity_alt1.res
tests/sav/test_sqlite3_nity_alt2.res
tests/test_fdstream.nit
tests/test_ffi_objc_types_and_callbacks.nit [new file with mode: 0644]
tests/test_file_open_fail.nit [new file with mode: 0644]
tests/test_ropes_buffer_add_overflow.nit [new file with mode: 0644]
tests/test_ropes_buffer_reverse.nit [new file with mode: 0644]
tests/test_ropes_buffer_to_s.nit [new file with mode: 0644]
tests/test_stream_poll.nit
tests/tests.sh
tests/turing.skip [new file with mode: 0644]

index 62da951..5cd49a0 100644 (file)
@@ -27,7 +27,7 @@ src/*.dot
 src/*.dat
 src/*.gnu
 src/*.bin
-src/nitg_0
+src/nitc_0
 
 c_src/*.o
 c_src/*.cksum
diff --git a/README b/README
index adb80ee..3ee0c42 100644 (file)
--- a/README
+++ b/README
@@ -32,7 +32,6 @@ Important files and directory:
        benchmarks/     Script to bench the compilers
        bin/            The Nit tools
        bin/nitc        The Nit compiler
-       bin/nitg        The new Nit compiler
        bin/nit         The Nit interpreter
        bin/nitdoc      The Nit autodoc
        c_src/          C code of nitc (needed to bootstrap)
index c85e49e..d4ffc90 100644 (file)
@@ -4,4 +4,4 @@
 *.png
 *.xml
 index.html
-nitg
+nitc
index 385d033..6b43e55 100755 (executable)
@@ -44,8 +44,8 @@ function run_compiler()
        local title=$1
        shift
        if test -n "$fast"; then
-               run_command "$@" ../src/nitg.nit -o "nitg.$title.bin"
-               bench_command "nitg-g" "nitg --global ../src/test_parser.nit" "./nitg.$title.bin" -v --global --no-cc ../src/test_parser.nit
+               run_command "$@" ../src/nitc.nit -o "nitc.$title.bin"
+               bench_command "nitc-g" "nitc --global ../src/test_parser.nit" "./nitc.$title.bin" -v --global --no-cc ../src/test_parser.nit
                run_command "$@" ../src/nit.nit -o "nit.$title.bin"
                bench_command "nit" "nit ../src/test_parser.nit ../src/location.nit" "./nit.$title.bin" -v ../src/test_parser.nit -- -n ../src/location.nit
                run_command "$@" ../examples/shoot/src/shoot_logic.nit -o "shoot.$title.bin"
@@ -53,9 +53,9 @@ function run_compiler()
                run_command "$@" ../tests/bench_bintree_gen.nit -o "bintrees.$title.bin"
                bench_command "bintrees" "bench_bintree_gen 16" "./bintrees.$title.bin" 16
        else
-               run_command "$@" ../src/nitg.nit -o "nitg.$title.bin"
-               bench_command "nitg-g" "nitg --global --no-cc ../src/nitls.nit" "./nitg.$title.bin" -v --global --no-cc ../src/nitls.nit
-               bench_command "nitg-s" "nitg --separate ../src/nitg.nit" "./nitg.$title.bin" -v --no-cc --separate ../src/nitg.nit
+               run_command "$@" ../src/nitc.nit -o "nitc.$title.bin"
+               bench_command "nitc-g" "nitc --global --no-cc ../src/nitls.nit" "./nitc.$title.bin" -v --global --no-cc ../src/nitls.nit
+               bench_command "nitc-s" "nitc --separate ../src/nitc.nit" "./nitc.$title.bin" -v --no-cc --separate ../src/nitc.nit
                run_command "$@" ../src/nit.nit -o "nit.$title.bin"
                bench_command "nit" "nit ../src/test_parser.nit ../src/nitls.nit" "./nit.$title.bin" -v ../src/test_parser.nit -- -n ../src/nitls.nit
                run_command "$@" ../src/nitdoc.nit -o "nitdoc.$title.bin"
@@ -116,8 +116,8 @@ fi
 
 ## COMPILE ENGINES
 
-# get the bootstrapped nitg
-cp ../bin/nitg .
+# get the bootstrapped nitc
+cp ../bin/nitc .
 
 ## EFFECTIVE BENCHS ##
 
@@ -125,172 +125,172 @@ function bench_steps()
 {
        name="$FUNCNAME"
        skip_test "$name" && return
-       prepare_res "$name-nitg.dat" "nitg-g" "Various steps of nitg --global"
-       bench_command "parse" "" ./nitg --global --only-parse ../src/nitg.nit
-       bench_command "metamodel" "" ./nitg --global --only-metamodel ../src/nitg.nit
-       bench_command "generate c" "" ./nitg --global --no-cc ../src/nitg.nit
-       bench_command "full" "" ./nitg --global ../src/nitg.nit -o "nitg_nitg.bin"
-
-       prepare_res "$name-nitg-s.dat" "nitg-s" "Various steps of nitg --separate"
-       bench_command "parse" "" ./nitg --separate --only-parse ../src/nitg.nit
-       bench_command "metamodel" "" ./nitg --separate --only-metamodel ../src/nitg.nit
-       bench_command "generate c" "" ./nitg --separate --no-cc ../src/nitg.nit
-       bench_command "full" "" ./nitg --separate ../src/nitg.nit -o "nitg_nitg-e.bin"
-
-       prepare_res "$name-nitg-e.dat" "nitg-e" "Various steps of nitg --erasure"
-       bench_command "parse" "" ./nitg --erasure --only-parse ../src/nitg.nit
-       bench_command "metamodel" "" ./nitg --erasure --only-metamodel ../src/nitg.nit
-       bench_command "generate c" "" ./nitg --erasure --no-cc ../src/nitg.nit
-       bench_command "full" "" ./nitg --erasure ../src/nitg.nit -o "nitg_nitg-e.bin"
+       prepare_res "$name-nitc.dat" "nitc-g" "Various steps of nitc --global"
+       bench_command "parse" "" ./nitc --global --only-parse ../src/nitc.nit
+       bench_command "metamodel" "" ./nitc --global --only-metamodel ../src/nitc.nit
+       bench_command "generate c" "" ./nitc --global --no-cc ../src/nitc.nit
+       bench_command "full" "" ./nitc --global ../src/nitc.nit -o "nitc_nitc.bin"
+
+       prepare_res "$name-nitc-s.dat" "nitc-s" "Various steps of nitc --separate"
+       bench_command "parse" "" ./nitc --separate --only-parse ../src/nitc.nit
+       bench_command "metamodel" "" ./nitc --separate --only-metamodel ../src/nitc.nit
+       bench_command "generate c" "" ./nitc --separate --no-cc ../src/nitc.nit
+       bench_command "full" "" ./nitc --separate ../src/nitc.nit -o "nitc_nitc-e.bin"
+
+       prepare_res "$name-nitc-e.dat" "nitc-e" "Various steps of nitc --erasure"
+       bench_command "parse" "" ./nitc --erasure --only-parse ../src/nitc.nit
+       bench_command "metamodel" "" ./nitc --erasure --only-metamodel ../src/nitc.nit
+       bench_command "generate c" "" ./nitc --erasure --no-cc ../src/nitc.nit
+       bench_command "full" "" ./nitc --erasure ../src/nitc.nit -o "nitc_nitc-e.bin"
 
        plot "$name.gnu"
 }
 bench_steps
 
 # $#: options to compare
-function bench_nitg-g_options()
+function bench_nitc-g_options()
 {
        tag=$1
        shift
        name="$FUNCNAME-$tag"
        skip_test "$name" && return
-       prepare_res "$name.dat" "no options" "nitg-g without options"
-       run_compiler "nitg-g" ./nitg --global
+       prepare_res "$name.dat" "no options" "nitc-g without options"
+       run_compiler "nitc-g" ./nitc --global
 
        if test "$1" = NOALL; then
                shift
        elif test -n "$2"; then
-               prepare_res "$name-all.dat" "all" "nitg-g with all options $@"
-               run_compiler "nitg-g-$tag" ./nitg --global $@
+               prepare_res "$name-all.dat" "all" "nitc-g with all options $@"
+               run_compiler "nitc-g-$tag" ./nitc --global $@
        fi
 
        for opt in "$@"; do
                ot=${opt// /+}
-               prepare_res "$name$ot.dat" "$opt" "nitg-g with option $opt"
-               run_compiler "nitg-g$ot" ./nitg --global $opt
+               prepare_res "$name$ot.dat" "$opt" "nitc-g with option $opt"
+               run_compiler "nitc-g$ot" ./nitc --global $opt
        done
 
        plot "$name.gnu"
 }
-bench_nitg-g_options "slower" --hardening --no-shortcut-range
-bench_nitg-g_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
+bench_nitc-g_options "slower" --hardening --no-shortcut-range
+bench_nitc-g_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
 
-function bench_nitg-s_options()
+function bench_nitc-s_options()
 {
        tag=$1
        shift
        name="$FUNCNAME-$tag"
        skip_test "$name" && return
-       prepare_res "$name.dat" "no options" "nitg-s without options"
-       run_compiler "nitg-s" ./nitg --separate
+       prepare_res "$name.dat" "no options" "nitc-s without options"
+       run_compiler "nitc-s" ./nitc --separate
 
        if test "$1" = NOALL; then
                shift
        elif test -n "$2"; then
-               prepare_res "$name-all.dat" "all" "nitg-s with all options $@"
-               run_compiler "nitg-s-$tag" ./nitg --separate $@
+               prepare_res "$name-all.dat" "all" "nitc-s with all options $@"
+               run_compiler "nitc-s-$tag" ./nitc --separate $@
        fi
 
        for opt in "$@"; do
                ot=${opt// /+}
-               prepare_res "$name-$ot.dat" "$opt" "nitg-s with option $opt"
-               run_compiler "nitg-s$ot" ./nitg --separate $opt
+               prepare_res "$name-$ot.dat" "$opt" "nitc-s with option $opt"
+               run_compiler "nitc-s$ot" ./nitc --separate $opt
        done
 
        plot "$name.gnu"
 }
-bench_nitg-s_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern "--no-gcc-directive likely --no-gcc-directive noreturn"
-bench_nitg-s_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
-bench_nitg-s_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph "--inline-some-methods --direct-call-monomorph" ""
+bench_nitc-s_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern "--no-gcc-directive likely --no-gcc-directive noreturn"
+bench_nitc-s_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert
+bench_nitc-s_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph "--inline-some-methods --direct-call-monomorph" ""
 
-function bench_nitg-e_options()
+function bench_nitc-e_options()
 {
        tag=$1
        shift
        name="$FUNCNAME-$tag"
        skip_test "$name" && return
-       prepare_res "$name.dat" "no options" "nitg-e without options"
-       run_compiler "nitg-e" ./nitg --erasure
+       prepare_res "$name.dat" "no options" "nitc-e without options"
+       run_compiler "nitc-e" ./nitc --erasure
 
        if test "$1" = NOALL; then
                shift
        elif test -n "$2"; then
-               prepare_res "$name-all.dat" "all" "nitg-e with all options $@"
-               run_compiler "nitg-e-$tag" ./nitg --erasure $@
+               prepare_res "$name-all.dat" "all" "nitc-e with all options $@"
+               run_compiler "nitc-e-$tag" ./nitc --erasure $@
        fi
 
        for opt in "$@"; do
                ot=${opt// /+}
-               prepare_res "$name$ot.dat" "$opt" "nitg-e with option $opt"
-               run_compiler "nitg-e$ot" ./nitg --erasure $opt
+               prepare_res "$name$ot.dat" "$opt" "nitc-e with option $opt"
+               run_compiler "nitc-e$ot" ./nitc --erasure $opt
        done
 
        plot "$name.gnu"
 }
-bench_nitg-e_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern
-bench_nitg-e_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert --no-check-erasure-cast
-bench_nitg-e_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph --rta
+bench_nitc-e_options "slower" --hardening --no-shortcut-equal --no-union-attribute --no-shortcut-range --no-inline-intern
+bench_nitc-e_options "nocheck" --no-check-null --no-check-autocast --no-check-attr-isset --no-check-covariance --no-check-assert --no-check-erasure-cast
+bench_nitc-e_options "faster" --skip-dead-methods --inline-coloring-numbers --inline-some-methods --direct-call-monomorph --rta
 
 function bench_engines()
 {
        name="$FUNCNAME"
        skip_test "$name" && return
-       prepare_res "$name-nitg-s.dat" "nitg-s" "nitg with --separate"
-       run_compiler "nitg-s" ./nitg --separate
-       prepare_res "$name-nitg-e.dat" "nitg-e" "nitg with --erasure"
-       run_compiler "nitg-e" ./nitg --erasure
-       prepare_res "$name-nitg-sg.dat" "nitg-sg" "nitg with --separate --semi-global"
-       run_compiler "nitg-sg" ./nitg --separate --semi-global
-       prepare_res "$name-nitg-eg.dat" "nitg-eg" "nitg with --erasure --semi-global"
-       run_compiler "nitg-eg" ./nitg --erasure --semi-global
-       prepare_res "$name-nitg-egt.dat" "nitg-egt" "nitg with --erasure --semi-global --rta"
-       run_compiler "nitg-egt" ./nitg --erasure --semi-global --rta
-       prepare_res "$name-nitg-g.dat" "nitg-g" "nitg with --global"
-       run_compiler "nitg-g" ./nitg --global
+       prepare_res "$name-nitc-s.dat" "nitc-s" "nitc with --separate"
+       run_compiler "nitc-s" ./nitc --separate
+       prepare_res "$name-nitc-e.dat" "nitc-e" "nitc with --erasure"
+       run_compiler "nitc-e" ./nitc --erasure
+       prepare_res "$name-nitc-sg.dat" "nitc-sg" "nitc with --separate --semi-global"
+       run_compiler "nitc-sg" ./nitc --separate --semi-global
+       prepare_res "$name-nitc-eg.dat" "nitc-eg" "nitc with --erasure --semi-global"
+       run_compiler "nitc-eg" ./nitc --erasure --semi-global
+       prepare_res "$name-nitc-egt.dat" "nitc-egt" "nitc with --erasure --semi-global --rta"
+       run_compiler "nitc-egt" ./nitc --erasure --semi-global --rta
+       prepare_res "$name-nitc-g.dat" "nitc-g" "nitc with --global"
+       run_compiler "nitc-g" ./nitc --global
        plot "$name.gnu"
 }
 bench_engines
 
-function bench_nitg-e_gc()
+function bench_nitc-e_gc()
 {
        name="$FUNCNAME"
        skip_test "$name" && return
-       prepare_res "$name-nitg-e.dat" "nitg-e" "nitg with --erasure"
-       run_compiler "nitg-e" ./nitg --erasure
-       prepare_res "$name-nitg-e-malloc.dat" "nitg-e-malloc" "nitg with --erasure and malloc"
-       NIT_GC_OPTION="malloc" run_compiler "nitg-e-malloc" ./nitg --erasure
-       prepare_res "$name-nitg-e-large.dat" "nitg-e-large" "nitg with --erasure and large"
-       NIT_GC_OPTION="large" run_compiler "nitg-e-large" ./nitg --erasure
+       prepare_res "$name-nitc-e.dat" "nitc-e" "nitc with --erasure"
+       run_compiler "nitc-e" ./nitc --erasure
+       prepare_res "$name-nitc-e-malloc.dat" "nitc-e-malloc" "nitc with --erasure and malloc"
+       NIT_GC_OPTION="malloc" run_compiler "nitc-e-malloc" ./nitc --erasure
+       prepare_res "$name-nitc-e-large.dat" "nitc-e-large" "nitc with --erasure and large"
+       NIT_GC_OPTION="large" run_compiler "nitc-e-large" ./nitc --erasure
        plot "$name.gnu"
 }
-bench_nitg-e_gc
+bench_nitc-e_gc
 
-function bench_cc_nitg-e()
+function bench_cc_nitc-e()
 {
        name="$FUNCNAME"
        skip_test "$name" && return
        for o in "gcc0:CC=\"ccache gcc\" CFLAGS=-O0" "cl0:CC=\"ccache clang\" CFLAGS=-O0" "gccs:CC=\"ccache gcc\" CFLAGS=-Os" "cls:CC=\"ccache clang\" CFLAGS=-Os" "gcc2:CC=\"ccache gcc\" CFLAGS=-O2" "cl2:CC=\"ccache clang\" CFLAGS=-O2" "gcc3:CC=\"ccache gcc\" CFLAGS=-O3"  "cl3:CC=\"ccache clang\" CFLAGS=-O3"; do
                f=`echo "$o" | cut -f1 -d:`
                o=`echo "$o" | cut -f2 -d:`
-               prepare_res "$name-nitg-e-$f.dat" "nitg-e-$f" "nitg with --erasure --make-flags $o"
-               run_compiler "nitg-e-$f" ./nitg --erasure --make-flags "$o"
+               prepare_res "$name-nitc-e-$f.dat" "nitc-e-$f" "nitc with --erasure --make-flags $o"
+               run_compiler "nitc-e-$f" ./nitc --erasure --make-flags "$o"
        done
        plot "$name.gnu"
 }
-bench_cc_nitg-e
+bench_cc_nitc-e
 
 function bench_policy()
 {
        name="$FUNCNAME"
        skip_test "$name" && return
-       prepare_res "$name-nitg-s.dat" "nitg-s" "nitg with --separate"
-       run_compiler "nitg-s" ./nitg --separate
-       prepare_res "$name-nitg-e.dat" "nitg-e" "nitg with --erasure"
-       run_compiler "nitg-e" ./nitg --erasure
-       prepare_res "$name-nitg-su.dat" "nitg-su" "nitg with --separate --no-check-covariance"
-       run_compiler "nitg-su" ./nitg --separate --no-check-covariance
-       prepare_res "$name-nitg-eu.dat" "nitg-eu" "nitg with --erasure --no-check-covariance --no-check-erasure-cast"
-       run_compiler "nitg-eu" ./nitg --erasure --no-check-covariance --no-check-erasure-cast
+       prepare_res "$name-nitc-s.dat" "nitc-s" "nitc with --separate"
+       run_compiler "nitc-s" ./nitc --separate
+       prepare_res "$name-nitc-e.dat" "nitc-e" "nitc with --erasure"
+       run_compiler "nitc-e" ./nitc --erasure
+       prepare_res "$name-nitc-su.dat" "nitc-su" "nitc with --separate --no-check-covariance"
+       run_compiler "nitc-su" ./nitc --separate --no-check-covariance
+       prepare_res "$name-nitc-eu.dat" "nitc-eu" "nitc with --erasure --no-check-covariance --no-check-erasure-cast"
+       run_compiler "nitc-eu" ./nitc --erasure --no-check-covariance --no-check-erasure-cast
        plot "$name.gnu"
 }
 bench_policy
@@ -300,13 +300,13 @@ function bench_nullables()
        name="$FUNCNAME"
        skip_test "$name" && return
        prepare_res "$name-nitc.dat" "nitc" "nitc no options"
-       run_compiler "nitc" ./nitg --separate
+       run_compiler "nitc" ./nitc --separate
        prepare_res "$name-nitc-ni.dat" "nitc-ni" "nitc --no-check-attr-isset"
-       run_compiler "nitc" ./nitg --separate --no-check-attr-isset
+       run_compiler "nitc" ./nitc --separate --no-check-attr-isset
        prepare_res "$name-nitc-nu.dat" "nitc-nu" "nitc --no-union-attribute"
-       run_compiler "nitc" ./nitg --separate --no-union-attribute
+       run_compiler "nitc" ./nitc --separate --no-union-attribute
        prepare_res "$name-nitc-nu-ni.dat" "nitc-nu-ni" "nitc --no-union-attribute --no-check-attr-isset"
-       run_compiler "nitc" ./nitg --separate --no-union-attribute --no-check-attr-isset
+       run_compiler "nitc" ./nitc --separate --no-union-attribute --no-check-attr-isset
        plot "$name.gnu"
 }
 bench_nullables
@@ -315,17 +315,17 @@ function bench_compilation_time
 {
        name="$FUNCNAME"
        skip_test "$name" && return
-       prepare_res "$name-nitg-g.dat" "nitg-g" "nitg --global"
-       for i in ../examples/hello_world.nit ../src/test_parser.nit ../src/nitg.nit; do
-               bench_command `basename "$i" .nit` "" ./nitg --global "$i" --no-cc
+       prepare_res "$name-nitc-g.dat" "nitc-g" "nitc --global"
+       for i in ../examples/hello_world.nit ../src/test_parser.nit ../src/nitc.nit; do
+               bench_command `basename "$i" .nit` "" ./nitc --global "$i" --no-cc
        done
-       prepare_res "$name-nitg-s.dat" "nitg-s" "nitg --separate"
-       for i in ../examples/hello_world.nit ../src/test_parser.nit ../src/nitg.nit; do
-               bench_command `basename "$i" .nit` "" ./nitg --separate "$i" --no-cc
+       prepare_res "$name-nitc-s.dat" "nitc-s" "nitc --separate"
+       for i in ../examples/hello_world.nit ../src/test_parser.nit ../src/nitc.nit; do
+               bench_command `basename "$i" .nit` "" ./nitc --separate "$i" --no-cc
        done
-       prepare_res "$name-nitg-e.dat" "nitg-e" "nitg --erasure"
-       for i in ../examples/hello_world.nit ../src/test_parser.nit ../src/nitg.nit; do
-               bench_command `basename "$i" .nit` "" ./nitg --erasure "$i" --no-cc
+       prepare_res "$name-nitc-e.dat" "nitc-e" "nitc --erasure"
+       for i in ../examples/hello_world.nit ../src/test_parser.nit ../src/nitc.nit; do
+               bench_command `basename "$i" .nit` "" ./nitc --erasure "$i" --no-cc
        done
        plot "$name.gnu"
 }
index fea39ff..0e0b59a 100755 (executable)
@@ -70,7 +70,7 @@ fi
 cd ../src
 test -f ./nitc_3 || ./ncall.sh -O
 cd ../benchmarks
-test -f ./nitg || ../src/nitc_3 ../src/nitg.nit -O -v
+test -f ./nitc || ../src/nitc_3 ../src/nitc.nit -O -v
 
 ## EFFECTIVE BENCHS ##
 
@@ -87,7 +87,7 @@ function bench_language()
        s=20
        seq="2 4 8"
        for b in $seq; do
-               run_command ./nitg languages/$name.nit -o $basedir/$name.bin
+               run_command ./nitc languages/$name.nit -o $basedir/$name.bin
                run_command $basedir/$name.bin $basedir "${t}_$b" "$b"
        done
 
@@ -152,35 +152,35 @@ function bench_language()
        done
 
        nitdir="${basedir}/nit"
-       prepare_res $nitdir/$name-nitg.dat "nitg" "nitg"
+       prepare_res $nitdir/$name-nitc.dat "nitc" "nitc"
        for b in $seq; do
-               run_command ./nitg $nitdir/${t}_$b.nit --global -o "$nitdir/${t}_$b.nitg.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-               bench_command "$b" "" "$nitdir/${t}_$b.nitg.bin" $s
+               run_command ./nitc $nitdir/${t}_$b.nit --global -o "$nitdir/${t}_$b.nitc.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc.bin" $s
        done
 
-       prepare_res $nitdir/$name-nitg-s.dat "nitg-s" "nitg-s"
+       prepare_res $nitdir/$name-nitc-s.dat "nitc-s" "nitc-s"
        for b in $seq; do
-               run_command ./nitg $nitdir/${t}_$b.nit --separate -o "$nitdir/${t}_$b.nitg-s.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-               bench_command "$b" "" "$nitdir/${t}_$b.nitg-s.bin" $s
+               run_command ./nitc $nitdir/${t}_$b.nit --separate -o "$nitdir/${t}_$b.nitc-s.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc-s.bin" $s
        done
 
 <<XXX
-       prepare_res $nitdir/$name-nitg-su.dat "nitg-su" "nitg-su"
+       prepare_res $nitdir/$name-nitc-su.dat "nitc-su" "nitc-su"
        for b in $seq; do
-               run_command ./nitg $nitdir/${t}_$b.nit --separate --no-check-covariance -o "$nitdir/${t}_$b.nitg-su.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-               bench_command "$b" "" "$nitdir/${t}_$b.nitg-su.bin" $s
+               run_command ./nitc $nitdir/${t}_$b.nit --separate --no-check-covariance -o "$nitdir/${t}_$b.nitc-su.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc-su.bin" $s
        done
 
-       prepare_res $nitdir/$name-nitg-e.dat "nitg-e" "nitg-e"
+       prepare_res $nitdir/$name-nitc-e.dat "nitc-e" "nitc-e"
        for b in $seq; do
-               run_command ./nitg $nitdir/${t}_$b.nit --erasure -o "$nitdir/${t}_$b.nitg-e.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-               bench_command "$b" "" "$nitdir/${t}_$b.nitg-e.bin" $s
+               run_command ./nitc $nitdir/${t}_$b.nit --erasure -o "$nitdir/${t}_$b.nitc-e.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc-e.bin" $s
        done
 
-       prepare_res $nitdir/$name-nitg-eu.dat "nitg-eu" "nitg-eu"
+       prepare_res $nitdir/$name-nitc-eu.dat "nitc-eu" "nitc-eu"
        for b in $seq; do
-               run_command ./nitg $nitdir/${t}_$b.nit --erasure --no-check-covariance --no-check-erasure-cast -o "$nitdir/${t}_$b.nitg-eu.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-               bench_command "$b" "" "$nitdir/${t}_$b.nitg-eu.bin" $s
+               run_command ./nitc $nitdir/${t}_$b.nit --erasure --no-check-covariance --no-check-erasure-cast -o "$nitdir/${t}_$b.nitc-eu.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc-eu.bin" $s
        done
 XXX
 
index 0bb13a7..16992e9 100755 (executable)
@@ -54,7 +54,7 @@ function bench_array()
                echo "*** Benching Array.to_s performance ***"
        fi
 
-       ../bin/nitg --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_flatstr.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_flatstr.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res arr_tos_ropes.out arr_tos_ropes ropes
        if $verbose; then
@@ -67,7 +67,7 @@ function bench_array()
                bench_command $i ropes$i ./array_tos --loops $2 --strlen $i --ccts $1 "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_flatstr.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_flatstr.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res arr_tos_buf_ropes.out arr_tos_buf_ropes buffered_ropes
        if $verbose; then
@@ -80,7 +80,7 @@ function bench_array()
                bench_command $i buf_ropes$i ./array_tos --loops $2 --strlen $i --ccts $1 "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_flatstr.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_flatstr.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res arr_tos_flat.out arr_tos_flat flatstring
        if $verbose; then
@@ -93,7 +93,7 @@ function bench_array()
                bench_command $i flatstring$i ./array_tos --loops $2 --strlen $i --ccts $1 "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_buffer.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_buffer.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res arr_tos_buf.out arr_tos_buf flatbuffer
        if $verbose; then
@@ -106,7 +106,7 @@ function bench_array()
                bench_command $i flatbuffer$i ./array_tos --loops $2 --strlen $i --ccts $1 "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_manual.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_manual.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res arr_tos_man.out arr_tos_man memmove
        if $verbose; then
@@ -119,7 +119,7 @@ function bench_array()
                bench_command $i memmove$i ./array_tos --loops $2 --strlen $i --ccts $1 "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_man_buf.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_man_buf.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res arr_tos_man_buf.out arr_tos_man_buf flatbuf_with_capacity
        if $verbose; then
@@ -132,7 +132,7 @@ function bench_array()
                bench_command $i flatbuf_with_capacity$i ./array_tos --loops $2 --strlen $i --ccts $1 "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_rope_buf.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/array_tos.nit -m ./strings/array_to_s_vars/array_to_s_rope_buf.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res arr_tos_rope_buf.out arr_tos_rope_buf ropebuf
        if $verbose; then
@@ -150,8 +150,8 @@ function bench_array()
 
 function bench_concat()
 {
-       ../bin/nitg --global ./strings/chain_concat.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-       ../bin/nitg --global ./strings/utf_chain_concat.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/chain_concat.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/utf_chain_concat.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        if $verbose; then
                echo "*** Benching concat performance ***"
@@ -190,7 +190,7 @@ function bench_concat()
                bench_command $i flatstr_utf8_noindex$i ./utf_chain_concat -m flatstr_utf8_noindex --loops $2 --strlen $3 --ccts $i "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/chain_concat.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/chain_concat.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res concat_ropes.out concat_ropes ropes
        if $verbose; then
@@ -203,7 +203,7 @@ function bench_concat()
                bench_command $i ropes$i ./chain_concat -m flatstr --loops $2 --strlen $3 --ccts $i "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/chain_concat.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/chain_concat.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res concat_buf_ropes.out concat_buf_ropes buffered_ropes
        if $verbose; then
@@ -216,7 +216,7 @@ function bench_concat()
                bench_command $i buf_ropes$i ./chain_concat -m flatstr --loops $2 --strlen $3 --ccts $i "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/chain_cct_ropebuf.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/chain_cct_ropebuf.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res cct_buf_ropes.out cct_buf_ropes cctbuf_ropes
        if $verbose; then
@@ -238,8 +238,8 @@ function bench_iteration()
                echo "*** Benching iteration performance ***"
        fi
 
-       ../bin/nitg --global ./strings/iteration_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-       ../bin/nitg --global ./strings/utf_iteration_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/iteration_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/utf_iteration_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res iter_flat_iter.out iter_flat_iter flatstring_iter
        if $verbose; then
@@ -307,7 +307,7 @@ function bench_iteration()
                bench_command $i flatstr_index_utf8_noindex$i ./utf_iteration_bench -m flatstr_utf8_noindex --iter-mode index --loops $2 --strlen $3 --ccts $i "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/iteration_bench.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/iteration_bench.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res iter_ropes_iter.out iter_ropes_iter ropes_iter
        if $verbose; then
@@ -331,7 +331,7 @@ function bench_iteration()
                bench_command $i ropes_index$i ./iteration_bench -m flatstr --iter-mode index --loops $2 --strlen $3 --ccts $i "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/iteration_bench.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/iteration_bench.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res iter_buf_ropes_iter.out iter_buf_ropes_iter buf_ropes_iter
        if $verbose; then
@@ -364,8 +364,8 @@ function bench_substr()
                echo "*** Benching substring performance ***"
        fi
 
-       ../bin/nitg --global ./strings/substr_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-       ../bin/nitg --global ./strings/utf_substr_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/substr_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/utf_substr_bench.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res substr_flat.out substr_flat flatstring
        if $verbose; then
@@ -400,7 +400,7 @@ function bench_substr()
                bench_command $i flatstring_utf8_noindex$i ./utf_substr_bench -m flatstr_utf8_noindex --loops $2 --strlen $3 --ccts $i "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/substr_bench.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/substr_bench.nit -m ../lib/standard/ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res substr_ropes.out substr_ropes ropes
        if $verbose; then
@@ -413,7 +413,7 @@ function bench_substr()
                bench_command $i ropes$i ./substr_bench -m flatstr --loops $2 --strlen $3 --ccts $i "NIT_GC_CHOOSER=large"
        done
 
-       ../bin/nitg --global ./strings/substr_bench.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       ../bin/nitc --global ./strings/substr_bench.nit -m ../lib/standard/ropes.nit -m ../lib/buffered_ropes.nit --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
 
        prepare_res substr_buf_ropes.out substr_buf_ropes buf_ropes
        if $verbose; then
diff --git a/bin/nitc b/bin/nitc
deleted file mode 100755 (executable)
index 42b4e3a..0000000
--- a/bin/nitc
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-dir=`dirname "$BASH_SOURCE"`
-exec "$dir/nitg" "$@"
diff --git a/bin/nitg b/bin/nitg
new file mode 100755 (executable)
index 0000000..4b78164
--- /dev/null
+++ b/bin/nitg
@@ -0,0 +1,4 @@
+#!/bin/bash
+self=`readlink /proc/$$/fd/255`
+dir=`dirname "$self"`
+exec "$dir/nitc" "$@"
index 4dcab2d..133019c 100644 (file)
@@ -2,7 +2,7 @@ all: server
 
 server:
        mkdir -p bin/
-       ../../bin/nitg --dir bin/ src/benitlux_daily.nit src/benitlux_web.nit
+       ../../bin/nitc --dir bin/ src/benitlux_daily.nit src/benitlux_web.nit
 
 src/benitlux_serial.nit:
        ../../bin/nitserial -o $@ src/benitlux_web.nit
index 136b674..8715228 100644 (file)
@@ -29,6 +29,6 @@ First, compile the interpreter with the Nit compiler/interpreter, and launch the
 
 Example:
 ~~~
-nitg ./brainfuck.nit
+nitc ./brainfuck.nit
 ./brainfuck ./examples/hello.bf
 ~~~
index eb9ce29..0039fff 100644 (file)
@@ -2,11 +2,11 @@ default: linux
 
 linux:
        mkdir -p bin
-       ../../bin/nitg -o bin/friendz src/friendz_linux.nit
+       ../../bin/nitc -o bin/friendz src/friendz_linux.nit
 
 android:
        mkdir -p bin
-       ../../bin/nitg -o bin/friendz.apk src/friendz_android.nit
+       ../../bin/nitc -o bin/friendz.apk src/friendz_android.nit
 
 doc:
        mkdir -p doc
index 0d5e052..89433b8 100644 (file)
@@ -15,7 +15,7 @@
 # Query the Github PR API to perform a merge
 module github_merge
 
-import github_api
+import github::github_curl
 import template
 
 redef class Object
index dd874a4..3ffabe8 100644 (file)
@@ -1,3 +1,3 @@
 default:
        mkdir -p bin
-       ../../bin/nitg -o bin/github_search_for_jni src/github_search_for_jni.nit
+       ../../bin/nitc -o bin/github_search_for_jni src/github_search_for_jni.nit
index 35b19b2..8029b2e 100644 (file)
@@ -17,7 +17,7 @@
 # Script to scan Github for repositories possibly using JNI.
 module github_search_for_jni
 
-import github_api
+import github::github_curl
 
 # The proprieties introduced by this redef are to be used only on a JSON object
 # representing a Github repository.
index 834ad14..d1f81e0 100644 (file)
@@ -2,7 +2,7 @@ all: bins tests
 
 bins:
        mkdir -p bin
-       ../../bin/nitg --dir bin src/svg_to_png_and_nit.nit src/svg_to_icons.nit
+       ../../bin/nitc --dir bin src/svg_to_png_and_nit.nit src/svg_to_icons.nit
 
 tests: test-dino test-app
 
index b788061..b42c5f4 100644 (file)
@@ -2,11 +2,11 @@ all: images icons linux
 
 linux:
        mkdir -p bin
-       ../../../../bin/nitg -o bin/s2pn src/s2pn_linux.nit
+       ../../../../bin/nitc -o bin/s2pn src/s2pn_linux.nit
 
 android:
        mkdir -p bin
-       ../../../../bin/nitg -o bin/s2pn.apk src/s2pn_android.nit
+       ../../../../bin/nitc -o bin/s2pn.apk src/s2pn_android.nit
 
 images:
        mkdir -p assets/images
index 5a5f77f..b554f0d 100644 (file)
@@ -2,7 +2,7 @@ default:
        mkdir -p bin
        make -C ../nitcc
        ../nitcc/src/nitcc ./grammar/javap.sablecc
-       ../../bin/nitg ./src/jwrapper.nit -o ./bin/jwrapper
+       ../../bin/nitc ./src/jwrapper.nit -o ./bin/jwrapper
        mv *.nit ./src/
        mkdir -p gen
        mv javap* ./gen/
index 53383f7..332b4ce 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-NITG=../../bin/nitg
+NITG=../../bin/nitc
 NITG_FLAGS=--dir bin
 NEO4J_DIR=/var/lib/neo4j
 OLD_PWD=${PWD}
index 67b0e52..9da5b92 100644 (file)
@@ -6,7 +6,7 @@ Neo4j that is readable by the `nx` tool.
 
 ## Installation
 
-Ensure that you have a working version of `nitg` in `../../bin` then run `make`
+Ensure that you have a working version of `nitc` in `../../bin` then run `make`
 in the present directory. The executable will be then generated at
 `bin/neo_doxygen`.
 
index 82ee0ff..91e9ad1 100644 (file)
@@ -20,7 +20,7 @@ nitcc generates a bunches of control files, a lexer, a parser, and a tester.
 
 To compile and run the tester:
 
-    nitg file_test_parser.nit
+    nitc file_test_parser.nit
     ./file_test_parser an_input_file_to_parse
 
 ## Examples and regression tests
index 4160f01..e316369 100644 (file)
@@ -1,15 +1,23 @@
 import minilang_test_parser
 
+# An naive recursive stack-based interpreter of the minilang language.
 class Interpretor
        super Visitor
+
+       # A stack of numeric values
        var stack = new Array[Int]
+
+       # A stack of boolean values
        var bstack = new Array[Bool]
+
+       # The current values assigned to each variable
        var vars = new HashMap[String, Int]
 
        redef fun visit(n) do n.accept_calculator(self)
 end
 
 redef class Node
+       # Execution of the node by the interpreter `v`
        fun accept_calculator(v: Interpretor) do visit_children(v)
 end
 
index f66a44e..ae422ff 100644 (file)
@@ -1,4 +1,4 @@
-NITC=../../../bin/nitg
+NITC=../../../bin/nitc
 
 all: nitcc calc minilang
 
index 8825b01..5ece999 100644 (file)
@@ -23,18 +23,18 @@ class Automaton
        # The start state
        var start: State
 
-       # State that are accect states
+       # State that are accept states
        var accept = new Array[State]
 
        # All states
        var states = new Array[State]
 
-       # Tokens associated on accept states
-       # use `add_tag` to update
+       # Tokens associated on accept states.
+       # Use `add_tag` to update
        var tags = new HashMap[State, Set[Token]]
 
-       # Accept states associated on tokens
-       # use `add_tag` to update
+       # Accept states associated on tokens.
+       # Use `add_tag` to update
        var retrotags = new HashMap[Token, Set[State]]
 
        # Tag all accept states
@@ -66,7 +66,7 @@ class Automaton
                assert retrotags[t].has(s)
        end
 
-       # Remove all occurences of a tag in an automaton
+       # Remove all occurrences of a tag in an automaton
        fun clear_tag(t: Token)
        do
                if not retrotags.has_key(t) then return
@@ -78,7 +78,7 @@ class Automaton
                retrotags.keys.remove(t)
        end
 
-       # Remove tokens from conflicting state according the the inclusion of language
+       # Remove tokens from conflicting state according the inclusion of language.
        # REQUIRE: self isa DFA automaton
        fun solve_token_inclusion
        do
@@ -101,8 +101,8 @@ class Automaton
                end
        end
 
-       # Initialize a new automaton for the empty language
-       # one state, no accept, no transition
+       # Initialize a new automaton for the empty language.
+       # One state, no accept, no transition.
        init empty
        do
                var state = new State
@@ -110,8 +110,8 @@ class Automaton
                states.add state
        end
 
-       # Initialize a new automaton for the empty-string language
-       # one state, is accept, no transition
+       # Initialize a new automaton for the empty-string language.
+       # One state, is accept, no transition.
        init epsilon
        do
                var state = new State
@@ -120,8 +120,8 @@ class Automaton
                states.add state
        end
 
-       # Initialize a new automation for the language that accepts only a single symbol
-       # Two state, the second is accept, one transition on `symbol`
+       # Initialize a new automation for the language that accepts only a single symbol.
+       # Two state, the second is accept, one transition on `symbol`.
        init atom(symbol: Int)
        do
                var s = new State
@@ -148,8 +148,8 @@ class Automaton
                states.add a
        end
 
-       # Contatenate `other` to `self`
-       # other is modified and invalidated.
+       # Concatenate `other` to `self`.
+       # Other is modified and invalidated.
        fun concat(other: Automaton)
        do
                var s2 = other.start
@@ -160,7 +160,7 @@ class Automaton
                states.add_all other.states
        end
 
-       # `self` become the alternation of `self` and `other`
+       # `self` become the alternation of `self` and `other`.
        # `other` is modified and invalidated.
        fun alternate(other: Automaton)
        do
@@ -184,9 +184,9 @@ class Automaton
                states.add_all other.states
        end
 
-       # Return a new automaton that recognize `self` but not `other`
-       # For a theorical POV, this is the substraction of languages.
-       # Note: the implementation use `to_dfa` internally, so the theorical complexity is not cheap.
+       # Return a new automaton that recognize `self` but not `other`.
+       # For a theoretical POV, this is the subtraction of languages.
+       # Note: the implementation use `to_dfa` internally, so the theoretical complexity is not cheap.
        fun except(other: Automaton): Automaton
        do
                var ta = new Token("1")
@@ -209,8 +209,8 @@ class Automaton
                return c
        end
 
-       # `self` absorbs all states, transisions, tags, and acceptations of `other`
-       # An epsilon transition is added between `self.start` and `other.start`
+       # `self` absorbs all states, transitions, tags, and acceptations of `other`.
+       # An epsilon transition is added between `self.start` and `other.start`.
        fun absorb(other: Automaton)
        do
                states.add_all other.states
@@ -494,8 +494,8 @@ class Automaton
                f.close
        end
 
-       # Transform a NFA to a DFA
-       # note: the DFA is not miminized
+       # Transform a NFA to a DFA.
+       # note: the DFA is not minimized.
        fun to_dfa: Automaton
        do
                trim
@@ -581,8 +581,8 @@ class Automaton
                return dfa
        end
 
-       # epsilon-closure on a state of states
-       # used by `to_dfa`
+       # Epsilon-closure on a state of states.
+       # Used by `to_dfa`.
        private fun eclosure(states: Collection[State]): Set[State]
        do
                var res = new ArraySet[State]
@@ -601,8 +601,8 @@ class Automaton
                return res
        end
 
-       # trans on a set of states
-       # Used by `to_dfa`
+       # Trans on a set of states.
+       # Used by `to_dfa`.
        fun trans(states: Collection[State], symbol: Int): Set[State]
        do
                var res = new ArraySet[State]
@@ -621,9 +621,9 @@ class Automaton
                return res
        end
 
-       # Generate the Nit source code of the lexer
-       # `filepath` is the name of the ouptit file
-       # `parser` is the name of the parser module (used to import the token classes)
+       # Generate the Nit source code of the lexer.
+       # `filepath` is the name of the output file.
+       # `parser` is the name of the parser module (used to import the token classes).
        fun gen_to_nit(filepath: String, name: String, parser: nullable String)
        do
                var gen = new DFAGenerator(filepath, name, self, parser)
@@ -638,12 +638,9 @@ private class DFAGenerator
        var automaton: Automaton
        var parser: nullable String
 
-       var out: OStream
-       init(filepath: String, name: String, automaton: Automaton, parser: nullable String) do
-               self.filepath = filepath
-               self.name = name
-               self.automaton = automaton
-               self.parser = parser
+       var out: OStream is noinit
+
+       init do
                self.out = new OFStream.open(filepath)
        end
 
@@ -747,9 +744,9 @@ end
 # A state in a finite automaton
 class State
        # Outgoing transitions
-
        var outs = new Array[Transition]
-       # Ingoing tyransitions
+
+       # Ingoing transitions
        var ins = new Array[Transition]
 
        # Add a transitions to `to` on `symbol` (null means epsilon)
@@ -761,6 +758,8 @@ class State
                return t
        end
 
+       # Get the first state following the transition `i`.
+       # Null if no transition for `i`.
        fun trans(i: Int): nullable State
        do
                for t in outs do
@@ -778,7 +777,12 @@ end
 
 # A range of symbols on a transition
 class TSymbol
+       # The first symbol in the range
        var first: Int
+
+       # The last symbol if any.
+       #
+       # `null` means infinity.
        var last: nullable Int
 
        redef fun to_s
@@ -808,8 +812,8 @@ class Transition
        # The symbol on the transition (null means epsilon)
        var symbol: nullable TSymbol
 
-       # Remove the transition from the automaton
-       # Detash from `from` and `to`
+       # Remove the transition from the automaton.
+       # Detach from `from` and `to`.
        fun delete
        do
                from.outs.remove(self)
index 620b0a0..eea312f 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# A gramar describing a language
+# A grammar describing a language
 class Gram
-       # The productions (non-terminal) of the conctete grammar
+       # The productions (non-terminal) of the concrete grammar
        var prods = new Array[Production]
 
-       # The additionnal abstract productions of the grammar
+       # The additional abstract productions of the grammar
        # TODO clean AST
        var ast_prods = new Array[Production]
 
@@ -35,7 +35,7 @@ class Gram
                                res.append("{p.name} =\n")
                        end
                        var last = null
-                       if not p.alts.is_empty then p.alts.last
+                       if not p.alts.is_empty then last = p.alts.last
                        for a in p.alts do
                                res.append("\t\{{a.name}:\} {a.elems.join(" ")}")
                                if a.codes == null then a.make_codes
@@ -55,7 +55,7 @@ class Gram
                return res.to_s
        end
 
-       # Inline (ie. remove from the conctete grammar) some production
+       # Inline (ie. remove from the concrete grammar) some production
        # REQUIRE: no circular production in `prods`
        fun inline(prods: Collection[Production])
        do
@@ -322,14 +322,14 @@ class Production
        # The alternative of the production
        var alts = new Array[Alternative]
 
-       # Additionnal alternatives in the AST
+       # Additional alternatives in the AST
        var ast_alts = new Array[Alternative]
 
        # Is self the accept production
        var accept = false
 
        # Is self transformed to a other production for the AST
-       # FIXME: cleaup AST
+       # FIXME: cleanup AST
        var spe: nullable Production = null is writable
 
        # Is self contains only a single alternative (then no need for a abstract production class in the AST)
@@ -345,8 +345,10 @@ class Production
 
        # Is the production nullable
        var is_nullable = false
+
        # The first tokens of the production
        var firsts = new HashSet[Item]
+
        # The tokens that may follows the production (as in SLR)
        var afters = new HashSet[Item]
 
@@ -432,7 +434,7 @@ class Alternative
        # Is the alternative unparsable? (ie not in the automaton)
        var phony = false is writable
 
-       # Imitialize codes with the elements
+       # Initialize codes with the elements
        fun make_codes
        do
                if codes != null then return
@@ -445,20 +447,27 @@ class Alternative
        end
 end
 
-# A step in the construction of the AST. used to modelize transformations
+# A step in the construction of the AST.
+# Used to model transformations
 interface Code
 end
+
 # Get a element from the stack
 class CodePop
        super Code
        redef fun to_s do return "pop"
 end
-# Allocate a new AST node for an alternative using the correct number of poped elements
+
+# Allocate a new AST node for an alternative using the correct number of popped elements
 class CodeNew
        super Code
+
+       # The associated alternative
        var alt: Alternative
+
        redef fun to_s do return "New {alt.name}/{alt.elems.length}"
 end
+
 # Get null
 class CodeNull
        super Code
@@ -477,7 +486,7 @@ abstract class Element
        # The mangled name of the element
        fun cname: String do return "N{name.to_cmangle}"
 
-       # the name of the class in the AST
+       # The name of the class in the AST
        fun acname: String do
                var res = acname_cache
                if res == null then
@@ -486,15 +495,17 @@ abstract class Element
                end
                return res
        end
+
+       # The name of the class in the AST
        fun acname=(s: String) do acname_cache = s
 end
 
 # A terminal element
 class Token
        super Element
-       # States of the LR automatio that shift on self
+       # States of the LR automaton that shift on self
        var shifts = new ArraySet[LRState]
-       # States of the LR automatio that reduce on self in the lookahead(1)
+       # States of the LR automaton that reduce on self in the lookahead(1)
        var reduces = new ArraySet[LRState]
 end
 
@@ -872,13 +883,13 @@ end
 
 # A state in a LR automaton
 class LRState
-       # name of the automaton (short part from the start)
+       # Name of the automaton (short part from the start)
        var name: String
 
-       # malglen name
+       # Mangled name
        fun cname: String do return name.to_cmangle
 
-       # number
+       # Number
        var number: Int = -1
 
        # Set of all items
@@ -893,7 +904,7 @@ class LRState
        # Ingoing transitions
        var outs = new Array[LRTransition]
 
-       # trans function
+       # Trans function
        fun trans(e: Element): nullable LRState
        do
                for t in outs do if t.elem == e then return t.to
@@ -915,7 +926,7 @@ class LRState
                return true
        end
 
-       # Recusively extends item outside the core
+       # Recursively extends item outside the core
        fun extends(i: Item)
        do
                var e = i.next
@@ -940,7 +951,7 @@ class LRState
        var gotos = new ArraySet[Production]
        # Reduction guarded by tokens
        var guarded_reduce = new HashMap[Token, Set[Item]]
-       # Shitfs guarded by tokens
+       # Shifts guarded by tokens
        var guarded_shift = new HashMap[Token, Set[Item]]
 
        # Does the state need a guard to perform an action?
@@ -949,7 +960,7 @@ class LRState
        # Is the state LR0?
        fun is_lr0: Bool do return reduces.length <= 1 and shifts.is_empty or reduces.is_empty
 
-       # compute guards and conflicts
+       # Compute guards and conflicts
        fun analysis
        do
                # Extends the core
@@ -1031,7 +1042,7 @@ class LRState
                end
        end
 
-       # Return `i` and all other items of the state that expands, directly or undirectly, to `i`
+       # Return `i` and all other items of the state that expands, directly or indirectly, to `i`
        fun back_expand(i: Item): Set[Item]
        do
                var res = new ArraySet[Item]
@@ -1056,7 +1067,7 @@ end
 class LRTransition
        # The origin state
        var from: LRState
-       # The testination state
+       # The destination state
        var to: LRState
        # The element labeling the transition
        var elem: Element
@@ -1085,14 +1096,14 @@ class Item
                return b.to_s
        end
 
-       # The element thatr follow the dot, null if the fdot is at the end
+       # The element that follows the dot, null if the dot is at the end
        fun next: nullable Element
        do
                if pos >= alt.elems.length then return null
                return alt.elems[pos]
        end
 
-       # SLR loohahead
+       # SLR lookahead
        fun lookahead: Set[Token]
        do
                var res = new HashSet[Token]
index 7fa876f..f3ff58d 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Ad-hoc hand-writen lexer for nitcc
-# This avoid to commit (and relyon ) a generated lexer
-#
+# Ad-hoc hand-written lexer for nitcc
+# This avoid to commit (and rely on) a generated lexer
 module nitcc_lexer0
 
 # Required for the tokens definitions
 import nitcc_parser
 
-# Hand-writen lexer of nitcc
-# Used only for the boostrap of the tool.
+# Hand-written lexer of nitcc.
+# Used only for the bootstrap of the tool.
 class Lexer_nitcc
+       # The text to tokenize
        var text: String
 
-       var iter: Iterator[Char] = "".chars.iterator
+       # The iterator on text
+       private var iter: Iterator[Char] is noinit
+
+       # The current position
        var pos = 0
 
-       var tokens = new Array[NToken]
+       # The tokens currently produced
+       private var tokens = new Array[NToken]
 
+       # Tokenize and returns the tokens
        fun lex: Array[NToken]
        do
                iter = text.chars.iterator
@@ -88,13 +93,13 @@ class Lexer_nitcc
                return tokens
        end
 
-       fun error(c: Char)
+       private fun error(c: Char)
        do
                print "pos {pos}: Lexer error on '{c}'."
                abort
        end
 
-       fun str
+       private fun str
        do
                var b = new FlatBuffer
                b.add('\'')
@@ -121,7 +126,7 @@ class Lexer_nitcc
                abort
        end
 
-       fun id(c: Char)
+       private fun id(c: Char)
        do
                var b = new FlatBuffer
                b.add c
@@ -138,7 +143,7 @@ class Lexer_nitcc
                tokens.add token
        end
 
-       fun kw(c: Char)
+       private fun kw(c: Char)
        do
                var b = new FlatBuffer
                b.add c
@@ -155,7 +160,7 @@ class Lexer_nitcc
                tokens.add token
        end
 
-       fun trim
+       private fun trim
        do
                while iter.is_ok and iter.item <= ' ' do
                        iter.next
index 73baa05..af417d4 100644 (file)
@@ -28,6 +28,7 @@ import re2nfa
 class CollectNameVisitor
        super Visitor
 
+       # All the productions
        var nprods = new Array[Nprod]
 
        # Symbol table to associate things (prods and exprs) with their name
@@ -44,7 +45,7 @@ class CollectNameVisitor
        # The current production, used to initialize priorities
        var prod: nullable Production = null
 
-       # The current priority counter to name tranformed productions
+       # The current priority counter to name transformed productions
        var pricpt: Int = 0
 
        # Run the semantic analysis of the grammar
@@ -53,13 +54,13 @@ class CollectNameVisitor
                # First visit to collect names
                enter_visit(n)
 
-               # Second visit to use collectec names and build rhings
+               # Second visit to use collected names and build things
                var v2 = new CheckNameVisitor(self)
                v2.enter_visit(n)
 
-               # Inline all the ?
+               # Inline all the `?`
                gram.inline(v2.quesizes.values)
-               # Inlile all the prods sufixed by '_imline' #TODO use a real keyword
+               # Inline all the prods suffixed by '_inline' #TODO use a real keyword
                for p in gram.prods do
                        if not p.name.has_suffix("_inline") then continue
                        print "inline {p}"
@@ -102,14 +103,14 @@ private class CheckNameVisitor
        # The collected element names, for the alternative
        var elems_names = new Array[nullable String]
 
-       # The collected elementname, for the nelem
+       # The collected element names, for the nelem
        var elemname: nullable String = null
 
        # Is the alternative transformed, for the alternative
        var trans = false
 
        # The current priority class
-       # Used the check, and tranform the grammar
+       # Used the check, and transform the grammar
        var pri: nullable Npriority = null
 
        # Known ignored tokens
@@ -119,7 +120,7 @@ private class CheckNameVisitor
        var rejecteds = new Array[Element]
 
        # Pool of elements that are modified with + (reuse them!)
-       private var plusizes = new HashMap[Element, Production]
+       var plusizes = new HashMap[Element, Production]
 
        # Create a + version of an element
        fun plusize(e: Element): Production
@@ -136,7 +137,7 @@ private class CheckNameVisitor
        end
 
        # Pool of elements that are modified with ? (reuse them!)
-       private var quesizes = new HashMap[Element, Production]
+       var quesizes = new HashMap[Element, Production]
 
        # Create a ? version of an element
        fun quesize(e: Element): Production
@@ -155,14 +156,13 @@ private class CheckNameVisitor
        end
 
        # The current nexpr, used to track dependency on named expressions (see `Nexpr::precs`)
-       var nexpr: nullable Nexpr
+       var nexpr: nullable Nexpr = null
 
        # The current production, used to initialize alternatives
-       var prod: nullable Production
+       var prod: nullable Production = null
 
        # The main visitor, used to access the grammar of the symbol table
        var v1: CollectNameVisitor
-       init(v1: CollectNameVisitor) do self.v1 = v1
 
        redef fun visit(n) do n.accept_check_name_visitor(self)
 end
@@ -204,7 +204,7 @@ redef class Nexpr
        # The associated NFA (cached, see `build_nfa`)
        private var nfa: nullable Automaton
 
-       # Build the NFA, possibily building the NFA of required expressions
+       # Build the NFA, possibly building the NFA of required expressions
        # Print an error if there is a circular dependency
        # The result is cached
        fun build_nfa: Automaton do
@@ -269,7 +269,7 @@ redef class Nign
                                abort
                        else if e isa Token then
                                # The token was build and registered during the visit
-                               # So, unregister then, the bit Ignred token will be build later
+                               # So, unregister them, the big Ignored token will be build later
                                v.v1.gram.tokens.remove(e)
                        else
                                abort
@@ -298,13 +298,13 @@ redef class Nrej
 end
 
 redef class Nprod
-       # The associated main production
-       # ie the last priority class
+       # The associated main production.
+       # i.e. the last priority class.
        var prod: nullable Production
 
-       # The associated most-priority production
-       # ie the first priority class
-       # If there is no priority then `sub_prod == prod`
+       # The associated most-priority production.
+       # i.e. the first priority class.
+       # If there is no priority then `sub_prod == prod`.
        var sub_prod: nullable Production
 
        redef fun accept_collect_prod(v) do
@@ -376,13 +376,14 @@ redef class Natrans
 end
 
 redef class Npriority
+       # It is the last priority group?
        var is_last = false
 
        # The associated production
        var prod: nullable Production
 
-       # The production in the with the next less priority class
-       # null is there is no priority or if the first priority class
+       # The production in the with the next less priority class.
+       # `null` if there is no priority or if the first priority class.
        var next: nullable Production
 
        redef fun accept_collect_prod(v) do
@@ -409,7 +410,7 @@ redef class Npriority
                v.pri = self
                super
 
-               # Inject a new alternative that goes to the next less prioty class
+               # Inject a new alternative that goes to the next less priority class
                var alt = prod.new_alt2(prod.name + "_" + prod.alts.length.to_s, [next.as(not null)])
                alt.trans = true
                alt.codes = [new CodePop]
@@ -462,6 +463,8 @@ redef class Npriority_unary
 end
 
 redef class Alternative
+       # The short name of the alternative.
+       # Used for errors
        var short_name: nullable String
 end
 
index 6bca660..d0d90ac 100644 (file)
@@ -37,7 +37,6 @@ redef class Nstr
        redef fun make_rfa: Automaton
        do
                var a = new Automaton.epsilon
-               var val
                for c in self.value.chars do
                        var b = new Automaton.atom(c.ascii)
                        a.concat(b)
index ffe3530..ccefcf6 100644 (file)
@@ -1,3 +1,3 @@
 all:
        mkdir -p bin/
-       ../../bin/nitg src/nitester.nit -o bin/nitester
+       ../../bin/nitc src/nitester.nit -o bin/nitester
index 37b5594..3431a86 100644 (file)
@@ -31,6 +31,9 @@ abstract class Processor
        # Controller rank is always 0
        var controller_rank: Rank = 0.rank
 
+       # Rank on this processor
+       fun rank: Rank is abstract
+
        # Where to store data for transfer between nodes
        #
        # Require: `buffer.length % 4 == 0`
@@ -49,7 +52,7 @@ abstract class Processor
        # Tag of a new task packet of size `tasks_per_packet`
        var task_tag: Tag = 0.tag
 
-       # Tag to return a set of `Result` throught `buffer`
+       # Tag to return a set of `Result` thought `buffer`
        var result_tag: Tag = 1.tag
 
        # Tag to notify `Worker` when to quit
@@ -67,7 +70,10 @@ abstract class Processor
        # Run the main logic of this node
        fun run is abstract
 
-       # Engines targetted by this execution
+       # Hash or name of the branch to test
+       var branch_hash: String is noinit
+
+       # Engines targeted by this execution
        var engines: Array[String] is noinit
 
        # All known engines, used to detect errors in `engines`
@@ -86,6 +92,10 @@ abstract class Processor
        fun read_cli_options
        do
                var opt_ctx = new OptionContext
+               var opt_hash = new OptionString(
+                       "Branch to test",
+                       "--hash", "-h")
+               opt_hash.mandatory = true
                var opt_engines = new OptionString(
                        "Engines to test, separated with commas ({all_engines.join(", ")} or all)",
                        "--engine", "-e")
@@ -97,7 +107,7 @@ abstract class Processor
                        "Clean up all nitester files (and do not run tests)",
                        "--cleanup", "-C")
 
-               opt_ctx.add_option(opt_engines, opt_help, opt_verbose, opt_cleanup)
+               opt_ctx.add_option(opt_hash, opt_engines, opt_help, opt_verbose, opt_cleanup)
                opt_ctx.parse args
 
                # --help?
@@ -132,6 +142,9 @@ abstract class Processor
                if rest.is_empty then opt_ctx.usage_error "This tool needs at least one test_program.nit"
                test_programs = rest
 
+               # hash
+               branch_hash = opt_hash.value.as(not null)
+
                # gather and check engines
                var engines_str = opt_engines.value
                var engines
@@ -160,12 +173,30 @@ abstract class Processor
        # All tasks to be performed
        var tasks = new Array[Task]
 
-       # Gather and registar all tasks
+       # Gather and register all tasks
        fun create_tasks
        do
+               # At this point we are in our local nit
+               var skip_path = "tests/turing.skip"
+               var skip
+               if skip_path.file_exists then
+                       var skip_file = new IFStream.open(skip_path)
+                       skip = skip_file.read_lines
+                       skip_file.close
+               else
+                       skip = new Array[String]
+               end
+
                for prog in test_programs do for engine in engines do
+
+                       # Is is blacklisted?
+                       for s in skip do if not s.is_empty and prog.has(s) then
+                               if verbose > 0 and rank == 0 then print "Skipping test '{prog}' because of '{s}' in turing.skip"
+                               continue label
+                       end
+
                        tasks.add new Task(engine, prog)
-               end
+               end label
        end
 end
 
@@ -173,6 +204,8 @@ end
 class Controller
        super Processor
 
+       redef fun rank do return controller_rank
+
        # Id as `Int` of the next task to distribute
        var next_task_id = 0
 
@@ -326,14 +359,11 @@ class Worker
        super Processor
 
        # The `Rank` of `self`
-       var rank: Rank
+       redef var rank: Rank
 
        # Compilation directory
        var comp_dir = "/dev/shm/nit_compile{rank}" is lazy
 
-       # Output file directory
-       var out_dir = "/dev/shm/nit_out{rank}" is lazy
-
        # Directory to store the xml files produced for Jenkins
        var xml_dir = "~/jenkins_xml/"
 
@@ -341,7 +371,10 @@ class Worker
        var tests_sh_out = "/dev/shm/nit_local_out{rank}" is lazy
 
        # Source Nit repository, must be already updated and `make` before execution
-       var nit_source_dir = "~/nit"
+       var local_nit = "/dev/shm/nit{rank}" is lazy
+
+       # Remote Nit repository (actually the local source)
+       var remote_nit = "~/nit/"
 
        # Compiled `Regex` to detect the argument of an execution
        var re_arg: Regex = "arg [0-9]+".to_re
@@ -364,6 +397,26 @@ class Worker
        fun setup
        do
                if verbose > 0 then sys.system "hostname"
+
+               if local_nit.file_exists then local_nit.rmdir
+
+               exec_and_check "git clone {remote_nit} {local_nit}"
+               local_nit.chdir
+               exec_and_check "git config remote.origin.fetch +refs/remotes/origin/pr/*:refs/remotes/origin/pr/*"
+               exec_and_check "git fetch origin --quiet"
+               exec_and_check "git checkout {branch_hash}"
+               exec_and_check "cp {remote_nit}/bin/nitg bin/"
+               exec_and_check "src/git-gen-version.sh"
+               exec_and_check "bin/nitg --dir bin/ src/nit.nit src/nitvm.nit"
+       end
+
+       private fun exec_and_check(cmd: String)
+       do
+               if verbose > 0 then
+                       print "+ {cmd}"
+                       var res = sys.system(cmd)
+                       assert res == 0 else print "Command '{cmd}' failed."
+               end
        end
 
        # Clean up the testing environment
@@ -372,8 +425,8 @@ class Worker
        fun cleanup
        do
                if comp_dir.file_exists then comp_dir.rmdir
-               if out_dir.file_exists then out_dir.rmdir
                if tests_sh_out.file_exists then tests_sh_out.file_delete
+               if local_nit.file_exists then local_nit.file_delete
        end
 
        # Single C `int` to hold the next task id received from the `Controller`
@@ -401,11 +454,12 @@ class Worker
                                        if task_id >= tasks.length then break
                                        var task = tasks[task_id]
 
+                                       "tests".chdir
+
                                        # Command line to execute test
-                                       var cmd = "XMLDIR={xml_dir} ERRLIST={out_dir}/errlist TMPDIR={out_dir} " +
+                                       var cmd = "XMLDIR={xml_dir} " +
                                                "CCACHE_DIR={ccache_dir} CCACHE_TEMPDIR={ccache_dir} CCACHE_BASEDIR={comp_dir} " +
-                                               "./tests.sh --compdir {comp_dir} --outdir {out_dir} " +
-                                               " --node --engine {task.engine} {task.test_program} > {tests_sh_out}"
+                                               "./tests.sh --node --engine {task.engine} {task.test_program} > {tests_sh_out}"
 
                                        # Execute test
                                        sys.system cmd
index 34ef866..e8ed742 100644 (file)
@@ -2,7 +2,7 @@
 ACE_BUILDS ?= ../../../ace-builds/
 
 default:
-       ../../bin/nitg --semi-global sources/nit/pnacl_nit.nit -I ../../src/
+       ../../bin/nitc --semi-global sources/nit/pnacl_nit.nit -I ../../src/
        cp pnacl_nit/pnacl_nit.pexe www/pnacl/ -f
        rm -rf pnacl_nit/
        rm -rf .nit_compile
index d50b84a..7d0e701 100644 (file)
@@ -21,7 +21,7 @@ import interpreter::naive_interpreter
 import interpreter::debugger
 import pnacl
 intrude import toolcontext
-intrude import modelbuilder
+intrude import loader
 intrude import standard::file
 
 # We redefine exit to start a new thread before killing the one that called exit.
index bbcf10b..f1c7392 100644 (file)
@@ -1,3 +1,3 @@
 all:
        mkdir -p bin/
-       ../../bin/nitg --dir bin/ src/opportunity_web.nit
+       ../../bin/nitc --dir bin/ src/opportunity_web.nit
index c2f2669..cf8c325 100644 (file)
@@ -1,6 +1,6 @@
 bin/pep8analysis:
        mkdir -p bin
-       ../../bin/nitg -o bin/pep8analysis src/pep8analysis.nit
+       ../../bin/nitc -o bin/pep8analysis src/pep8analysis.nit
 
 doc/index.html:
        ../../bin/nitdoc src/pep8analysis.nit
@@ -9,7 +9,7 @@ tests: bin/pep8analysis
        bin/pep8analysis --cfg-long tests/privat/*.pep tests/micro/*.pep tests/terrasa/*.pep
 
 www/pep8analysis.js:
-       ../../bin/nitg -o www/pep8analysis.js --semi-global src/pep8analysis_web.nit
+       ../../bin/nitc -o www/pep8analysis.js --semi-global src/pep8analysis_web.nit
        mkdir -p www/samples
        cp tests/micro/*.pep tests/privat/02-fibo.pep tests/privat/06-calc-non-pur.pep www/samples
 
index 5a161e9..83db947 100644 (file)
@@ -1,6 +1,6 @@
 build:
        mkdir -p bin/
-       ../../bin/nitg -o bin/sort_downloads src/sort_downloads.nit
+       ../../bin/nitc -o bin/sort_downloads src/sort_downloads.nit
 
 install:
        install bin/sort_downloads /usr/local/bin/
index c2a1d4a..5bb713b 100644 (file)
@@ -1,3 +1,3 @@
 all:
        mkdir -p bin/
-       ../../bin/nitg --dir bin src/tnitter.nit
+       ../../bin/nitc --dir bin src/tnitter.nit
index acb600b..f16f885 100644 (file)
@@ -1,8 +1,8 @@
 all:
        mkdir -p bin/
-       ../../bin/nitg --dir bin/ src/calculator_gtk.nit src/calculator_test.nit
+       ../../bin/nitc --dir bin/ src/calculator_gtk.nit src/calculator_test.nit
 
 android:
        mkdir -p bin/ res/
        ../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
-       ../../bin/nitg -o bin/calculator.apk src/calculator_android.nit
+       ../../bin/nitc -o bin/calculator.apk src/calculator_android.nit
index 4ec01ea..773bafd 100644 (file)
@@ -1,2 +1,2 @@
 emscripten:
-       ../../../bin/nitg -o www/fibonacci.js ../../fibonacci.nit -m emscripten
+       ../../../bin/nitc -o www/fibonacci.js ../../fibonacci.nit -m emscripten
index 5fea224..c58231b 100644 (file)
@@ -1,2 +1,2 @@
 emscripten:
-       ../../../bin/nitg -o www/hello_world.js ../../hello_world.nit -m emscripten
+       ../../../bin/nitc -o www/hello_world.js ../../hello_world.nit -m emscripten
index ad19515..337c69a 100644 (file)
@@ -5,7 +5,7 @@ default: android
 
 android: icon
        mkdir -p bin
-       ../../bin/nitg -o bin/ballz.apk src/ballz_android.nit
+       ../../bin/nitc -o bin/ballz.apk src/ballz_android.nit
 
 icon: ../../contrib/inkscape_tools/bin/svg_to_icons
        mkdir -p res
index 607d571..42364bf 100644 (file)
@@ -2,11 +2,11 @@ default: linux
 
 linux:
        mkdir -p bin
-       ../../bin/nitg -o bin/dino src/dino_linux.nit
+       ../../bin/nitc -o bin/dino src/dino_linux.nit
 
 android: android-icons
        mkdir -p bin
-       ../../bin/nitg -o bin/dino.apk src/dino_android.nit
+       ../../bin/nitc -o bin/dino.apk src/dino_android.nit
 
 ../../contrib/inkscape_tools/bin/svg_to_icons:
        $(MAKE) -C ../../contrib/inkscape_tools
index d46e5fc..1c4c732 100644 (file)
@@ -2,11 +2,11 @@ default: linux
 
 linux:
        mkdir -p bin
-       ../../bin/nitg -o bin/moles src/moles_linux.nit
+       ../../bin/nitc -o bin/moles src/moles_linux.nit
 
 android: android-icons
        mkdir -p bin
-       ../../bin/nitg -o bin/moles.apk src/moles_android.nit
+       ../../bin/nitc -o bin/moles.apk src/moles_android.nit
 
 ../../contrib/inkscape_tools/bin/svg_to_icons:
        $(MAKE) -C ../../contrib/inkscape_tools
index 791adb3..01189c9 100644 (file)
@@ -2,11 +2,11 @@ default: linux
 
 linux:
        mkdir -p bin
-       ../../bin/nitg -o bin/simple src/simple_linux.nit
+       ../../bin/nitc -o bin/simple src/simple_linux.nit
 
 android:
        mkdir -p bin
-       ../../bin/nitg -o bin/simple.apk src/complete_simple_android.nit
+       ../../bin/nitc -o bin/simple.apk src/complete_simple_android.nit
 
 clean:
        rm -rf bin
index b2564bf..c4ffc7f 100644 (file)
@@ -33,14 +33,14 @@ redef class App
        # Testing the assets manager
        fun test_assets
        do
-               assert asset_manager.bitmap("fighter.png") != null
+               assert asset_manager.bitmap("fighter.png").width == 32
        end
 
        # Testing the resources manager
        fun test_resources do
                assert resource_manager.string("string_test") == "string test"
                assert resource_manager.boolean("test_bool") == true
-               assert resource_manager.dimension("test_dimen_1") != null
-               assert resource_manager.dimension("test_dimen_2") != null
+               assert resource_manager.dimension("test_dimen_1") == 25
+               assert resource_manager.dimension("test_dimen_2") == 150
        end
 end
index 09212ae..07654b4 100644 (file)
@@ -1,6 +1,6 @@
 bin/mpi_simple:
        mkdir -p bin/
-       ../../bin/nitg src/mpi_simple.nit -o bin/mpi_simple
+       ../../bin/nitc src/mpi_simple.nit -o bin/mpi_simple
 
 run:
        mpiexec.openmpi --hostfile hosts bin/mpi_simple
index 510983c..22113aa 100644 (file)
@@ -1,7 +1,7 @@
 all:
        mkdir -p bin/
-       ../../bin/nitg --dir bin src/nitcorn_hello_world.nit src/file_server_on_port_80.nit
+       ../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/file_server_on_port_80.nit
 
 xymus.net:
        mkdir -p bin/
-       ../../bin/nitg --dir bin/ -I ../../contrib/tnitter/src/ -I ../../contrib/benitlux/src/ -I ../../contrib/opportunity/src/ src/xymus_net.nit
+       ../../bin/nitc --dir bin/ -I ../../contrib/tnitter/src/ -I ../../contrib/benitlux/src/ -I ../../contrib/opportunity/src/ src/xymus_net.nit
index fd7fe73..a9e8f55 100644 (file)
@@ -23,7 +23,8 @@ module nitcorn_hello_world
 
 import nitcorn
 
-class MyAction
+# An action that responds by displaying a static html content.
+class StaticAction
        super Action
 
        redef fun answer(http_request, turi)
@@ -49,10 +50,29 @@ class MyAction
        end
 end
 
+# An action that uses parameterized uris to customize the output.
+class ParamAction
+       super Action
+
+       redef fun answer(http_request, turi)
+       do
+               var response = new HttpResponse(200)
+               var name = http_request.param("name")
+               if name == null then
+                       response.body = "No name..."
+               else
+                       response.body = "Hello {name}"
+               end
+               return response
+       end
+end
+
+
 var vh = new VirtualHost("localhost:8080")
 
 # Serve index.html with our custom handler
-vh.routes.add new Route("/index.html", new MyAction)
+vh.routes.add new Route("/index.html", new StaticAction)
+vh.routes.add new Route("/hello/:name", new ParamAction)
 
 # Serve everything else with a standard `FileServer` with a root at "www/hello_world/"
 vh.routes.add new Route(null, new FileServer("www/hello_world/"))
index aee71e8..1ef5995 100644 (file)
@@ -1,5 +1,5 @@
 default: 
-       ../../../bin/nitg --semi-global converter.nit
+       ../../../bin/nitc --semi-global converter.nit
 
 HTTPD_PY := python $(NACL_SDK_ROOT)/tools/httpd.py
 serve:
index fa70fff..f487f9f 100644 (file)
@@ -5,7 +5,7 @@ Steps to make the example work :
 2. Declare the environment variable NACL_SDK_ROOT as the root of the target platform within the SDK (ex: ~/nacl_sdk/pepper_34/) :
        $ export NACL_SDK_ROOT=/path/to/nacl_sdk/pepper_[your_version]
 
-3. Compile the Nit code with: `nitg --semi-global converter.nit` or `make`.
+3. Compile the Nit code with: `nitc --semi-global converter.nit` or `make`.
 
 You must use the '--semi-global' (or `--global`) option. Some features in the standard library are not supported by the NaCL platform, the global compiler do not try to compile them.
 
diff --git a/examples/rosettacode/hailstone.nit b/examples/rosettacode/hailstone.nit
new file mode 100644 (file)
index 0000000..b76c533
--- /dev/null
@@ -0,0 +1,57 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+# Copyright 2014 amineorion <amineorion@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Task: Hailstone sequence
+# SEE: <http://rosettacode.org/wiki/Hailstone_sequence>
+#
+# Not optimize version.
+
+module hailstone
+
+fun hailstone (n: Int): Array[Int]
+do
+       var sequence = [n]
+       while n != 1 do
+               if n.bin_and(0x01) == 0 then
+                       n = n / 2
+               else
+                       n = 3 * n + 1
+               end
+               sequence.add(n)
+       end
+       return sequence
+end
+var sequence27 = hailstone(27)
+var size27 = sequence27.length
+print "Sequence for 27 has {size27} begin with: {sequence27[0]}, " +
+                "{sequence27[1]}, {sequence27[2]}, {sequence27[3]} " +
+                "and end with: {sequence27[size27 - 4]}, {sequence27[size27 - 3]}, " +
+                "{sequence27[size27 - 2]}, {sequence27[size27 - 1]}"
+
+var max = hailstone(1).length
+var max_sequence = hailstone(1)
+var max_element = 1
+
+for i in [2..100000] do
+       var sequence = hailstone(i)
+       if max < sequence.length then
+               max = sequence.length
+               max_element = i
+               max_sequence = sequence
+       end
+end
+print "The number with longest sequence is {max_element} " +
+               "with length of {max_sequence.length}"
diff --git a/examples/rosettacode/hamming_number.nit b/examples/rosettacode/hamming_number.nit
new file mode 100644 (file)
index 0000000..3ff8a44
--- /dev/null
@@ -0,0 +1,129 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+# Copyright 2014 amineorion <amineorion@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Task: Hamming numbers
+# SEE: <http://rosettacode.org/wiki/Hamming_numbers>
+#
+# Version that don't support 1,000,000th position of Hamming number yet.
+
+module hamming_number
+
+class HammingTriple
+       super Comparable
+
+       var a: Int
+       var b: Int
+       var c: Int
+
+       redef type OTHER: HammingTriple
+
+       init(a: Int, b: Int, c: Int)
+       do
+               self.a = a
+               self.b = b
+               self.c = c
+       end
+
+       redef fun >(other: OTHER): Bool
+       do
+               if a > other.a and b > other.b and c > other.c then
+                       return true
+               end
+
+               if a < other.a and b < other.b and c < other.c then
+                       return false
+               end
+
+               if self == other then
+                       return false
+               end
+
+               var log1 = log
+               var log2 = other.log
+
+               if not log1.is_approx(log2, 0.000001) then
+                       return log1 > log2
+               end
+               return false
+       end
+
+       fun log: Float
+       do
+               return a.to_f * 2.0.log + b.to_f * 3.0.log + c.to_f * 5.0.log
+       end
+
+       redef fun ==(o)
+       do
+               return o isa HammingTriple and a == o.a and b == o.b and c == o.c
+       end
+
+       fun calcul: Int
+       do
+               return (2 ** a) * (3 ** b) * (5 ** c)
+       end
+
+       redef fun to_s: String
+       do
+               return "a: {a}, b: {b}, c: {c}"
+       end
+end
+
+class HammingArray
+       super Array[HammingTriple]
+
+       fun get_min: HammingTriple
+       do
+               var min = self[0]
+               var min_index = 0
+               for i in [1..length[ do
+                       if min > self[i] then
+                               min = self[i]
+                               min_index = i
+                       end
+               end
+               remove_at(min_index)
+               return min
+       end
+end
+
+fun hamming(limit_position: Int): Int
+do
+       var hamming_triple = new HammingArray
+       hamming_triple.add(new HammingTriple(0, 0, 0))
+       loop
+               var current = hamming_triple.get_min
+               limit_position -= 1
+               if limit_position == 0 then
+                       return current.calcul
+               end
+
+               if current.a == 0 and current.b == 0 then
+                       hamming_triple.add(new HammingTriple(current.a, current.b, current.c + 1))
+               end
+
+               if current.a == 0 then
+                       hamming_triple.add(new HammingTriple(current.a, current.b + 1, current.c))
+               end
+
+               hamming_triple.add(new HammingTriple(current.a + 1, current.b, current.c))
+       end
+end
+
+for i in [1..20] do
+       print "{i}: {hamming(i)}"
+end
+
+print "1691: {hamming(1691)}"
index 97925ff..0c5ed80 100644 (file)
@@ -116,7 +116,7 @@ class Node
                loop
                        var frontier_node: nullable N = null
 
-                       var bucket_searched: Int = 0
+                       var bucket_searched = 0
 
                        # find next valid node in frontier/buckets
                        loop
index 6041e73..d5a0ec9 100644 (file)
@@ -8,6 +8,7 @@
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
+# Simple toolkit for artificial intelligence.
 module ai
 
 import backtrack
index 64904ee..69b5c0d 100644 (file)
@@ -159,8 +159,6 @@ interface SearchProblem[S: Object, A]
        # `steps` is the maximum number of steps a giver configuration can run.
        fun run_configs(steps: Int)
        do
-               var s
-
                var c = 0
                loop
                        if astar.run_config(steps, c, "A*") then break
@@ -597,7 +595,7 @@ class SearchSolver[S: Object, A]
                print msg
 
                var t = new Clock
-               var res = run_steps(steps)
+               run_steps(steps)
                print "\t{self}"
                var l = t.lapse
                print "\ttime={l}"
index 5255657..f0fd982 100644 (file)
@@ -1,7 +1,7 @@
 android:
        mkdir -p bin/ res/
        ../../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
-       ../../../bin/nitg --dir bin/ src/ui_test.nit
+       ../../../bin/nitc --dir bin/ src/ui_test.nit
        adb install -r bin/ui_test.apk
 
 install: android
index 2d941e9..7b78d78 100644 (file)
@@ -36,6 +36,10 @@ redef class String
        # Encodes the receiver string to base64.
        # By default, uses "=" for padding.
        fun encode_base64 : String do return encode_base64_custom_padding( '=' )
+
+       # Encodes the receiver string to base64 using a custom padding character.
+       #
+       # If using the default padding character `=`, see `encode_base64`.
        fun encode_base64_custom_padding( padding : Char ) : String
        do
                var base64_chars = once base64_chars
@@ -78,6 +82,10 @@ redef class String
        # Decodes the receiver string from base64.
        # By default, uses "=" for padding.
        fun decode_base64 : String do return decode_base64_custom_padding( '=' )
+
+       # Decodes the receiver string to base64 using a custom padding character.
+       #
+       # If using the default padding character `=`, see `decode_base64`.
        fun decode_base64_custom_padding( padding : Char ) : String
        do
                var inverted_base64_chars = once inverted_base64_chars
index 09a4ee8..703ff5c 100644 (file)
@@ -24,6 +24,8 @@ module bucketed_game
 
 # Something acting on the game
 class Turnable[G: Game]
+
+       # Execute `turn` for this instance.
        fun do_turn(turn: GameTurn[G]) is abstract
 end
 
@@ -39,9 +41,11 @@ class Bucketable[G: Game]
        fun cancel_act do act_at = null
 end
 
-# Optiomized organization of `Bucketable` instances
+# Optimized organization of `Bucketable` instances
 class Buckets[G: Game]
        super Turnable[G]
+
+       # Bucket type used in this implementation.
        type BUCKET: HashSet[Bucketable[G]]
 
        private var buckets: Array[BUCKET] is noinit
@@ -59,6 +63,7 @@ class Buckets[G: Game]
                end
        end
 
+       # Add the Bucketable event `e` at `at_tick`.
        fun add_at(e: Bucketable[G], at_tick: Int)
        do
                var at_key = key_for_tick(at_tick)
@@ -103,6 +108,8 @@ end
 
 # Game related event
 interface GameEvent
+
+       # Apply `self` to `game` logic.
        fun apply( game : ThinGame ) is abstract
 end
 
@@ -113,39 +120,43 @@ end
 
 # Game logic on the client
 class ThinGame
+
+       # Game tick when `self` should act.
+       #
+       # Default is 0.
        var tick: Int = 0 is protected writable
 end
 
 # Game turn on the client
 class ThinGameTurn[G: ThinGame]
-       var tick: Int = 0 is protected writable
 
-       var events: List[GameEvent] = new List[GameEvent] is protected writable
+       # Game tick when `self` should act.
+       var tick: Int is protected writable
 
-       init (t: Int) do tick = t
+       # List of game events occured for `self`.
+       var events = new List[GameEvent] is protected writable
 end
 
 # Game turn on the full logic
 class GameTurn[G: Game]
        super ThinGameTurn[G]
+
+       # Game that `self` belongs to.
        var game: G
 
-       init (g: G)
-       do
-               super(g.tick)
-               game = g
+       # Create a new game turn for `game`.
+       init (game: G) is old_style_init do
+               super(game.tick)
+               self.game = game
        end
 
-       fun act_next(e: Bucketable[G])
-       do
-               game.buckets.add_at(e, tick + 1)
-       end
+       # Insert the Bucketable event `e` to be executed at next tick.
+       fun act_next(e: Bucketable[G]) do game.buckets.add_at(e, tick + 1)
 
-       fun act_in(e: Bucketable[G], t: Int)
-       do
-               game.buckets.add_at(e, tick + t)
-       end
+       # Insert the Bucketable event `e` to be executed at tick `t`.
+       fun act_in(e: Bucketable[G], t: Int) do game.buckets.add_at(e, tick + t)
 
+       # Add and `apply` a game `event`.
        fun add_event( event : GameEvent )
        do
                event.apply( game )
@@ -156,8 +167,11 @@ end
 # Full game logic
 class Game
        super ThinGame
+
+       # Game type used in this implementation.
        type G: Game
 
+       # Bucket list in this game.
        var buckets: Buckets[G] = new Buckets[G]
 
        # Last turn executed in this game
@@ -167,6 +181,10 @@ class Game
 
        init do end
 
+       # Execute and return a new GameTurn.
+       #
+       # This method calls `do_pre_turn` before executing the GameTurn
+       # and `do_post_turn` after.
        fun do_turn: GameTurn[G]
        do
                var turn = new GameTurn[G](self)
@@ -182,6 +200,15 @@ class Game
                return turn
        end
 
+       # Execute something before executing the current GameTurn.
+       #
+       # Should be redefined by clients to add a pre-turn behavior.
+       # See `Game::do_turn`.
        fun do_pre_turn(turn: GameTurn[G]) do end
+
+       # Execute something after executing the current GameTurn.
+       #
+       # Should be redefined by clients to add a post-turn behavior.
+       # See `Game::do_turn`.
        fun do_post_turn(turn: GameTurn[G]) do end
 end
index b287871..bbed4ea 100644 (file)
@@ -68,10 +68,13 @@ end
 private class LeafSubstrings
        super IndexedIterator[Text]
 
-       var str: String
+       var leaf: Leaf
+       var str: String is noinit
        var avail = true
 
-       init(l: Leaf) do str = new FlatString.with_infos(l.buf.ns, l.length, 0, l.length - 1)
+       init do
+               str = new FlatString.with_infos(leaf.buf.ns, leaf.length, 0, leaf.length - 1)
+       end
 
        redef fun is_ok do return avail
 
@@ -86,8 +89,8 @@ end
 private class Leaf
        super RopeString
 
-       private var buf: ManualBuffer
-       private var bns: NativeString is noinit
+       var buf: ManualBuffer
+       var bns: NativeString is noinit
        redef var length: Int is noinit
 
        redef fun empty do return new Leaf(new ManualBuffer)
@@ -240,7 +243,6 @@ redef class Concat
 
        redef fun +(o) do
                var s = o.to_s
-               var mlen = length
                var slen = s.length
                if s isa FlatString then
                        var r = right
@@ -286,7 +288,6 @@ redef class FlatString
                        return new Concat(sl + self, s.right)
                else if s isa Leaf then
                        if slen + mlen > maxlen then return new Concat(self, s)
-                       var mits = items
                        var mifrom = index_from
                        var sb = s.buf
                        var b = new ManualBuffer
index 3732085..d94ae8e 100644 (file)
--- a/lib/c.nit
+++ b/lib/c.nit
@@ -29,7 +29,7 @@ abstract class CArray[E]
        # Pointer to the real C array
        var native_array: NATIVE is noinit
 
-       private init(length: Int) do self._length = length
+       private init(length: Int) is old_style_init do self._length = length
 
        redef fun [](index)
        do
@@ -38,6 +38,7 @@ abstract class CArray[E]
                return native_array[index]
        end
 
+       # Set `val` at `index`.
        fun []=(index: Int, val: E)
        do
                assert not destroyed
@@ -45,7 +46,14 @@ abstract class CArray[E]
                native_array[index] = val
        end
 
+       # Was this instance destroyed?
+       #
+       # See `CArray::destroy`.
        var destroyed = false
+
+       # Free used memory used by `native_array`.
+       #
+       # Also set `destroyed` to true.
        fun destroy
        do
                if destroyed then return
@@ -57,9 +65,14 @@ end
 
 # A native C array, as in a pointer to the first element of the array
 extern class NativeCArray `{ void * `}
+
+       # Type of contained elements.
        type E: nullable Object
 
+       # Get element at `index`.
        fun [](index: E): E is abstract
+
+       # Set `val` at `index`.
        fun []=(index: E, val: E) is abstract
 
        # Return pointer to the address to the second element of this array
@@ -73,8 +86,8 @@ class CIntArray
        super CArray[Int]
        redef type NATIVE: NativeCIntArray
 
-       init(size: Int)
-       do
+       # Initialize a new CIntArray of `size` elements.
+       init(size: Int) is old_style_init do
                native_array = new NativeCIntArray(size)
                super size
        end
@@ -95,7 +108,9 @@ extern class NativeCIntArray `{ int* `}
        super NativeCArray
        redef type E: Int
 
+       # Initialize a new NativeCIntArray of `size` elements.
        new(size: Int) `{ return calloc(size, sizeof(int)); `}
+
        redef fun [](index) `{ return recv[index]; `}
        redef fun []=(index, val) `{ recv[index] = val; `}
 
@@ -108,8 +123,7 @@ class CByteArray
        redef type NATIVE: NativeCByteArray
 
        # Allocate a new array of `size`
-       init(size: Int)
-       do
+       init(size: Int) is old_style_init do
                native_array = new NativeCByteArray(size)
                super size
        end
diff --git a/lib/cocoa/app_kit.nit b/lib/cocoa/app_kit.nit
new file mode 100644 (file)
index 0000000..91bc87c
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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 Application Kit provides services to create GUI
+module app_kit is c_linker_option "-framework AppKit"
+
+import foundation
+
+in "ObjC Header" `{
+       #import <AppKit/AppKit.h>
+`}
+
+# A simple message box
+extern class NSAlert in "ObjC" `{ NSAlert * `}
+       super NSObject
+
+       # Allocate and instanciate a new `NSAlert`
+       new in "ObjC" `{ return [[NSAlert alloc] init]; `}
+
+       # Set the content of this message box
+       fun message_text=(text: NSString) in "ObjC" `{ [recv setMessageText:text]; `}
+
+       # Show this message box
+       fun run_modal in "ObjC" `{ [recv runModal]; `}
+end
diff --git a/lib/cocoa/cocoa.nit b/lib/cocoa/cocoa.nit
new file mode 100644 (file)
index 0000000..99c6b12
--- /dev/null
@@ -0,0 +1,26 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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 Cocoa API is the development layer of OS X
+#
+# This module is only compatible with OS X.
+#
+# This wrapper of the Cocoa API regroups the Foundation Kit and the
+# Application Kit.
+module cocoa is c_linker_option "-framework Cocoa"
+
+import foundation
+import app_kit
diff --git a/lib/cocoa/examples/cocoa_extern_types.nit b/lib/cocoa/examples/cocoa_extern_types.nit
new file mode 100644 (file)
index 0000000..775e6e4
--- /dev/null
@@ -0,0 +1,43 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+# Test extern classes from the Cocoa framework and extern factories
+module cocoa_extern_types
+
+import cocoa
+
+in "ObjC Header" `{
+       // for `NSString::hello`
+       #import <Foundation/Foundation.h>
+`}
+
+redef extern class NSString
+
+       # Additionnal factory
+       new hello in "ObjC" `{ return @"Factory Hello"; `}
+end
+
+# Print a custom string to the log
+fun nslog(text: NSString) in "ObjC" `{
+       NSLog(text);
+`}
+
+nslog "Hello using to_nsstring".to_nsstring
+nslog new NSString.hello
+
+var msg_box = new NSAlert
+msg_box.message_text = "From Nit".to_nsstring
+if "NIT_TESTING".environ != "true" then msg_box.run_modal
diff --git a/lib/cocoa/examples/cocoa_message_box.nit b/lib/cocoa/examples/cocoa_message_box.nit
new file mode 100644 (file)
index 0000000..72e35d7
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+# Simple message box using the Cocoa framework
+module cocoa_message_box
+
+import cocoa
+
+in "ObjC" `{
+       #import <Cocoa/Cocoa.h>
+`}
+
+fun dialog in "ObjC" `{
+       NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+       [alert setMessageText:@"Hello world!"];
+       [alert runModal];
+`}
+
+if "NIT_TESTING".environ != "true" then dialog
diff --git a/lib/cocoa/examples/hello_cocoa.nit b/lib/cocoa/examples/hello_cocoa.nit
new file mode 100644 (file)
index 0000000..3a2b168
--- /dev/null
@@ -0,0 +1,33 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+# Hello world using the Cocoa framework
+module hello_cocoa
+
+import cocoa::foundation
+
+in "ObjC" `{
+       #import <Foundation/Foundation.h>
+`}
+
+# Print `"Hello world!"` to the log
+fun hello_world in "ObjC" `{
+       @autoreleasepool {
+               NSLog(@"Hello World!");
+       }
+`}
+
+hello_world
diff --git a/lib/cocoa/foundation.nit b/lib/cocoa/foundation.nit
new file mode 100644 (file)
index 0000000..ab027fd
--- /dev/null
@@ -0,0 +1,47 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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 Foundation Kit provides basic Objective-C classes and structures
+module foundation is c_linker_option "-framework Foundation"
+
+in "ObjC Header" `{
+       #import <Foundation/Foundation.h>
+`}
+
+# Base of the Foundation framework class hierarchy
+extern class NSObject in "ObjC" `{ NSObject * `}
+end
+
+# String of the Foundation Kit
+#
+# Created using `Text::to_nsstring`.
+extern class NSString in "ObjC" `{ NSString * `}
+       super NSObject
+end
+
+redef class NativeString
+       # Get a `NSString` from `self` with the specified `length`
+       fun to_nsstring(length: Int): NSString in "ObjC" `{
+               return [[NSString alloc] initWithBytes:recv
+                       length:length
+                       encoding:NSASCIIStringEncoding];
+       `}
+end
+
+redef class Text
+       # Get a `NSString` from `self`
+       fun to_nsstring: NSString do return to_cstring.to_nsstring(length)
+end
index 4076c03..cb1612a 100644 (file)
@@ -120,7 +120,7 @@ private class CartesianIterator[E]
        var collection: CartesianCollection[E]
 
        # The array of iterations that will be increased in the lexicographic order.
-       private var iterators = new Array[Iterator[E]]
+       var iterators = new Array[Iterator[E]]
 
        init
        do
@@ -314,8 +314,8 @@ private class CombinationIterator[E]
        super Iterator[SequenceRead[E]]
        var product: CombinationCollection[E]
 
-       private var iterators = new Array[Iterator[E]]
-       private var indices = new Array[Int]
+       var iterators = new Array[Iterator[E]]
+       var indices = new Array[Int]
 
        var are_sorted: Bool is noinit
        var are_unique: Bool is noinit
@@ -374,7 +374,7 @@ private class CombinationIterator[E]
                end
        end
 
-       private fun next_free(rank: Int, start: Int): Int
+       fun next_free(rank: Int, start: Int): Int
        do
                loop
                        for i in [0..rank[ do
@@ -388,7 +388,7 @@ private class CombinationIterator[E]
                return start
        end
 
-       private fun reset_iterator(rank: Int): Iterator[E]
+       fun reset_iterator(rank: Int): Iterator[E]
        do
                var it = product.collection.iterator
                iterators[rank] = it
index e922a84..c0a8772 100644 (file)
@@ -44,8 +44,6 @@ end
 class TermMoveUp
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -56,8 +54,6 @@ end
 class TermMoveDown
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -68,8 +64,6 @@ end
 class TermMoveFoward
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -80,8 +74,6 @@ end
 class TermMoveBackward
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -102,8 +94,6 @@ class TermMove
        # 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.
index a84358f..a4abbd1 100644 (file)
@@ -69,7 +69,6 @@ redef class String
                var rot = x % 26
                if rot < 0 then rot += 26
                var d = new FlatBuffer.with_capacity(length)
-               var p = 0
                for i in chars do
                        d.add i.rot(rot)
                end
diff --git a/lib/csv.nit b/lib/csv.nit
deleted file mode 100644 (file)
index 82fbb05..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-# 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.
-
-# CSV output facilities
-module csv
-
-# A CSV document representation
-class CSVDocument
-       super Streamable
-
-       var header: Array[String] = new Array[String] is writable
-       var lines: Array[Array[String]] = new Array[Array[String]]
-
-       fun set_header(values: Object...) do
-               header.clear
-               for value in values do
-                       header.add(value.to_s)
-               end
-       end
-
-       fun add_line(values: Object...) do
-               if values.length != header.length then
-                       print "CSV error: header declares {header.length} columns, line contains {values.length} values"
-                       abort
-               end
-               var line = new Array[String]
-               for value in values do
-                       line.add(value.to_s)
-               end
-               lines.add(line)
-       end
-
-       private fun write_line_to(line: Collection[String], stream: OStream)
-       do
-               var i = line.iterator
-               if i.is_ok then
-                       stream.write(i.item)
-                       i.next
-                       while i.is_ok do
-                               stream.write(";")
-                               stream.write(i.item)
-                               i.next
-                       end
-               end
-               stream.write("\n")
-       end
-
-       redef fun write_to(stream)
-       do
-               write_line_to(header, stream)
-               for line in lines do write_line_to(line, stream)
-       end
-
-       # deprecated alias for `write_to_file`
-       fun save(file: String) do write_to_file(file)
-end
diff --git a/lib/csv/csv.nit b/lib/csv/csv.nit
new file mode 100644 (file)
index 0000000..441f95c
--- /dev/null
@@ -0,0 +1,385 @@
+# 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.
+
+# CSV document handling.
+module csv
+
+# Specifies a CSV format.
+class CsvFormat
+       # The character that delimits escaped value.
+       #
+       # The delimiter is escaped by doubling it.
+       var delimiter: Char
+
+       # The character that split each cell in a row.
+       var separator: Char
+
+       # The character that ends a row (end of line).
+       var eol: String
+
+       # Escape sequence for the delimiter.
+       private var escaping = "{delimiter}{delimiter}" is lazy
+
+       # Escape the specified cell.
+       private fun escape_cell(cell: String): Text do
+               var result = new RopeBuffer
+               result.add delimiter
+               result.append cell.replace(delimiter, escaping)
+               result.add delimiter
+               return result
+       end
+
+       # Can the specified value be inserted without any escaping?
+       private fun is_value_clean(value: String): Bool do
+               for c in value.chars do
+                       if c == delimiter then return false
+                       if c == separator then return false
+                       if eol.chars.has(c) then return false
+               end
+               return true
+       end
+end
+
+# A CSV document representation.
+class CsvDocument
+       super Streamable
+
+       # The format to use.
+       #
+       # Defaults to `rfc4180`.
+       var format: CsvFormat = rfc4180 is writable
+
+       # The header.
+       #
+       # Contains the name of all fields in this table.
+       var header: Array[String] = new Array[String] is writable
+
+       # The list of the records.
+       #
+       # All records must have the same length than `header`.
+       var records: Array[Array[String]] = new Array[Array[String]]
+
+       # Replace the header by the specified row.
+       fun set_header(values: Object...) do
+               header.clear
+               for value in values do header.add(value.to_s)
+       end
+
+       # Append the specfied record.
+       fun add_record(values: Object...) do
+               assert values.length == header.length else
+                       sys.stderr.write "CSV error: Header declares {header.length} columns, record contains {values.length} values.\n"
+               end
+               var record = new Array[String]
+               for value in values do record.add(value.to_s)
+               records.add(record)
+       end
+
+       redef fun write_to(stream) do
+               var writer = new CsvWriter.with_format(stream, format)
+               writer.write_sequence(header)
+               for record in records do writer.write_sequence(record)
+       end
+
+       # Deprecated alias for `write_to_file`.
+       fun save(file: String) do write_to_file(file)
+
+       # Load from the specified stream.
+       #
+       # Parameters:
+       #
+       # * `stream`: Input stream.
+       # * `has_header`: Is the first row the header?
+       # * `skip_empty`: Do we skip the empty lines?
+       # For details, see `CsvReader.skip_empty`.
+       fun load_from(stream: IStream, has_header: Bool, skip_empty: Bool) do
+               var reader = new CsvReader.with_format(stream, format)
+               reader.skip_empty = skip_empty
+               if has_header then
+                       if reader.is_ok then
+                               header = reader.item
+                       else
+                               header.clear
+                       end
+               end
+               records.clear
+               for record in reader do records.add(record)
+       end
+
+       # Load from the specified file.
+       #
+       # Parameters:
+       #
+       # * `path`: Path of the file.
+       # * `has_header`: Is the first row the header?
+       # * `skip_empty`: Do we skip the empty lines?
+       fun load(path: String, has_header: Bool, skip_empty: Bool) do
+               var istream = new IFStream.open(path)
+               load_from(istream, has_header, skip_empty)
+               istream.close
+       end
+end
+
+# Appends CSV rows to a file.
+#
+# By default, uses the format recommended by RFC 4180 (see `rfc4180`).
+#
+# Note: If a row contains only an empty cell, its representation is
+# undistinguishable from an empty line. This is because the empty values are
+# always written unescaped in order to avoid them to be interpreted as escaped
+# delimiters by some parsers.
+#
+# ~~~nit
+# var out = new StringOStream
+# var writer = new CsvWriter(out)
+# writer.write_row(1, 2.0, "foo\nbar")
+# writer.write_sequence([""])
+# assert out.to_s == """1,2.0,"foo\nbar"\r\n\r\n"""
+# ~~~
+class CsvWriter
+
+       # The output stream.
+       var ostream: OStream
+
+       # The format to use.
+       #
+       # Defaults to `rfc4180`.
+       var format: CsvFormat = rfc4180
+
+       # Do we escape all cells (except empty ones)?
+       #
+       # If `false` (the default), escape only cells that contain a metacharacter
+       # of the format. In all cases, empty cells are not escaped. This option
+       # permits to choose between the optimization of the performances (when
+       # `true`) and optimization of the size of the output (when `false`).
+       #
+       # Note: Escaping may not be correctly recognized by some parsers.
+       var always_escape = false is writable
+
+       # Create a new writer with the specified format.
+       init with_format(ostream:OStream, format: CsvFormat) do
+               self.ostream = ostream
+               self.format = format
+       end
+
+       # Append the specified sequence as a row.
+       #
+       # The representation of each cell is determined by `to_s`.
+       fun write_sequence(row: SequenceRead[Object]) do
+               if not row.is_empty then
+                       var i = row.iterator
+                       var separator = format.separator.to_s
+                       write_cell i.item.to_s
+                       i.next
+                       for cell in i do
+                               ostream.write separator
+                               write_cell cell.to_s
+                       end
+               end
+               ostream.write format.eol
+       end
+
+       # Append the specified row.
+       #
+       # The representation of each cell is determined by `to_s`.
+       fun write_row(row: Object...) do write_sequence(row)
+
+       # Close the output stream.
+       fun close do ostream.close
+
+       private fun write_cell(cell: String) do
+               if cell.is_empty then return
+               if not always_escape and format.is_value_clean(cell) then
+                       ostream.write cell
+               else
+                       ostream.write format.escape_cell(cell)
+               end
+       end
+end
+
+# Reads rows from a CSV file.
+#
+# By default, uses the format recommended by RFC 4180 (see `rfc4180`).
+#
+# ~~~nit
+# var example = new StringIStream("""
+# foo,bar\r
+# "Hello, word!",1234.5 + 42\r
+# "Something\r
+# ""else""\", baz\r
+# """)
+# var reader = new CsvReader(example)
+# var table = new Array[Array[String]]
+#
+# for row in reader do table.add row
+# assert table == [
+#                      ["foo","bar"],
+#                      ["Hello, word!","1234.5 + 42"],
+#                      ["Something\r\n\"else\""," baz"]
+#              ]
+# ~~~
+class CsvReader
+       super Iterator[Array[String]]
+
+       # The input stream.
+       var istream: IStream
+
+       # The format to use.
+       #
+       # Defaults to `rfc4180`.
+       var format: CsvFormat = rfc4180 is lazy
+
+       # Do we skip the empty lines?
+       #
+       # Note: Even if this attribute is `false`, the presence of an line ending at
+       # end of the last row does not change the number of returned rows.
+       # This is because the line endings are processed as terminators, not as
+       # separators. Therefore, when there is more than one line ending at the end
+       # of the file, the additional lines are interpreted as empty rows that
+       # are skipped only if `skip_empty` is set to `true`.
+       #
+       # `false` by default.
+       var skip_empty: Bool = false is writable
+
+       # The last read row.
+       private var row: nullable Array[String] = null
+
+       # Did we read something?
+       private var started = false
+
+       # Create a new reader with the specified format.
+       init with_format(istream:IStream, format: CsvFormat) do
+               self.istream = istream
+               self.format = format
+       end
+
+       # Read the first row, if needed.
+       fun prepare do
+               if not started then
+                       row = read_row
+                       started = true
+               end
+       end
+
+       redef fun next do
+               prepare
+               assert is_ok else
+                       sys.stderr.write "Already at the end of the stream.\n"
+               end
+               row = read_row
+       end
+
+       # Return the last read row.
+       redef fun item do
+               prepare
+               return row.as(not null)
+       end
+
+       redef fun is_ok do
+               prepare
+               return row != null
+       end
+
+       # Free some internal ressources and set `is_ok` to `false`.
+       #
+       # Do not close the input stream.
+       redef fun finish do row = null
+
+       # Close the input stream.
+       fun close do istream.close
+
+       private fun read_row: nullable Array[String] do
+               if istream.eof then return null
+               var row = new Array[String]
+               var value = new RopeBuffer
+
+               # Number of unescaped characters since the last delimiter or separator.
+               var unescaped = 0
+
+               # Do we read the start of a row?
+               var got_row = false
+
+               # Do we found a delimited string in the current cell?
+               var got_delimiter = false
+
+               loop
+                       var i = istream.read_char
+                       var c: Char
+
+                       if i < 0 then
+                               if got_row then
+                                       row.add value.to_s
+                                       return row
+                               else
+                                       return null
+                               end
+                       end
+                       c = i.ascii
+
+                       if c == format.delimiter then
+                               if got_delimiter and unescaped == 0 then
+                                       # Got an escaped delimiter.
+                                       value.add format.delimiter
+                               end
+                               # Read all bytes until the delimiter.
+                               loop
+                                       i = istream.read_char
+                                       assert not_eof: i >= 0 else
+                                               sys.stderr.write "Unexpected end of file before the end of a delimited value.\n"
+                                       end
+                                       c = i.ascii
+                                       if c == format.delimiter then break
+                                       value.add c
+                               end
+                               unescaped = 0
+                               got_row = true
+                               got_delimiter = true
+                       else if c == format.separator then
+                               # Flush the value to the row.
+                               row.add value.to_s
+                               value.clear
+                               unescaped = 0
+                               got_delimiter = false
+                       else
+                               value.add c
+                               unescaped += 1
+                               if unescaped >= format.eol.length and
+                                               value.has_suffix(format.eol) then
+                                       var value_trimed = value.substring(0,
+                                                       value.length - format.eol.length).to_s
+                                       if skip_empty and row.is_empty and
+                                                       value_trimed.is_empty and
+                                                       not got_delimiter then
+                                               # Skip the empty line.
+                                               value.clear
+                                               unescaped = 0
+                                               got_row = false
+                                       else
+                                               row.add value_trimed
+                                               return row
+                                       end
+                               else
+                                       got_row = true
+                               end
+                       end
+               end
+       end
+end
+
+# The CSV format recommended by [RFC 4180](https://tools.ietf.org/html/rfc4180).
+#
+# * `delimiter`: `'"'`
+# * `separator`: `','`
+# * `eol`: `"\r\n"`
+fun rfc4180: CsvFormat do return once new CsvFormat('"', ',', "\r\n")
diff --git a/lib/csv/test_csv.nit b/lib/csv/test_csv.nit
new file mode 100644 (file)
index 0000000..074fced
--- /dev/null
@@ -0,0 +1,202 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT. This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. You can modify it is you want, provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You are allowed to redistribute it and sell it, alone or is a part of
+# another product.
+
+# Tests for `csv`.
+module test_csv is test_suite
+
+import test_suite
+import csv
+
+class TestCsvWriter
+       super TestSuite
+
+       # The custom CSV format used in the tests.
+       private var custom_format = new CsvFormat('/', ':', "#")
+
+       # Expect to write `row` as `expected_rfc4180` and as `expected_custom`.
+       #
+       # Parameters:
+       #
+       # * `always_escape`: value of the `always_escape` option.
+       # * `row`: row to write.
+       # * `expected_rfc4180`: expected result in RFC 4180.
+       # * `expected_custom`: expected result in the custom CSV format.
+       private fun expect(always_escape: Bool, row: SequenceRead[String],
+                       expected_rfc4180: String,
+                       expected_custom: String) do
+               var out = new StringOStream
+               var writer = new CsvWriter(out)
+
+               writer.always_escape = always_escape
+               writer.write_sequence(row)
+               assert out.to_s == expected_rfc4180 else
+                       sys.stderr.write "\nFormat: RFC 4180\n"
+                       sys.stderr.write "Expecting: \"{expected_rfc4180.escape_to_nit}\"\n"
+                       sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
+               end
+               writer.close
+
+               out = new StringOStream
+               writer = new CsvWriter.with_format(out, custom_format)
+               writer.always_escape = always_escape
+               writer.write_sequence(row)
+               assert out.to_s == expected_custom else
+                       sys.stderr.write "\nFormat: {custom_format.delimiter}"
+                       sys.stderr.write " {custom_format.separator}"
+                       sys.stderr.write " {custom_format.eol.escape_to_nit}\n"
+                       sys.stderr.write "Expecting: \"{expected_custom.escape_to_nit}\"\n"
+                       sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
+               end
+               writer.close
+       end
+
+       fun test_empty do expect(true, new Array[String], "\r\n", "#")
+
+       fun test_one_cell do expect(true, ["foo/\"\r\n,"],
+                       "\"foo/\"\"\r\n,\"\r\n",
+                       "/foo//\"\r\n,/#")
+
+       fun test_optimize_size_escaped do expect(false, ["foo/\"\r\n,"],
+                       "\"foo/\"\"\r\n,\"\r\n",
+                       "/foo//\"\r\n,/#")
+
+       fun test_optimize_size_eol do expect(false, ["foo\r#\n"],
+                       "\"foo\r#\n\"\r\n",
+                       "/foo\r#\n/#")
+
+       fun test_optimize_size_unescaped do expect(false, ["foo"],
+                       "foo\r\n",
+                       "foo#")
+
+       fun test_multiple_cells do expect(true, ["1", "", "/"],
+                       "\"1\",,\"/\"\r\n",
+                       "/1/::////#")
+
+       fun test_multiple_cells_optimize_size do expect(false, ["1", "", "/"],
+                       "1,,/\r\n",
+                       "1::////#")
+end
+
+class TestCsvReader
+       super TestSuite
+
+       # The custom CSV format used in the tests.
+       private var custom_format = new CsvFormat('/', ':', "#")
+
+       # Expect to read `expected`.
+       #
+       # Parameters:
+       #
+       # * `skip_empty`: value of the `skip_empty` option.
+       # * `modal_escaping`: value of the `modal_escaping` option.
+       # * `input_rfc4180`: input in the RFC 4180 format.
+       # * `input_custom`: input in the custom CSV format.
+       # * `expected`: expected resulting table.
+       private fun expect(skip_empty: Bool,
+                       input_rfc4180: String,
+                       input_custom: String,
+                       expected: SequenceRead[SequenceRead[String]]) do
+               var istream: IStream
+               var reader: CsvReader
+               var i = 0
+
+               istream = new StringIStream(input_rfc4180)
+               reader = new CsvReader(istream)
+               reader.skip_empty = skip_empty
+               assert_table_equals("RFC 4180", reader, expected.iterator)
+
+               istream = new StringIStream(input_custom)
+               reader = new CsvReader.with_format(istream, custom_format)
+               reader.skip_empty = skip_empty
+               assert_table_equals("{custom_format.delimiter} " +
+                               "{custom_format.separator} " +
+                               "{custom_format.eol.escape_to_nit}", reader, expected.iterator)
+       end
+
+       # Check if tables are equal.
+       private fun assert_table_equals(format: String,
+                       actual: Iterator[SequenceRead[String]],
+                       expected: Iterator[SequenceRead[String]]) do
+               var i = 0
+
+               for actual_row in actual do
+                       assert expected.is_ok else fail(format,"Too many rows.")
+                       var expected_row = expected.item
+                       assert_row_equals(format, i, actual_row, expected_row)
+                       expected.next
+                       i += 1
+               end
+               assert not expected.is_ok else fail(format, "Not enough rows.")
+               expected.finish
+       end
+
+       # Check if rows are equal.
+       private fun assert_row_equals(format: String,
+                       row_index: Int,
+                       actual: SequenceRead[String],
+                       expected: SequenceRead[String]) do
+               assert actual == expected else
+                       fail(format, """
+At row {{{row_index}}}.
+Expecting: {{{expected.join("|")}}}
+Got: {{{actual.join("|")}}}""")
+               end
+       end
+
+       # Output an error message with an indication of the format used.
+       private fun fail(format: Text, message: Text) do
+               sys.stderr.write "\nFormat: {format}\n"
+               sys.stderr.write message
+               sys.stderr.write "\n"
+       end
+
+       fun test_empty do expect(false, "", "", new Array[Array[String]])
+
+       fun test_empty_eol do expect(false, "\r\n", "#", [[""]])
+
+       fun test_empty_skip do expect(true, "", "", new Array[Array[String]])
+
+       fun test_empty_skip1 do expect(true, "\r\n", "#", new Array[Array[String]])
+
+       fun test_empty_skip2 do expect(true, "\r\n\r\n", "##", new Array[Array[String]])
+
+       fun test_escaped do expect(false, "\"foo/\"\"\r\n,\"\r\n",
+                       "/foo//\"\r\n,/#", [["foo/\"\r\n,"]])
+
+       fun test_unescaped do expect(false, "foo bar\r\n",
+                       "foo bar#", [["foo bar"]])
+
+       fun test_escaped_no_eol do expect(false, "\"foo/\"\"\r\n,\"",
+                       "/foo//\"\r\n,/", [["foo/\"\r\n,"]])
+
+       fun test_unescaped_no_eol do expect(false, "foo bar",
+                       "foo bar", [["foo bar"]])
+
+       fun test_multiple_cells do expect(false, "\"1\",,\"/\"\r\n",
+                       "/1/::////#", [["1", "", "/"]])
+
+       fun test_multiple_cells_unescaped do expect(false, "1,,/\r\n",
+                       "1::////#", [["1", "", "/"]])
+
+       fun test_modal_escaping do expect(false, """a"b""/c","d"e""",
+                       """/ab"///c:d/e/""", [["""ab"/c""", "de"]])
+
+       fun test_skip_start do expect(true, "\r\n1,,/\r\n",
+                       "#1::////#", [["1", "", "/"]])
+
+       fun test_dont_skip_empty_delimited do expect(true, "\"\"\r\n",
+                       "//#", [[""]])
+
+       fun test_dont_skip_multiple_empty_cells do expect(true, ",\r\n",
+                       ":#", [["",""]])
+
+       fun test_mutiple_rows do expect(false, "\"a\r\nb#\",c\r\nd,\r\n,e\r\n",
+                       "/a\r\nb#/:c#d:#:e#", [["a\r\nb#", "c"], ["d", ""], ["", "e"]])
+end
index 88e7008..9061e53 100644 (file)
@@ -1,6 +1,7 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
+# Copyright 2014 Alexandre Terrasa <alexandre@moz-code.org>
 #
 # This file is free software, which comes along with NIT.  This software is
 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
@@ -10,6 +11,7 @@
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
+# A `Set` that contains only integers.
 class DummyArray
        super Set[Int]
        private var capacity: Int
@@ -75,14 +77,15 @@ class DummyArray
                return _values[pos]
        end
 
-       init(capacity: Int)
-       do
+       # initialize a new DummyArray with `capacity`.
+       init(capacity: Int) is old_style_init do
                _capacity = capacity
                _keys = new NativeArray[Int](capacity)
                _values = new NativeArray[Int](capacity)
        end
 end
 
+# An iterator over a `DummyArray`.
 class DummyIterator
        super Iterator[Int]
        private var array: DummyArray
@@ -101,8 +104,8 @@ class DummyIterator
 
        redef fun next do _pos = _pos + 1 end
 
-       init(array: DummyArray)
-       do
+       # Initialize an iterator for `array`.
+       init(array: DummyArray) is old_style_init do
                _pos = 0
                _array = array
        end
index e349a0d..5c50387 100644 (file)
@@ -167,13 +167,13 @@ extern class EGLDisplay `{ EGLDisplay `}
                return NativeString_to_s((char *)eglQueryString(recv, name));
        `}
 
-       fun vendor: String do return query_string("3053".to_hex)
+       fun vendor: String do return query_string(0x3053)
 
-       fun version: String do return query_string("3054".to_hex)
+       fun version: String do return query_string(0x3054)
 
-       fun extensions: Array[String] do return query_string("3055".to_hex).split_with(" ")
+       fun extensions: Array[String] do return query_string(0x3055).split_with(" ")
 
-       fun client_apis: Array[String] do return query_string("308D".to_hex).split_with(" ")
+       fun client_apis: Array[String] do return query_string(0x308D).split_with(" ")
 
        fun swap_buffers(surface: EGLSurface) `{ eglSwapBuffers(recv, surface); `}
 end
@@ -208,23 +208,23 @@ class EGLConfigAttribs
        var display: EGLDisplay
        var config: EGLConfig
 
-       fun buffer_size: Int do return display.config_attrib(config, "3020".to_hex)
-       fun alpha_size: Int do return display.config_attrib(config, "3021".to_hex)
-       fun blue_size: Int do return display.config_attrib(config, "3022".to_hex)
-       fun green_size: Int do return display.config_attrib(config, "3023".to_hex)
-       fun red_size: Int do return display.config_attrib(config, "3024".to_hex)
-       fun depth_size: Int do return display.config_attrib(config, "3025".to_hex)
-       fun stencil_size: Int do return display.config_attrib(config, "3026".to_hex)
+       fun buffer_size: Int do return display.config_attrib(config, 0x3020)
+       fun alpha_size: Int do return display.config_attrib(config, 0x3021)
+       fun blue_size: Int do return display.config_attrib(config, 0x3022)
+       fun green_size: Int do return display.config_attrib(config, 0x3023)
+       fun red_size: Int do return display.config_attrib(config, 0x3024)
+       fun depth_size: Int do return display.config_attrib(config, 0x3025)
+       fun stencil_size: Int do return display.config_attrib(config, 0x3026)
 
-       fun native_visual_id: Int do return display.config_attrib(config, "302E".to_hex)
-       fun native_visual_type: Int do return display.config_attrib(config, "302F".to_hex)
+       fun native_visual_id: Int do return display.config_attrib(config, 0x302E)
+       fun native_visual_type: Int do return display.config_attrib(config, 0x302F)
 
        fun caveat: EGLConfigCaveat do
-               return new EGLConfigCaveat.from_i(display.config_attrib(config, "3027".to_hex))
+               return new EGLConfigCaveat.from_i(display.config_attrib(config, 0x3027))
        end
 
        fun conformant: EGLConformant do
-               return new EGLConformant.from_i(display.config_attrib(config, "3042".to_hex))
+               return new EGLConformant.from_i(display.config_attrib(config, 0x3042))
        end
 end
 
@@ -290,21 +290,21 @@ class EGLSurfaceAttribs
        var display: EGLDisplay
        var surface: EGLSurface
 
-       fun height: Int do return display.query_surface(surface, "3056".to_hex)
-       fun width: Int do return display.query_surface(surface, "3057".to_hex)
-       fun largest_pbuffer: Int do return display.query_surface(surface, "3058".to_hex)
-       fun texture_format: Int do return display.query_surface(surface, "3080".to_hex)
-       fun texture_target: Int do return display.query_surface(surface, "3081".to_hex)
-       fun mipmap_texture: Int do return display.query_surface(surface, "3082".to_hex)
-       fun mipmap_level: Int do return display.query_surface(surface, "3083".to_hex)
-       fun render_buffer: Int do return display.query_surface(surface, "3086".to_hex)
-       fun vg_colorspace: Int do return display.query_surface(surface, "3087".to_hex)
-       fun vg_alpha_format: Int do return display.query_surface(surface, "3088".to_hex)
-       fun horizontal_resolution: Int do return display.query_surface(surface, "3090".to_hex)
-       fun vertical_resolution: Int do return display.query_surface(surface, "3091".to_hex)
-       fun pixel_aspect_ratio: Int do return display.query_surface(surface, "3092".to_hex)
-       fun swap_behavior: Int do return display.query_surface(surface, "3093".to_hex)
-       fun multisample_resolve: Int do return display.query_surface(surface, "3099".to_hex)
+       fun height: Int do return display.query_surface(surface, 0x3056)
+       fun width: Int do return display.query_surface(surface, 0x3057)
+       fun largest_pbuffer: Int do return display.query_surface(surface, 0x3058)
+       fun texture_format: Int do return display.query_surface(surface, 0x3080)
+       fun texture_target: Int do return display.query_surface(surface, 0x3081)
+       fun mipmap_texture: Int do return display.query_surface(surface, 0x3082)
+       fun mipmap_level: Int do return display.query_surface(surface, 0x3083)
+       fun render_buffer: Int do return display.query_surface(surface, 0x3086)
+       fun vg_colorspace: Int do return display.query_surface(surface, 0x3087)
+       fun vg_alpha_format: Int do return display.query_surface(surface, 0x3088)
+       fun horizontal_resolution: Int do return display.query_surface(surface, 0x3090)
+       fun vertical_resolution: Int do return display.query_surface(surface, 0x3091)
+       fun pixel_aspect_ratio: Int do return display.query_surface(surface, 0x3092)
+       fun swap_behavior: Int do return display.query_surface(surface, 0x3093)
+       fun multisample_resolve: Int do return display.query_surface(surface, 0x3099)
 end
 
 extern class EGLError `{ EGLint `}
@@ -405,26 +405,26 @@ class EGLConfigChooser
        end
 
        fun close do
-               insert_attrib_key "3038".to_hex
+               insert_attrib_key 0x3038
                closed = true
        end
 
-       fun surface_type=(flag: Int) do insert_attrib_with_val("3033".to_hex, flag)
+       fun surface_type=(flag: Int) do insert_attrib_with_val(0x3033, flag)
        fun surface_type_egl do surface_type = 4
 
-       fun blue_size=(size: Int) do insert_attrib_with_val("3022".to_hex, size)
-       fun green_size=(size: Int) do insert_attrib_with_val("3023".to_hex, size)
-       fun red_size=(size: Int) do insert_attrib_with_val("3024".to_hex, size)
+       fun blue_size=(size: Int) do insert_attrib_with_val(0x3022, size)
+       fun green_size=(size: Int) do insert_attrib_with_val(0x3023, size)
+       fun red_size=(size: Int) do insert_attrib_with_val(0x3024, size)
 
-       fun buffer_size=(size: Int) do insert_attrib_with_val("3020".to_hex, size)
-       fun alpha_size=(size: Int) do insert_attrib_with_val("3021".to_hex, size)
-       fun depth_size=(size: Int) do insert_attrib_with_val("3025".to_hex, size)
-       fun stencil_size=(size: Int) do insert_attrib_with_val("3026".to_hex, size)
-       fun sample_buffers=(size: Int) do insert_attrib_with_val("3031".to_hex, size)
+       fun buffer_size=(size: Int) do insert_attrib_with_val(0x3020, size)
+       fun alpha_size=(size: Int) do insert_attrib_with_val(0x3021, size)
+       fun depth_size=(size: Int) do insert_attrib_with_val(0x3025, size)
+       fun stencil_size=(size: Int) do insert_attrib_with_val(0x3026, size)
+       fun sample_buffers=(size: Int) do insert_attrib_with_val(0x3031, size)
 
-       fun caveat=(caveat: EGLConfigCaveat) do insert_attrib_with_val("3050".to_hex, caveat.to_i)
+       fun caveat=(caveat: EGLConfigCaveat) do insert_attrib_with_val(0x3050, caveat.to_i)
 
-       fun conformant=(conformant: EGLConformant) do insert_attrib_with_val("3042".to_hex, conformant.to_i)
+       fun conformant=(conformant: EGLConformant) do insert_attrib_with_val(0x3042, conformant.to_i)
 
        fun choose(display: EGLDisplay): nullable Array[EGLConfig]
        do
index 3fd2fff..2277a51 100644 (file)
@@ -16,7 +16,7 @@
 
 # Platform for the _emscripten_ framework
 #
-# Importing this module from your project will tell _nitg_ to compile
+# Importing this module from your project will tell `nitg` to compile
 # to JavaScript for the _emscripten_ framework.
 module emscripten is platform
 
diff --git a/lib/github/github.nit b/lib/github/github.nit
new file mode 100644 (file)
index 0000000..759b921
--- /dev/null
@@ -0,0 +1,18 @@
+# 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.
+
+# Github API related features.
+module github
+
+import github_curl
similarity index 93%
rename from lib/github_api.nit
rename to lib/github/github_curl.nit
index 39fca84..5e50474 100644 (file)
@@ -14,7 +14,7 @@
 
 # Curl extention to access the Github API
 # See https://developer.github.com/v3/ for details
-module github_api
+module github_curl
 
 import curl
 import json::static
@@ -24,7 +24,7 @@ class GithubCurl
        super Curl
 
        # Headers to use on all requests
-       var header: HeaderMap
+       var header: HeaderMap is noinit
 
        # OAuth token
        var auth: String
@@ -33,12 +33,7 @@ class GithubCurl
        # Eg. "Awesome-Octocat-App"
        var user_agent: String
 
-       init(auth: String, user_agent: String)
-       do
-               super
-               self.auth = auth
-               self.user_agent = user_agent
-
+       init do
                header = new HeaderMap
                header["Authorization"] = "token {auth}"
        end
@@ -83,4 +78,3 @@ do
        p.close
        return token.trim
 end
-
diff --git a/lib/github/test_github_curl.nit b/lib/github/test_github_curl.nit
new file mode 100644 (file)
index 0000000..e15f684
--- /dev/null
@@ -0,0 +1,48 @@
+# 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.
+
+module test_github_curl is test_suite
+
+import github::github_curl
+import test_suite
+
+class TestGithubCurl
+       super TestSuite
+
+       var auth: String = get_github_oauth
+       var user_agent: String = "nit"
+       var testee: GithubCurl is noinit
+
+       redef fun before_test do
+               testee = new GithubCurl(auth, user_agent)
+       end
+
+       fun test_get_repo do
+               var uri = "https://api.github.com/repos/privat/nit"
+               var res = testee.get_and_check(uri)
+
+               assert res isa JsonObject
+               assert res["name"] == "nit"
+               assert res["owner"] isa JsonObject
+       end
+
+       fun test_get_user do
+               var uri = "https://api.github.com/users/Morriar"
+               var res = testee.get_and_check(uri)
+
+               assert res isa JsonObject
+               assert res["login"] == "Morriar"
+               assert res["html_url"] == "https://github.com/Morriar"
+       end
+end
index 1d5904d..8d5ec6a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# OpenGL graphics rendering library for embedded systems, version 2.0.
+# OpenGL graphics rendering library for embedded systems, version 2.0
+#
+# This is a low-level wrapper, it can be useful for developers already familiar
+# with the C API of OpenGL. Most developers will prefer to use higher level
+# wrappers such as `mnit` and `gammit`.
 #
 # Defines the annotations `glsl_vertex_shader` and `glsl_fragment_shader`
 # applicable on string literals to check shader code using `glslangValidator`.
@@ -31,45 +35,81 @@ module glesv2 is
        new_annotation glsl_fragment_shader
 end
 
+
 in "C Header" `{
        #include <GLES2/gl2.h>
 `}
 
-# Opengl ES program to which we attach shaders
+# OpenGL ES program to which we attach shaders
 extern class GLProgram `{GLuint`}
+       # Create a new program
+       #
+       # The newly created instance should be checked using `is_ok`.
        new `{ return glCreateProgram(); `}
 
+       # Is this a valid program?
        fun is_ok: Bool `{ return glIsProgram(recv); `}
 
+       # Attach a `shader` to this program
        fun attach_shader(shader: GLShader) `{ glAttachShader(recv, shader); `}
 
+       # Set the location for the attribute by `name`
        fun bind_attrib_location(index: Int, name: String) import String.to_cstring `{
                GLchar *c_name = String_to_cstring(name);
                glBindAttribLocation(recv, index, c_name);
        `}
 
+       # Get the location of the attribute by `name`
+       #
+       # Returns `-1` if there is no active attribute named `name`.
        fun attrib_location(name: String): Int import String.to_cstring `{
                GLchar *c_name = String_to_cstring(name);
                return glGetAttribLocation(recv, c_name);
        `}
 
+       # Get the location of the uniform by `name`
+       #
+       # Returns `-1` if there is no active uniform named `name`.
+       fun uniform_location(name: String): Int import String.to_cstring `{
+               GLchar *c_name = String_to_cstring(name);
+               return glGetUniformLocation(recv, c_name);
+       `}
+
+       # Query information on this program
        fun query(pname: Int): Int `{
                int val;
                glGetProgramiv(recv, pname, &val);
                return val;
        `}
 
+       # Try to link this program
+       #
+       # Check result using `in_linked` and `info_log`.
        fun link `{ glLinkProgram(recv); `}
+
+       # Is this program linked?
        fun is_linked: Bool do return query(0x8B82) != 0
 
+       # Use this program for the following operations
        fun use `{ glUseProgram(recv); `}
 
+       # Delete this program
        fun delete `{ glDeleteProgram(recv); `}
+
+       # Has this program been deleted?
        fun is_deleted: Bool do return query(0x8B80) != 0
 
+       # Validate whether this program can be executed in the current OpenGL state
+       #
+       # Check results using `is_validated` and `info_log`.
        fun validate `{ glValidateProgram(recv); `}
+
+       # Boolean result of `validate`, must be called after `validate`
        fun is_validated: Bool do return query(0x8B83) != 0
 
+       # Retrieve the information log of this program
+       #
+       # Useful with `link` and `validate`
        fun info_log: String import NativeString.to_s `{
                int size;
                glGetProgramiv(recv, GL_INFO_LOG_LENGTH, &size);
@@ -77,37 +117,137 @@ extern class GLProgram `{GLuint`}
                glGetProgramInfoLog(recv, size, NULL, msg);
                return NativeString_to_s(msg);
        `}
+
+       # Number of active uniform in this program
+       #
+       # This should be the number of uniforms declared in all shader, except
+       # unused uniforms which may have been optimized out.
+       fun n_active_uniforms: Int do return query(0x8B86)
+
+       # Length of the longest uniform name in this program, including `\n`
+       fun active_uniform_max_length: Int do return query(0x8B87)
+
+       # Number of active attributes in this program
+       #
+       # This should be the number of uniforms declared in all shader, except
+       # unused uniforms which may have been optimized out.
+       fun n_active_attributes: Int do return query(0x8B89)
+
+       # Length of the longest uniform name in this program, including `\n`
+       fun active_attribute_max_length: Int do return query(0x8B8A)
+
+       # Number of shaders attached to this program
+       fun n_attached_shaders: Int do return query(0x8B85)
+
+       # Name of the active attribute at `index`
+       fun active_attrib_name(index: Int): String
+       do
+               var max_size = active_attribute_max_length
+               return active_attrib_name_native(index, max_size).to_s
+       end
+       private fun active_attrib_name_native(index, max_size: Int): NativeString `{
+               char *name = malloc(max_size);
+               glGetActiveAttrib(recv, index, max_size, NULL, NULL, NULL, name);
+               return name;
+       `}
+
+       # Size of the active attribute at `index`
+       fun active_attrib_size(index: Int): Int `{
+               int size;
+               glGetActiveAttrib(recv, index, 0, NULL, NULL, &size, NULL);
+               return size;
+       `}
+
+       # Type of the active attribute at `index`
+       #
+       # May only be float related data types (single float, vectors and matrix).
+       fun active_attrib_type(index: Int): GLFloatDataType `{
+               GLenum type;
+               glGetActiveAttrib(recv, index, 0, NULL, &type, NULL, NULL);
+               return type;
+       `}
+
+       # Name of the active uniform at `index`
+       fun active_uniform_name(index: Int): String
+       do
+               var max_size = active_attribute_max_length
+               return active_uniform_name_native(index, max_size).to_s
+       end
+       private fun active_uniform_name_native(index, max_size: Int): NativeString `{
+               char *name = malloc(max_size);
+               glGetActiveUniform(recv, index, max_size, NULL, NULL, NULL, name);
+               return name;
+       `}
+
+       # Size of the active uniform at `index`
+       fun active_uniform_size(index: Int): Int `{
+               int size;
+               glGetActiveUniform(recv, index, 0, NULL, NULL, &size, NULL);
+               return size;
+       `}
+
+       # Type of the active uniform at `index`
+       #
+       # May be any data type supported by OpenGL ES 2.0 shaders.
+       fun active_uniform_type(index: Int): GLDataType `{
+               GLenum type;
+               glGetActiveUniform(recv, index, 0, NULL, &type, NULL, NULL);
+               return type;
+       `}
 end
 
 # Abstract OpenGL ES shader object, implemented by `GLFragmentShader` and `GLVertexShader`
 extern class GLShader `{GLuint`}
+       # Set the source of the shader
        fun source=(code: String) import String.to_cstring, String.length `{
                GLchar *c_code = String_to_cstring(code);
                glShaderSource(recv, 1, (const GLchar * const*)&c_code, NULL);
        `}
-       fun source: nullable String import NativeString.to_s `{
-               int size;
-               glGetShaderiv(recv, GL_SHADER_SOURCE_LENGTH, &size);
-               if (size == 0) return NULL;
+
+       # Source of the shader, if available
+       #
+       # Returns `null` if the source is not available, usually when the shader
+       # was created from a binary file.
+       fun source: nullable String
+       do
+               var size = query(0x8B88)
+               if size == 0 then return null
+               return source_native(size).to_s
+       end
+
+       private fun source_native(size: Int): NativeString `{
                GLchar *code = malloc(size);
                glGetShaderSource(recv, size, NULL, code);
-               return NativeString_to_s(code);
+               return code;
        `}
 
+       # Query information on this shader
        protected fun query(pname: Int): Int `{
                int val;
                glGetShaderiv(recv, pname, &val);
                return val;
        `}
 
+       # Try to compile `source` into a binary GPU program
+       #
+       # Check the result using `is_compiled` and `info_log`
        fun compile `{ glCompileShader(recv); `}
+
+       # Has this shader been compiled?
        fun is_compiled: Bool do return query(0x8B81) != 0
 
+       # Delete this shader
        fun delete `{ glDeleteShader(recv); `}
+
+       # Has this shader been deleted?
        fun is_deleted: Bool do return query(0x8B80) != 0
 
+       # Is this a valid shader?
        fun is_ok: Bool `{ return glIsShader(recv); `}
 
+       # Retrieve the information log of this shader
+       #
+       # Useful with `link` and `validate`
        fun info_log: String import NativeString.to_s `{
                int size;
                glGetShaderiv(recv, GL_INFO_LOG_LENGTH, &size);
@@ -115,25 +255,35 @@ extern class GLShader `{GLuint`}
                glGetShaderInfoLog(recv, size, NULL, msg);
                return NativeString_to_s(msg);
        `}
-
 end
 
+# An OpenGL ES 2.0 fragment shader
 extern class GLFragmentShader
        super GLShader
 
+       # Create a new fragment shader
+       #
+       # The newly created instance should be checked using `is_ok`.
        new `{ return glCreateShader(GL_FRAGMENT_SHADER); `}
 end
 
+# An OpenGL ES 2.0 vertex shader
 extern class GLVertexShader
        super GLShader
 
+       # Create a new fragment shader
+       #
+       # The newly created instance should be checked using `is_ok`.
        new `{ return glCreateShader(GL_VERTEX_SHADER); `}
 end
 
 # An array of `Float` associated to a program variable
 class VertexArray
        var index: Int
+
+       # Number of data per vertex
        var count: Int
+
        protected var glfloat_array: GLfloatArray
 
        init(index, count: Int, array: Array[Float])
@@ -168,6 +318,7 @@ extern class GLfloatArray `{GLfloat *`}
        `}
 end
 
+# An OpenGL ES 2.0 error code
 extern class GLError `{ GLenum `}
        fun is_ok: Bool do return is_no_error
        fun is_no_error: Bool `{ return recv == GL_NO_ERROR; `}
@@ -189,7 +340,10 @@ extern class GLError `{ GLenum `}
        end
 end
 
+# Clear the color buffer with `r`, `g`, `b`, `a`
 protected fun gl_clear_color(r, g, b, a: Float) `{ glClearColor(r, g, b, a); `}
+
+# Set the viewport
 protected fun gl_viewport(x, y, width, height: Int) `{ glViewport(x, y, width, height); `}
 
 # Direct call to `glClear`, call with a combinaison of `gl_clear_color_buffer`,
@@ -214,20 +368,61 @@ do
        end
 end
 
+# Query the boolean value at `key`
 private fun gl_get_bool(key: Int): Bool `{
        GLboolean val;
        glGetBooleanv(key, &val);
        return val == GL_TRUE;
 `}
+
+# Query the floating point value at `key`
 private fun gl_get_float(key: Int): Float `{
        GLfloat val;
        glGetFloatv(key, &val);
        return val;
 `}
+
+# Query the integer value at `key`
 private fun gl_get_int(key: Int): Int `{
        GLint val;
        glGetIntegerv(key, &val);
        return val;
 `}
 
+# Does this driver support shader compilation?
+#
+# Should always return `true` in OpenGL ES 2.0 and 3.0.
 fun gl_shader_compiler: Bool do return gl_get_bool(0x8DFA)
+
+# Float related data types of OpenGL ES 2.0 shaders
+#
+# Only data types supported by shader attributes, as seen with
+# `GLProgram::active_attrib_type`.
+extern class GLFloatDataType `{ GLenum `}
+       fun is_float: Bool `{ return recv == GL_FLOAT; `}
+       fun is_float_vec2: Bool `{ return recv == GL_FLOAT_VEC2; `}
+       fun is_float_vec3: Bool `{ return recv == GL_FLOAT_VEC3; `}
+       fun is_float_vec4: Bool `{ return recv == GL_FLOAT_VEC4; `}
+       fun is_float_mat2: Bool `{ return recv == GL_FLOAT_MAT2; `}
+       fun is_float_mat3: Bool `{ return recv == GL_FLOAT_MAT3; `}
+       fun is_float_mat4: Bool `{ return recv == GL_FLOAT_MAT4; `}
+end
+
+# All data types of OpenGL ES 2.0 shaders
+#
+# These types can be used by shader uniforms, as seen with
+# `GLProgram::active_uniform_type`.
+extern class GLDataType
+       super GLFloatDataType
+
+       fun is_int: Bool `{ return recv == GL_INT; `}
+       fun is_int_vec2: Bool `{ return recv == GL_INT_VEC2; `}
+       fun is_int_vec3: Bool `{ return recv == GL_INT_VEC3; `}
+       fun is_int_vec4: Bool `{ return recv == GL_INT_VEC4; `}
+       fun is_bool: Bool `{ return recv == GL_BOOL; `}
+       fun is_bool_vec2: Bool `{ return recv == GL_BOOL_VEC2; `}
+       fun is_bool_vec3: Bool `{ return recv == GL_BOOL_VEC3; `}
+       fun is_bool_vec4: Bool `{ return recv == GL_BOOL_VEC4; `}
+       fun is_sampler_2d: Bool `{ return recv == GL_SAMPLER_2D; `}
+       fun is_sampler_cube: Bool `{ return recv == GL_SAMPLER_CUBE; `}
+end
index f7ae180..83ff255 100644 (file)
@@ -325,6 +325,6 @@ end
 private class HTMLRaw
        super HTMLTag
 
-       private var content: String
+       var content: String
        redef fun render_in(res) do res.add content
 end
index 257c662..5453901 100644 (file)
@@ -12,7 +12,7 @@
 module io::push_back_reader
 
 # Input stream that permits to push bytes back to the stream.
-interface PushBackReader
+class PushBackReader
        super IStream
 
        # Push the specified byte back to the stream.
index f090b24..c41426d 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Handles serialization and deserialization of objects to/from Json.
 module json_serialization
 
 import serialization
 import json::static
 
+# Serializer of Nit objects to Json string.
 class JsonSerializer
        super Serializer
 
        # Target writing stream
        var stream: OStream
 
-       init(stream: OStream) do self.stream = stream
-
        redef fun serialize(object)
        do
                if object == null then
@@ -52,9 +52,10 @@ class JsonSerializer
                end
        end
 
-       # Map of references to already serialized objects
+       # Map of references to already serialized objects.
        var refs_map = new HashMap[Serializable,Int]
 
+       # Get the internal serialized reference for this `object`.
        private fun ref_id_for(object: Serializable): Int
        do
                if refs_map.keys.has(object) then
@@ -67,18 +68,28 @@ class JsonSerializer
        end
 end
 
-# Deserializer from a Json string
+# Deserializer from a Json string.
 class JsonDeserializer
        super Deserializer
 
-       var root: nullable Jsonable
+       # Json text to deserialize from.
+       private var text: Text
+
+       # Root json object parsed from input text.
+       var root: nullable Jsonable is noinit
+
+       # Depth-first path in the serialized object tree.
        var path = new Array[JsonObject]
+
+       # Map of refenrences to already deserialized objects.
        var id_to_object = new HashMap[Int, Object]
 
+       # Last encountered object reference id.
+       #
+       # See `id_to_object`.
        var just_opened_id: nullable Int = null
 
-       init(text: Text)
-       do
+       init do
                var root = text.parse_json
                if root isa JsonObject then path.add(root)
                self.root = root
@@ -246,6 +257,7 @@ redef class Array[E]
                end
        end
 
+       # Instanciate a new `Array` from its serialized representation.
        init from_deserializer(v: Deserializer)
        do
                if v isa JsonDeserializer then
index b7cedea..30a6b40 100644 (file)
@@ -34,7 +34,7 @@ private class LinuxStore
        # Sqlite3 table used
        fun db_table: String do return "data_store"
 
-       private var db_cache: nullable Sqlite3DB = null
+       var db_cache: nullable Sqlite3DB = null
 
        # Database to use to implement the `DataStore`
        fun db: nullable Sqlite3DB
index 9a56b95..97c38f2 100644 (file)
@@ -21,12 +21,20 @@ import socket
 
 # Connection to a MPD server
 class MPDConnection
+
+       # Socket connection to server.
        var socket: nullable Socket = null
 
+       # Server hostname.
        var host: String
+
+       # Server port.
        var port: Int
+
+       # Server password.
        var password: nullable String
 
+       # Last occured error.
        var error: nullable String = null
 
        # Connect to the MPD server
@@ -130,6 +138,7 @@ class MPDConnection
                error = "Cannot get volume"
        end
 
+       # Set MPD server volume.
        fun volume=(vol: Int) do write_and_check("setvol {vol}\n")
 
        # Pause music playing on the MPD server
@@ -179,6 +188,7 @@ class MPDConnection
                end
        end
 
+       # Load playlist named `name`.
        fun load_playlist(name: String)
        do
                write_and_check "load \"{name}\""
@@ -187,23 +197,44 @@ end
 
 # MPD song info
 class SongInfo
+       # Song album.
        var album: String
+
+       # Song artist.
        var artist: String
+
+       # Song title.
        var title: String
+
+       # Song total duration.
        var time: Int
 end
 
 # MPD server status
 class ServerStatus
+
+       # MPD server volume.
        var volume: nullable Int
 
+       # Playback state (play/stop/pause).
        var state: String
+
+       # Is MPD server playing?
        fun playing: Bool do return state == "play"
+
+       # Is MPD server stopped?
        fun stopped: Bool do return state == "stop"
 
+       # Time elapsed within the current song.
        var elapsed: nullable Int
+
+       # TODO comment
        var time_at: nullable Int
+
+       # Total time of the current song.
        var time_total: nullable Int
+
+       # Get the cursor position on the song duration.
        fun time_ratio: nullable Float do
                if time_at == null or time_total == null then return null
                return time_at.to_f / time_total.to_f
index 1d8c406..01137f7 100644 (file)
@@ -117,7 +117,7 @@ class MPI
                # Deserialize message
                var deserializer = new JsonDeserializer(buffer)
                var deserialized = deserializer.deserialize
-               
+
                if deserialized == null then print "|{buffer}|{buffer.chars.join("-")}| {buffer.length}"
 
                return deserialized
@@ -191,19 +191,46 @@ end
 
 # An MPI data type
 extern class DataType `{ MPI_Datatype `}
+       # Get a MPI char.
        new char `{ return MPI_CHAR; `}
+
+       # Get a MPI short.
        new short `{ return MPI_SHORT; `}
+
+       # Get a MPI int.
        new int `{ return MPI_INT; `}
+
+       # Get a MPI long.
        new long `{ return MPI_LONG; `}
+
+       # Get a MPI long long.
        new long_long `{ return MPI_LONG_LONG; `}
+
+       # Get a MPI unsigned char.
        new unsigned_char `{ return MPI_UNSIGNED_CHAR; `}
+
+       # Get a MPI unsigned short.
        new unsigned_short `{ return MPI_UNSIGNED_SHORT; `}
+
+       # Get a MPI unsigned int.
        new unsigned `{ return MPI_UNSIGNED; `}
+
+       # Get a MPI unsigned long.
        new unsigned_long `{ return MPI_UNSIGNED_LONG; `}
+
+       # Get a MPI unsigned long long.
        new unsigned_long_long `{ return MPI_UNSIGNED_LONG_LONG; `}
+
+       # Get a MPI float.
        new float `{ return MPI_FLOAT; `}
+
+       # Get a MPI double.
        new double `{ return MPI_DOUBLE; `}
+
+       # Get a MPI long double.
        new long_double `{ return MPI_LONG_DOUBLE; `}
+
+       # Get a MPI byte.
        new byte `{ return MPI_BYTE; `}
 end
 
@@ -235,21 +262,60 @@ end
 
 # An MPI operation
 #
-# Used with the `reduce` method
+# Used with the `reduce` method.
+#
+# See <http://www.mpi-forum.org/docs/mpi-1.1/mpi-11-html/node78.html>
 extern class Op `{ MPI_Op `}
+       # Get a MPI null operation.
        new op_null `{ return MPI_OP_NULL; `}
+
+       # Get a MPI maximum operation.
        new max `{ return MPI_MAX; `}
+
+       # Get a MPI minimum operation.
        new min `{ return MPI_MIN; `}
+
+       # Get a MPI sum operation.
        new sum `{ return MPI_SUM; `}
+
+       # Get a MPI product operation.
        new prod `{ return MPI_PROD; `}
+
+       # Get a MPI logical and operation.
        new land `{ return MPI_LAND; `}
+
+       # Get a MPI bit-wise and operation.
        new band `{ return MPI_BAND; `}
+
+       # Get a MPI logical or operation.
        new lor `{ return MPI_LOR; `}
+
+       # Get a MPI bit-wise or operation.
        new bor `{ return MPI_BOR; `}
+
+       # Get a MPI logical xor operation.
        new lxor `{ return MPI_LXOR; `}
+
+       # Get a MPI bit-wise xor operation.
        new bxor `{ return MPI_BXOR; `}
+
+       # Get a MPI minloc operation.
+       #
+       # Used to compute a global minimum and also an index attached
+       # to the minimum value.
+       #
+       # See <http://www.mpi-forum.org/docs/mpi-1.1/mpi-11-html/node79.html#Node79>
        new minloc `{ return MPI_MINLOC; `}
+
+       # Get a MPI maxloc operation.
+       #
+       # Used to compute a global maximum and also an index attached
+       # to the maximum value.
+       #
+       # See <http://www.mpi-forum.org/docs/mpi-1.1/mpi-11-html/node79.html#Node79>
        new maxloc `{ return MPI_MAXLOC; `}
+
+       # Get a MPI replace operation.
        new replace `{ return MPI_REPLACE; `}
 end
 
index f9e952d..c64b5cb 100644 (file)
@@ -10,6 +10,7 @@ Want to see `nitcorn` in action? Examples are available at ../../examples/nitcor
  - [x] Configuration change on the fly
  - [x] Sessions
  - [x] Reading cookies
+ - [x] Parameterized routes
  - [ ] Full cookie support
  - [ ] Close interfaces on the fly
  - [ ] Better logging
@@ -28,3 +29,7 @@ Jean-Philippe Caissy, Guillaume Auger, Frederic Sevillano, Justin Michaud-Ouelle
 Stephan Michaud and Maxime Bélanger.
 
 It has been adapted to a library, and is currently maintained, by Alexis Laferrière.
+
+Other contributors:
+
+* Alexandre Terrasa
index 0457f3c..48084e0 100644 (file)
@@ -21,7 +21,7 @@ module reactor
 import more_collections
 import libevent
 
-import server_config
+import vararg_routes
 import http_request
 import http_response
 
@@ -32,7 +32,10 @@ class HttpServer
        # The associated `HttpFactory`
        var factory: HttpFactory
 
-       init(buf_ev: NativeBufferEvent, factory: HttpFactory) do self.factory = factory
+       # Init the server using `HttpFactory`.
+       init(buf_ev: NativeBufferEvent, factory: HttpFactory) is old_style_init do
+               self.factory = factory
+       end
 
        private var parser = new HttpRequestParser is lazy
 
@@ -69,6 +72,9 @@ class HttpServer
                if virtual_host != null then
                        var route = virtual_host.routes[request.uri]
                        if route != null then
+                               # include uri parameters in request
+                               request.uri_params = route.parse_params(request.uri)
+
                                var handler = route.handler
                                var root = route.path
                                var turi
@@ -91,7 +97,7 @@ redef abstract class Action
        # `request` is fully formed request object and has a reference to the session
        # if one preexists.
        #
-       # `truncated_uri` is the ending of the fulle request URI, truncated from the route
+       # `truncated_uri` is the ending of the full request URI, truncated from the route
        # leading to this `Action`.
        fun answer(request: HttpRequest, truncated_uri: String): HttpResponse is abstract
 end
index f90d4ef..c2bd614 100644 (file)
@@ -40,8 +40,7 @@ class VirtualHost
        var routes = new Routes(self)
 
        # Create a virtual host from interfaces as strings
-       init(interfaces: String ...)
-       do
+       init(interfaces: String ...) is old_style_init do
                for i in interfaces do self.interfaces.add_from_string i
        end
 end
@@ -115,18 +114,19 @@ class Routes
        # Back reference to the config of the virtual host
        var config: VirtualHost
 
-       private var array = new Array[Route]
+       # Internal routes array.
+       protected var routes = new Array[Route]
 
        # Add `e` to `self`
-       fun add(e: Route) do array.add e
+       fun add(e: Route) do routes.add e
 
        # Remove `e` from `self`
-       fun remove(e: Route) do array.remove e
+       fun remove(e: Route) do routes.remove e
 
        # Get the first `Route` than has `key` as prefix to its path
        fun [](key: String): nullable Route
        do
-               for route in array do
+               for route in routes do
                        var path = route.path
                        if path == null or key.has_prefix(path) then return route
                end
diff --git a/lib/nitcorn/vararg_routes.nit b/lib/nitcorn/vararg_routes.nit
new file mode 100644 (file)
index 0000000..9099284
--- /dev/null
@@ -0,0 +1,228 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexandre Terrasa <alexandre@moz-code.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.
+
+# Routes with uri parameters.
+#
+# Using `vararg_routes`, a `Route` can contain variable parts
+# that will be matched against a `HttpRequest` path.
+#
+# Variable parts of path can be specified using the `:` prefix.
+#
+# ## Route matching
+#
+# Route can match variables expression.
+#
+# ~~~
+# # We need an Action to try routes.
+# class DummyAction super Action end
+# var action = new DummyAction
+#
+# var route = new Route("/users/:id", action)
+# assert not route.match("/users")
+# assert route.match("/users/1234")
+# assert route.match("/users/") # empty id
+# ~~~
+#
+# Route without uri parameters still behave like before.
+#
+# ~~~
+# route = new Route("/users", action)
+# assert route.match("/users")
+# assert route.match("/users/1234")
+# assert not route.match("/issues/1234")
+# ~~~
+#
+# ## Route priority
+#
+# Priority depends on the order the routes were added to the `Routes` dispatcher.
+#
+# ~~~
+# var host = new VirtualHost("")
+# var routes = new Routes(host)
+#
+# routes.add new Route("/:a/:b/:c", action)
+# routes.add new Route("/users/:id", action)
+# routes.add new Route("/:foo", action)
+#
+# assert routes["/a/b/c"].path == "/:a/:b/:c"
+# assert routes["/a/b/c/d"].path == "/:a/:b/:c"
+# assert routes["/users/1234/foo"].path == "/:a/:b/:c"
+#
+# assert routes["/users/"].path == "/users/:id"
+# assert routes["users/"].path == "/users/:id"
+# assert routes["/users/1234"].path == "/users/:id"
+#
+# assert routes["/users"].path == "/:foo"
+# assert routes["/"].path == "/:foo"
+# assert routes[""].path == "/:foo"
+# ~~~
+#
+# ## Accessing uri parameter and values
+#
+# Parameters can be accessed by parsing the uri.
+#
+# ~~~
+# route = new Route("/users/:id", action)
+# var params = route.parse_params("/users/1234")
+# assert params.has_key("id")
+# assert not params.has_key("foo")
+# assert params["id"] == "1234"
+# ~~~
+#
+# Or from the `HttpRequest`.
+#
+# ~~~
+# route = new Route("/users/:id", action)
+# var req = new HttpRequest
+# req.uri_params = route.parse_params("/users/1234")
+# assert req.params == ["id"]
+# assert req.param("id") == "1234"
+# assert req.param("foo") == null
+# ~~~
+#
+# Note that normally, all this work is done by nitcorn.
+# Params can then be accessed in the `HttpRequest` given to `Action::answer`.
+module vararg_routes
+
+import server_config
+import http_request
+
+# A route to an `Action` according to a `path`
+redef class Route
+
+       redef init do
+               super
+               parse_pattern(path)
+       end
+
+       # Cut `path` into `UriParts`.
+       private fun parse_pattern(path: nullable String) do
+               if path == null then return
+               path = standardize_path(path)
+               var parts = path.split("/")
+               for part in parts do
+                       if not part.is_empty and part.first == ':' then
+                               # is an uri param
+                               var name = part.substring(1, part.length)
+                               var param = new UriParam(name)
+                               pattern_parts.add param
+                       else
+                               # is a standard string
+                               pattern_parts.add new UriString(part)
+                       end
+               end
+       end
+
+       # `UriPart` forming `self` pattern.
+       private var pattern_parts = new Array[UriPart]
+
+       # Does `self` matches `uri`?
+       fun match(uri: nullable String): Bool do
+               if pattern_parts.is_empty then return true
+               if uri == null then return false
+               uri = standardize_path(uri)
+               var parts = uri.split("/")
+               for i in [0 .. pattern_parts.length[ do
+                       if i >= parts.length then return false
+                       var ppart = pattern_parts[i]
+                       var part = parts[i]
+                       if not ppart.match(part) then return false
+               end
+               return true
+       end
+
+       # Extract parameter values from `uri`.
+       fun parse_params(uri: nullable String): Map[String, String] do
+               var res = new HashMap[String, String]
+               if pattern_parts.is_empty then return res
+               if uri == null then return res
+               uri = standardize_path(uri)
+               var parts = uri.split("/")
+               for i in [0 .. pattern_parts.length[ do
+                       if i >= parts.length then return res
+                       var ppart = pattern_parts[i]
+                       var part = parts[i]
+                       if not ppart.match(part) then return res
+                       if ppart isa UriParam then
+                               res[ppart.name] = part
+                       end
+               end
+               return res
+       end
+
+       # Remove first occurence of `/`.
+       private fun standardize_path(path: String): String do
+               if not path.is_empty and path.first == '/' then
+                       return path.substring(1, path.length)
+               end
+               return path
+       end
+end
+
+# A String that compose an URI.
+#
+# In practice, UriPart can be parameters or static strings.
+private interface UriPart
+       # Does `self` matches a part of the uri?
+       fun match(uri_part: String): Bool is abstract
+end
+
+# An uri parameter string like `:id`.
+private class UriParam
+       super UriPart
+
+       # Param `name` in the route uri.
+       var name: String
+
+       # Parameters match everything.
+       redef fun match(part) do return true
+end
+
+# A static uri string like `users`.
+private class UriString
+       super UriPart
+
+       # Uri part string.
+       var string: String
+
+       # Empty strings match everything otherwise matching is based on string equality.
+       redef fun match(part) do return string.is_empty or string == part
+end
+
+redef class Routes
+       # Use `Route::match` instead of `==`.
+       redef fun [](key) do
+               for route in routes do
+                       if route.match(key) then return route
+               end
+               return null
+       end
+end
+
+redef class HttpRequest
+
+       # Parameters found in uri associated to their values.
+       var uri_params: Map[String, String] = new HashMap[String, String] is public writable
+
+       # Get the value for parameter `name` or `null`.
+       fun param(name: String): nullable String do
+               if not uri_params.has_key(name) then return null
+               return uri_params[name]
+       end
+
+       # List all uri parameters matched by this request.
+       fun params: Array[String] do return uri_params.keys.to_a
+end
index 4008422..70b8e7c 100644 (file)
@@ -44,6 +44,7 @@ in "C Header" `{
        #include <string.h>
        #include <stdlib.h>
        #include <pthread.h>
+       #include <poll.h>
 
        #define MAX_DICTIONARY_QUEUE_SIZE 200
        #define MAX_MESSAGE_QUEUE_SIZE 10
@@ -358,7 +359,7 @@ in "C Header" `{
        }
 
        /* Hack in order to avoid the problem with file. */
-       int poll(void *fds, int nfds, int timeout) { return 0; }
+       int poll(struct pollfd* fds, nfds_t nfds, int timeout) { return 0; }
 `}
 
 # Nit class representing a Pepper C API PP_Var typed as a Dictionary.
index e8f7e73..a5425dd 100644 (file)
@@ -226,21 +226,26 @@ class POSet[E: Object]
 
        # Write the POSet as a graphviz digraph.
        #
-       # Nodes are identified with their `to_s`.
+       # Nodes are labeled with their `to_s` so homonymous nodes may appear.
        # Edges are unlabeled.
        fun write_dot(f: OStream)
        do
                f.write "digraph \{\n"
+               var ids = new HashMap[E, Int]
+               for x in elements.keys do
+                       ids[x] = ids.length
+               end
                for x in elements.keys do
                        var xstr = x.to_s.escape_to_dot
-                       f.write "\"{xstr}\";\n"
+                       var nx = "n{ids[x]}"
+                       f.write "{nx}[label=\"{xstr}\"];\n"
                        var xe = self.elements[x]
                        for y in xe.dtos do
-                               var ystr = y.to_s.escape_to_dot
+                               var ny = "n{ids[y]}"
                                if self.has_edge(y,x) then
-                                       f.write "\"{xstr}\" -> \"{ystr}\"[dir=both];\n"
+                                       f.write "{nx} -> {ny}[dir=both];\n"
                                else
-                                       f.write "\"{xstr}\" -> \"{ystr}\";\n"
+                                       f.write "{nx} -> {ny};\n"
                                end
                        end
                end
@@ -254,7 +259,6 @@ class POSet[E: Object]
        fun show_dot
        do
                var f = new OProcess("dot", "-Txlib")
-               f.write "\}\n"
                write_dot(f)
                f.close
                f.wait
index 68b7834..b66ade0 100644 (file)
@@ -21,17 +21,24 @@ in "C header" `{
 #include <time.h>
 `}
 
+# Elapsed time representation.
 extern class Timespec `{struct timespec*`}
+
+       # Init a new Timespec from `s` seconds and `ns` nanoseconds.
        new ( s, ns : Int ) `{
                struct timespec* tv = malloc( sizeof(struct timespec) );
                tv->tv_sec = s; tv->tv_nsec = ns;
                return tv;
        `}
+
+       # Init a new Timespec from now.
        new monotonic_now `{
                struct timespec* tv = malloc( sizeof(struct timespec) );
                clock_gettime( CLOCK_MONOTONIC, tv );
                return tv;
        `}
+
+       # Init a new Timespec copied from another.
        new copy_of( other : Timespec ) `{
                struct timespec* tv = malloc( sizeof(struct timespec) );
                tv->tv_sec = other->tv_sec;
@@ -39,9 +46,12 @@ extern class Timespec `{struct timespec*`}
                return tv;
        `}
 
+       # Update `self` clock.
        fun update `{
                clock_gettime( CLOCK_MONOTONIC, recv );
        `}
+
+       # Substract a Timespec from `self`.
        fun - ( o : Timespec ) : Timespec
        do
                var s = sec - o.sec
@@ -50,17 +60,18 @@ extern class Timespec `{struct timespec*`}
                return new Timespec( s, ns )
        end
 
+       # Number of whole seconds of elapsed time.
        fun sec : Int `{
                return recv->tv_sec;
        `}
+
+       # Rest of the elapsed time (a fraction of a second).
+       #
+       # Number of nanoseconds.
        fun nanosec : Int `{
                return recv->tv_nsec;
        `}
 
-       fun destroy `{
-               free( recv );
-       `}
-
        # Seconds in Float
        # Loss of precision but great to print
        fun to_f: Float do return sec.to_f + nanosec.to_f / 1000000000.0
index 97f16d8..f54ec0f 100644 (file)
@@ -91,7 +91,7 @@ class AttributesImpl
                                return data[index * 5 + 3]
                        end
                else if index isa String and "" != index then
-                       var i: Int = 0
+                       var i = 0
 
                        while i < data.length do
                                if data[i + 2] == index then
@@ -130,7 +130,7 @@ class AttributesImpl
                                return data[index * 5 + 4]
                        end
                else if index isa String and "" != index then
-                       var i: Int = 0
+                       var i = 0
 
                        while i < data.length do
                                if data[i + 2] == index then
@@ -158,7 +158,7 @@ class AttributesImpl
        # The index of the attribute, or -1 if it does not
        # appear in the list.
        redef fun index_ns(uri: String, local_name: String): Int do
-               var i: Int = 0
+               var i = 0
 
                if "" != local_name then
                        while i < data.length do
@@ -185,7 +185,7 @@ class AttributesImpl
        # The index of the attribute, or -1 if it does not
        # appear in the list.
        redef fun index_of(qname: String): Int do
-               var i: Int = 0
+               var i = 0
 
                if "" != qname then
                        while i < data.length do
@@ -219,7 +219,7 @@ class AttributesImpl
        # attribute is not in the list or if Namespace
        # processing is not being performed.
        redef fun type_ns(uri: String, local_name: String): nullable String do
-               var i: Int = 0
+               var i = 0
 
                if "" != local_name then
                        while i < data.length do
@@ -253,7 +253,7 @@ class AttributesImpl
        # attribute is not in the list or if Namespace
        # processing is not being performed.
        redef fun value_ns(uri: String, local_name: String): nullable String do
-               var i: Int = 0
+               var i = 0
 
                if "" != local_name then
                        while i < data.length do
@@ -281,7 +281,7 @@ class AttributesImpl
        #
        # * `atts`: attributes to copy.
        fun attributes=(atts: Attributes) do
-               var i: Int = 0
+               var i = 0
 
                clear
                length = atts.length
index dc7ecd2..27c98c4 100644 (file)
@@ -18,7 +18,7 @@ class TestAttributesImpl
        super TestSuite
 
        private fun sample: AttributesImpl do
-               var subject: AttributesImpl = new AttributesImpl
+               var subject = new AttributesImpl
 
                # The parser may include everything...
                subject.add("http://example.com/", "bar", "foo:bar", "CDATA", "baz")
@@ -33,7 +33,7 @@ class TestAttributesImpl
        end
 
        fun test_length do
-               var subject: AttributesImpl = new AttributesImpl
+               var subject = new AttributesImpl
 
                assert 0 == subject.length
                subject.add("http://example.com/", "bar", "foo:bar", "CDATA", "baz")
@@ -48,7 +48,7 @@ class TestAttributesImpl
        end
 
        fun test_uri do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "http://example.com/" == subject.uri(0)
                assert "urn:is:not:often:used" == subject.uri(1)
@@ -61,7 +61,7 @@ class TestAttributesImpl
        end
 
        fun test_local_name do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "bar" == subject.local_name(0)
                assert "i-am_ME" == subject.local_name(1)
@@ -74,7 +74,7 @@ class TestAttributesImpl
        end
 
        fun test_qname do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "foo:bar" == subject.qname(0)
                assert "" == subject.qname(1)
@@ -87,7 +87,7 @@ class TestAttributesImpl
        end
 
        fun test_type_of do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "CDATA" == subject.type_of(0)
                assert "ID" == subject.type_of(1)
@@ -100,7 +100,7 @@ class TestAttributesImpl
        end
 
        fun test_type_of_qname do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "CDATA" == subject.type_of("foo:bar")
                assert subject.type_of("") == null
@@ -112,7 +112,7 @@ class TestAttributesImpl
        end
 
        fun test_value_of do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "baz" == subject.value_of(0)
                assert "noop" == subject.value_of(1)
@@ -125,7 +125,7 @@ class TestAttributesImpl
        end
 
        fun test_value_of_qname do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "baz" == subject.value_of("foo:bar")
                assert subject.value_of("") == null
@@ -137,7 +137,7 @@ class TestAttributesImpl
        end
 
        fun test_index_ns do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert 0 == subject.index_ns("http://example.com/", "bar")
                assert 1 == subject.index_ns("urn:is:not:often:used", "i-am_ME")
@@ -149,7 +149,7 @@ class TestAttributesImpl
        end
 
        fun test_index_of do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert 0 == subject.index_of("foo:bar")
                assert -1 == subject.index_of("")
@@ -161,7 +161,7 @@ class TestAttributesImpl
        end
 
        fun test_type_ns do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "CDATA" == subject.type_ns("http://example.com/", "bar")
                assert "ID" == subject.type_ns("urn:is:not:often:used", "i-am_ME")
@@ -173,7 +173,7 @@ class TestAttributesImpl
        end
 
        fun test_value_ns do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                assert "baz" == subject.value_ns("http://example.com/", "bar")
                assert "noop" == subject.value_ns("urn:is:not:often:used", "i-am_ME")
@@ -185,8 +185,8 @@ class TestAttributesImpl
        end
 
        fun test_attributes_set do
-               var subject: AttributesImpl = sample
-               var subject2: AttributesImpl = new AttributesImpl
+               var subject = sample
+               var subject2 = new AttributesImpl
 
                subject.attributes = subject2
                assert subject.length == 0
@@ -196,7 +196,7 @@ class TestAttributesImpl
        end
 
        fun test_set do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                subject.set(1, "urn:is:not:often:used", "i-am_ME", "i-am_ME", "ID",
                                "noop")
@@ -206,7 +206,7 @@ class TestAttributesImpl
        end
 
        fun test_remove_at do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                subject.remove_at(1)
                assert 3 == subject.length
@@ -214,7 +214,7 @@ class TestAttributesImpl
        end
 
        fun test_uri_set do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                subject.uri(0) = "https://example.org/serious"
                subject.uri(1) = "ftp://wat"
@@ -223,7 +223,7 @@ class TestAttributesImpl
        end
 
        fun test_local_name_set do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                subject.local_name(0) = "trololol"
                subject.local_name(1) = "ImYou42"
@@ -232,7 +232,7 @@ class TestAttributesImpl
        end
 
        fun test_qname_set do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                subject.qname(0) = "go-to:bar"
                subject.qname(1) = "yo:i-am_ME"
@@ -241,7 +241,7 @@ class TestAttributesImpl
        end
 
        fun test_type_of_set do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                subject.type_of(0) = "NMTOKENS"
                subject.type_of(1) = "ENTITY"
@@ -250,7 +250,7 @@ class TestAttributesImpl
        end
 
        fun test_value_of_set do
-               var subject: AttributesImpl = sample
+               var subject = sample
 
                subject.value_of(0) = "buz"
                subject.value_of(1) = "bizzz"
index 07277ef..289d7fe 100644 (file)
@@ -350,7 +350,7 @@ class XophonLexer
        # match `expected`.
        fun expect_string(expected: String, context: String): Bool do
                var chars = expected.chars
-               var i: Int = 0
+               var i = 0
 
                while i < chars.length do
                        if not accept(chars[i]) then
index 02e6104..1073b4d 100644 (file)
@@ -591,7 +591,7 @@ class XophonReader
                var buffer: Buffer = new FlatBuffer
 
                # Number of consecutive closing brackets.
-               var closing: Int = 0
+               var closing = 0
 
                if lexer.expect_string("CDATA[",
                                " at the beginning of a CDATA section.") then
index a3f4b15..1987d8c 100644 (file)
@@ -87,7 +87,7 @@ class SAXEventLogger
                var buf = new FlatBuffer
                var sub_diff: Array[Int]
                var equal: Bool
-               var i: Int = 0
+               var i = 0
                var min: Int
                var max: Int
 
@@ -140,7 +140,7 @@ class SAXEventLogger
        private fun diff_entry(actual: Array[String], expected: Array[String]):
                        Array[Int] do
                var result = new Array[Int]
-               var i: Int = 0
+               var i = 0
                var min: Int
                var max: Int
 
@@ -234,7 +234,7 @@ class SAXEventLogger
        private fun diff_append_mismatch_entry(buf: Buffer, entry: Array[String],
                        sorted_mismatches: Collection[Int], term_normal: String,
                        term_emphasis: String) do
-               var i: Int = 0
+               var i = 0
                var j = sorted_mismatches.iterator
                var length = entry.length
 
index 5a1616d..5225f9f 100644 (file)
@@ -280,6 +280,24 @@ class SDLMouseButtonEvent
        redef var pressed: Bool
        redef fun depressed: Bool do return not pressed
 
+       # Is this event raised by the left button?
+       fun is_left_button: Bool do return button == 1
+
+       # Is this event raised by the right button?
+       fun is_right_button: Bool do return button == 2
+
+       # Is this event raised by the middle button?
+       fun is_middle_button: Bool do return button == 3
+
+       # Is this event raised by the wheel going down?
+       fun is_down_wheel: Bool do return button == 4
+
+       # Is this event raised by the wheel going up?
+       fun is_up_wheel: Bool do return button == 5
+
+       # Is this event raised by the wheel?
+       fun is_wheel: Bool do return is_down_wheel or is_up_wheel
+
        init (x, y: Float, button: Int, pressed: Bool)
        do
                super(x, y)
index 993e0e5..744919d 100644 (file)
@@ -1,3 +1,3 @@
 all:
        mkdir -p bin/
-       ../../../../bin/nitg -o bin/minimal src/minimal.nit
+       ../../../../bin/nitc -o bin/minimal src/minimal.nit
index 331a9d9..add6077 100644 (file)
@@ -109,7 +109,7 @@ abstract class AbstractArrayRead[E]
        #     var b = [10, 20, 30, 40, 50]
        #     a.copy_to(1, 2, b, 2)
        #     assert b      ==  [10, 20, 2, 3, 50]
-       protected fun copy_to(start: Int, len: Int, dest: AbstractArray[E], new_start: Int)
+       fun copy_to(start: Int, len: Int, dest: AbstractArray[E], new_start: Int)
        do
                # TODO native one
                var i = len
index 2495800..79f9bfd 100644 (file)
@@ -15,7 +15,7 @@
 # Standard input and output can be handled through streams.
 module exec
 
-import stream
+import file
 
 # Simple sub-process
 class Process
@@ -93,7 +93,7 @@ class IProcess
        super IStream
 
        # File Descriptor used for the input.
-       var stream_in: FDIStream is noinit
+       var stream_in: IFStream is noinit
 
        redef fun close do stream_in.close
 
@@ -106,7 +106,7 @@ class IProcess
        redef fun execute
        do
                super
-               stream_in = new FDIStream(data.out_fd)
+               stream_in = new IFStream.from_fd(data.out_fd)
        end
 end
 
@@ -129,7 +129,7 @@ class OProcess
        redef fun execute
        do
                super
-               stream_out = new FDOStream(data.in_fd)
+               stream_out = new OFStream.from_fd(data.in_fd)
        end
 end
 
index c808105..c69ba64 100644 (file)
@@ -26,22 +26,19 @@ se_exec_data_t* exec_Process_Process_basic_exec_execute_4(void *s, char *prog, c
        if (pipeflag & 1) {
                int res = pipe(in_fd);
                if ( res == -1 ) {
-                       fprintf( stderr, "Pipe init failed in Process:basic_exec_execute: %s\n", strerror( errno ) );
-                       exit(1);
+                       return NULL;
                }
        }
        if (pipeflag & 2) {
                int res = pipe(out_fd);
                if ( res == -1 ) {
-                       fprintf( stderr, "Pipe init failed in Process:basic_exec_execute: %s\n", strerror( errno ) );
-                       exit(1);
+                       return NULL;
                }
        }
        if (pipeflag & 4) {
                int res = pipe(err_fd);
                if ( res == -1 ) {
-                       fprintf( stderr, "Pipe init failed in Process:basic_exec_execute: %s\n", strerror( errno ) );
-                       exit(1);
+                       return NULL;
                }
        }
                                        
index 60f5f69..a7c48db 100644 (file)
@@ -27,6 +27,8 @@ in "C Header" `{
        #include <sys/stat.h>
        #include <unistd.h>
        #include <stdio.h>
+       #include <poll.h>
+       #include <errno.h>
 `}
 
 # File Abstract Stream
@@ -43,6 +45,21 @@ abstract class FStream
 
        # File descriptor of this file
        fun fd: Int do return _file.fileno
+
+       # Sets the buffering mode for the current FStream
+       #
+       # If the buf_size is <= 0, its value will be 512 by default
+       #
+       # The mode is any of the buffer_mode enumeration in `Sys`:
+       #       - buffer_mode_full
+       #       - buffer_mode_line
+       #       - buffer_mode_none
+       fun set_buffering_mode(buf_size, mode: Int) do
+               if buf_size <= 0 then buf_size = 512
+               if _file.set_buffering_type(buf_size, mode) != 0 then
+                       last_error = new IOError("Error while changing buffering type for FStream, returned error {sys.errno.strerror}")
+               end
+       end
 end
 
 # File input stream
@@ -56,8 +73,14 @@ class IFStream
        # The original path is reused, therefore the reopened file can be a different file.
        fun reopen
        do
-               if not eof then close
+               if not eof and not _file.address_is_null then close
+               last_error = null
                _file = new NativeFile.io_open_read(path.to_cstring)
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Opening file at '{path.as(not null)}' failed with '{sys.errno.strerror}'")
+                       end_reached = true
+                       return
+               end
                end_reached = false
                _buffer_pos = 0
                _buffer.clear
@@ -65,7 +88,8 @@ class IFStream
 
        redef fun close
        do
-               _file.io_close
+               if _file.address_is_null then return
+               var i = _file.io_close
                _buffer.clear
                end_reached = true
        end
@@ -80,7 +104,7 @@ class IFStream
                _buffer.length = nb
                _buffer_pos = 0
        end
-       
+
        # End of file?
        redef var end_reached: Bool = false
 
@@ -90,11 +114,21 @@ class IFStream
                self.path = path
                prepare_buffer(10)
                _file = new NativeFile.io_open_read(path.to_cstring)
-               assert not _file.address_is_null else
-                       print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'"
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'")
+                       end_reached = true
                end
        end
 
+       init from_fd(fd: Int) do
+               self.path = ""
+               prepare_buffer(1)
+               _file = fd.fd_to_stream(read_only)
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Converting fd {fd} to stream failed with '{sys.errno.strerror}'")
+                       end_reached = true
+               end
+       end
 end
 
 # File output stream
@@ -104,30 +138,52 @@ class OFStream
        
        redef fun write(s)
        do
-               assert _is_writable
+               if last_error != null then return
+               if not _is_writable then
+                       last_error = new IOError("Cannot write to non-writable stream")
+                       return
+               end
                if s isa FlatText then
                        write_native(s.to_cstring, s.length)
                else
                        for i in s.substrings do write_native(i.to_cstring, i.length)
                end
+               _file.flush
        end
 
        redef fun close
        do
-               _file.io_close
+               if _file.address_is_null then
+                       if last_error != null then return
+                       last_error = new IOError("Cannot close unopened write stream")
+                       _is_writable = false
+                       return
+               end
+               var i = _file.io_close
+               if i != 0 then
+                       last_error = new IOError("Close failed due to error {sys.errno.strerror}")
+               end
                _is_writable = false
        end
-
        redef var is_writable = false
        
        # Write `len` bytes from `native`.
        private fun write_native(native: NativeString, len: Int)
        do
-               assert _is_writable
+               if last_error != null then return
+               if not _is_writable then
+                       last_error = new IOError("Cannot write to non-writable stream")
+                       return
+               end
+               if _file.address_is_null then
+                       last_error = new IOError("Writing on a null stream")
+                       _is_writable = false
+                       return
+               end
                var err = _file.io_write(native, len)
                if err != len then
                        # Big problem
-                       printn("Problem in writing : ", err, " ", len, "\n")
+                       last_error = new IOError("Problem in writing : {err} {len} \n")
                end
        end
        
@@ -135,14 +191,43 @@ class OFStream
        init open(path: String)
        do
                _file = new NativeFile.io_open_write(path.to_cstring)
-               assert not _file.address_is_null else
-                       print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'"
-               end
                self.path = path
                _is_writable = true
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'")
+                       is_writable = false
+               end
+       end
+
+       # Creates a new File stream from a file descriptor
+       init from_fd(fd: Int) do
+               self.path = ""
+               _file = fd.fd_to_stream(wipe_write)
+               _is_writable = true
+                if _file.address_is_null then
+                        last_error = new IOError("Error: Opening stream from file descriptor {fd} failed with '{sys.errno.strerror}'")
+                       _is_writable = false
+               end
        end
 end
 
+redef class Int
+       # Creates a file stream from a file descriptor `fd` using the file access `mode`.
+       #
+       # NOTE: The `mode` specified must be compatible with the one used in the file descriptor.
+       private fun fd_to_stream(mode: NativeString): NativeFile is extern "file_int_fdtostream"
+end
+
+# Constant for read-only file streams
+private fun read_only: NativeString do return "r".to_cstring
+
+# Constant for write-only file streams
+#
+# If a stream is opened on a file with this method,
+# it will wipe the previous file if any.
+# Else, it will create the file.
+private fun wipe_write: NativeString do return "w".to_cstring
+
 ###############################################################################
 
 # Standard input stream.
@@ -531,7 +616,7 @@ redef class String
        end
 
        # returns files contained within the directory represented by self
-       fun files : Set[ String ] is extern import HashSet[String], HashSet[String].add, NativeString.to_s, String.to_cstring, HashSet[String].as(Set[String]) `{
+       fun files: Array[String] is extern import Array[String], Array[String].add, NativeString.to_s, String.to_cstring `{
                char *dir_path;
                DIR *dir;
 
@@ -543,22 +628,22 @@ redef class String
                }
                else
                {
-                       HashSet_of_String results;
+                       Array_of_String results;
                        String file_name;
                        struct dirent *de;
 
-                       results = new_HashSet_of_String();
+                       results = new_Array_of_String();
 
                        while ( ( de = readdir( dir ) ) != NULL )
                                if ( strcmp( de->d_name, ".." ) != 0 &&
                                        strcmp( de->d_name, "." ) != 0 )
                                {
                                        file_name = NativeString_to_s( strdup( de->d_name ) );
-                                       HashSet_of_String_add( results, file_name );
+                                       Array_of_String_add( results, file_name );
                                }
 
                        closedir( dir );
-                       return HashSet_of_String_as_Set_of_String( results );
+                       return results;
                }
        `}
 end
@@ -617,6 +702,10 @@ private extern class NativeFile `{ FILE* `}
        fun io_close: Int is extern "file_NativeFile_NativeFile_io_close_0"
        fun file_stat: FileStat is extern "file_NativeFile_NativeFile_file_stat_0"
        fun fileno: Int `{ return fileno(recv); `}
+       # Flushes the buffer, forcing the write operation
+       fun flush: Int is extern "fflush"
+       # Used to specify how the buffering will be handled for the current stream.
+       fun set_buffering_type(buf_length: Int, mode: Int): Int is extern "file_NativeFile_NativeFile_set_buffering_type_0"
 
        new io_open_read(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_read_1"
        new io_open_write(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_write_1"
@@ -627,6 +716,10 @@ end
 
 redef class Sys
 
+       init do
+               if stdout isa FStream then stdout.as(FStream).set_buffering_mode(256, buffer_mode_line)
+       end
+
        # Standard input
        var stdin: PollableIStream = new Stdin is protected writable
 
@@ -636,6 +729,89 @@ redef class Sys
        # Standard output for errors
        var stderr: OStream = new Stderr is protected writable
 
+       # Enumeration for buffer mode full (flushes when buffer is full)
+       fun buffer_mode_full: Int is extern "file_Sys_Sys_buffer_mode_full_0"
+       # Enumeration for buffer mode line (flushes when a `\n` is encountered)
+       fun buffer_mode_line: Int is extern "file_Sys_Sys_buffer_mode_line_0"
+       # Enumeration for buffer mode none (flushes ASAP when something is written)
+       fun buffer_mode_none: Int is extern "file_Sys_Sys_buffer_mode_none_0"
+
+       # returns first available stream to read or write to
+       # return null on interruption (possibly a signal)
+       protected fun poll( streams : Sequence[FStream] ) : nullable FStream
+       do
+               var in_fds = new Array[Int]
+               var out_fds = new Array[Int]
+               var fd_to_stream = new HashMap[Int,FStream]
+               for s in streams do
+                       var fd = s.fd
+                       if s isa IFStream then in_fds.add( fd )
+                       if s isa OFStream then out_fds.add( fd )
+
+                       fd_to_stream[fd] = s
+               end
+
+               var polled_fd = intern_poll( in_fds, out_fds )
+
+               if polled_fd == null then
+                       return null
+               else
+                       return fd_to_stream[polled_fd]
+               end
+       end
+
+       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]) : nullable Int is extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{
+               int in_len, out_len, total_len;
+               struct pollfd *c_fds;
+               sigset_t sigmask;
+               int i;
+               int first_polled_fd = -1;
+               int result;
+
+               in_len = Array_of_Int_length( in_fds );
+               out_len = Array_of_Int_length( out_fds );
+               total_len = in_len + out_len;
+               c_fds = malloc( sizeof(struct pollfd) * total_len );
+
+               /* input streams */
+               for ( i=0; i<in_len; i ++ ) {
+                       int fd;
+                       fd = Array_of_Int__index( in_fds, i );
+
+                       c_fds[i].fd = fd;
+                       c_fds[i].events = POLLIN;
+               }
+
+               /* output streams */
+               for ( i=0; i<out_len; i ++ ) {
+                       int fd;
+                       fd = Array_of_Int__index( out_fds, i );
+
+                       c_fds[i].fd = fd;
+                       c_fds[i].events = POLLOUT;
+               }
+
+               /* poll all fds, unlimited timeout */
+               result = poll( c_fds, total_len, -1 );
+
+               if ( result > 0 ) {
+                       /* analyse results */
+                       for ( i=0; i<total_len; i++ )
+                               if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
+                                        c_fds[i].revents & POLLHUP ) /* closed */
+                               {
+                                       first_polled_fd = c_fds[i].fd;
+                                       break;
+                               }
+
+                       return Int_as_nullable( first_polled_fd );
+               }
+               else if ( result < 0 )
+                       fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
+
+               return null_Int();
+       `}
+
 end
 
 # Print `objects` on the standard output (`stdout`).
index 06f5e2e..f6a6987 100644 (file)
@@ -65,3 +65,11 @@ int file_stdin_poll_in_(void) {
        }
        return res > 0;
 }
+
+FILE* file_int_fdtostream(int fd, char* mode){
+       return fdopen(fd, mode);
+}
+
+int file_NativeFile_NativeFile_set_buffering_type_0(FILE* f, int buf_sz, int mode){
+       return setvbuf(f, NULL, mode, buf_sz);
+}
index 1f5b183..d8d9b4b 100644 (file)
@@ -24,6 +24,8 @@ extern int string_NativeString_NativeString_file_exists_0(char *f);
 extern void *string_NativeString_NativeString_file_stat_0(char *f);
 extern void *file_NativeFile_NativeFile_file_stat_0(FILE *f);
 extern int string_NativeString_NativeString_file_delete_0(char *f);
+FILE* file_int_fdtostream(int fd, char* mode);
+int file_NativeFile_NativeFile_set_buffering_type_0(FILE* f, int buf_sz, int mode);
 
 #define file_NativeFile_NativeFile_io_read_2(p, b, l) fread((b), 1, (l), (FILE*)(p))
 #define file_NativeFile_NativeFile_io_write_2(p, b, l) fwrite((b), 1, (l), (FILE*)(p))
@@ -40,6 +42,9 @@ extern int string_NativeString_NativeString_file_delete_0(char *f);
 #define file_FileStat_FileStat_ctime_0(self) (((struct stat*)self)->st_ctime)
 #define file_FileStat_FileStat_mtime_0(self) (((struct stat*)self)->st_mtime)
 #define file_FileStat_FileStat_size_0(self) (((struct stat*)self)->st_size)
+#define file_Sys_Sys_buffer_mode_full_0(self) _IOFBF
+#define file_Sys_Sys_buffer_mode_line_0(self) _IOLBF
+#define file_Sys_Sys_buffer_mode_none_0(self) _IONBF
 
 #define string_NativeString_NativeString_file_mkdir_0(p) (mkdir(p, 0777))
 #define string_NativeString_NativeString_file_getcwd_0(p) (getcwd(NULL, 0))
index 9ef2f4d..d7bc8e8 100644 (file)
@@ -229,6 +229,7 @@ class RopeBuffer
                var nns = new NativeString(buf_size)
                var blen = rpos - dumped
                ns.copy_to(nns, blen, dumped, 0)
+               ns = nns
                dumped = 0
                rpos = blen
                written = false
@@ -241,6 +242,10 @@ class RopeBuffer
                length = 0
                rpos = 0
                dumped = 0
+               if written then
+                       ns = new NativeString(buf_size)
+                       written = false
+               end
        end
 
        redef fun substring(from, count) do
@@ -337,14 +342,13 @@ class RopeBuffer
 
        redef fun add(c) do
                var rp = rpos
-               length += 1
-               ns[rp] = c
-               rp += 1
-               if rp == buf_size then
-                       rpos = rp
+               if rp >= buf_size then
                        dump_buffer
                        rp = 0
                end
+               ns[rp] = c
+               rp += 1
+               length += 1
                rpos = rp
        end
 
@@ -384,15 +388,12 @@ class RopeBuffer
        end
 
        redef fun reverse do
-               str = str.reversed
-               var nns = new NativeString(buf_size)
-               var j = rpos
-               var mits = ns
-               for i in [0 .. rpos[ do
-                       nns[i] = mits[j]
-                       j -= 1
+               # Flush the buffer in order to only have to reverse `str`.
+               if rpos > 0 and dumped != rpos then
+                       str += new FlatString.with_infos(ns, rpos - dumped, dumped, rpos - 1)
+                       dumped = rpos
                end
-               ns = nns
+               str = str.reversed
        end
 
        redef fun upper do
index 3c2c292..0b7bd0a 100644 (file)
 module stream
 
 intrude import ropes
+import error
 
 in "C" `{
        #include <unistd.h>
-       #include <poll.h>
-       #include <errno.h>
        #include <string.h>
        #include <signal.h>
 `}
 
+# Any kind of error that could be produced by an operation on Streams
+class IOError
+       super Error
+end
+
 # Abstract stream class
-interface IOS
+abstract class IOS
+       # Error produced by the file stream
+       #
+       #     var ifs = new IFStream.open("donotmakethisfile.binx")
+       #     ifs.read_all
+       #     ifs.close
+       #     assert ifs.last_error != null
+       var last_error: nullable IOError = null
+
        # close the stream
        fun close is abstract
 end
 
 # Abstract input streams
-interface IStream
+abstract class IStream
        super IOS
        # Read a character. Return its ASCII value, -1 on EOF or timeout
        fun read_char: Int is abstract
@@ -36,6 +48,7 @@ interface IStream
        # Read at most i bytes
        fun read(i: Int): String
        do
+               if last_error != null then return ""
                var s = new FlatBuffer.with_capacity(i)
                while i > 0 and not eof do
                        var c = read_char
@@ -76,6 +89,7 @@ interface IStream
        # NOTE: Use `append_line_to` if the line terminator needs to be preserved.
        fun read_line: String
        do
+               if last_error != null then return ""
                if eof then return ""
                var s = new FlatBuffer
                append_line_to(s)
@@ -116,6 +130,7 @@ interface IStream
        # ~~~
        fun read_all: String
        do
+               if last_error != null then return ""
                var s = new FlatBuffer
                while not eof do
                        var c = read_char
@@ -159,6 +174,7 @@ interface IStream
        # Therefore CARRIAGE RETURN & LINE FEED (`\r\n`) is also recognized.
        fun append_line_to(s: Buffer)
        do
+               if last_error != null then return
                loop
                        var x = read_char
                        if x == -1 then
@@ -177,7 +193,7 @@ interface IStream
 end
 
 # IStream capable of declaring if readable without blocking
-interface PollableIStream
+abstract class PollableIStream
        super IStream
 
        # Is there something to read? (without blocking)
@@ -186,7 +202,7 @@ interface PollableIStream
 end
 
 # Abstract output stream
-interface OStream
+abstract class OStream
        super IOS
        # write a string
        fun write(s: Text) is abstract
@@ -229,10 +245,9 @@ abstract class BufferedIStream
        super IStream
        redef fun read_char
        do
-               if _buffer_pos >= _buffer.length then
-                       fill_buffer
-               end
-               if _buffer_pos >= _buffer.length then
+               if last_error != null then return -1
+               if eof then
+                       last_error = new IOError("Stream has reached eof")
                        return -1
                end
                var c = _buffer.chars[_buffer_pos]
@@ -242,6 +257,7 @@ abstract class BufferedIStream
 
        redef fun read(i)
        do
+               if last_error != null then return ""
                if _buffer.length == _buffer_pos then
                        if not eof then
                                return read(i)
@@ -259,6 +275,7 @@ abstract class BufferedIStream
 
        redef fun read_all
        do
+               if last_error != null then return ""
                var s = new FlatBuffer
                while not eof do
                        var j = _buffer_pos
@@ -346,138 +363,9 @@ abstract class BufferedIStream
 end
 
 # An Input/Output Stream
-interface IOStream
-       super IStream
-       super OStream
-end
-
-##############################################################"
-
-# A File Descriptor Stream.
-abstract class FDStream
-       super IOS
-       # File description
-       var fd: Int
-
-       redef fun close do native_close(fd)
-
-       private fun native_close(i: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
-       private fun native_read_char(i: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
-       private fun native_read(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
-       private fun native_write(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
-       private fun native_write_char(i: Int, c: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
-end
-
-# An Input File Descriptor Stream.
-class FDIStream
-       super FDStream
+abstract class IOStream
        super IStream
-       redef var eof: Bool = false
-       
-       redef fun read_char
-       do
-               var nb = native_read_char(fd)
-               if nb == -1 then eof = true
-               return nb
-       end
-end
-
-# An Output File Descriptor Stream.
-class FDOStream
-       super FDStream
        super OStream
-       redef var is_writable = true
-
-       redef fun write(s)
-       do
-               var nb = native_write(fd, s.to_cstring, s.length)
-               if nb < s.length then is_writable = false
-       end
-end
-
-# An Input/Output File Descriptor Stream.
-class FDIOStream
-       super FDIStream
-       super FDOStream
-       super IOStream
-end
-
-redef interface Object
-       # returns first available stream to read or write to
-       # return null on interruption (possibly a signal)
-       protected fun poll( streams : Sequence[FDStream] ) : nullable FDStream
-       do
-               var in_fds = new Array[Int]
-               var out_fds = new Array[Int]
-               var fd_to_stream = new HashMap[Int,FDStream]
-               for s in streams do
-                       var fd = s.fd
-                       if s isa FDIStream then in_fds.add( fd )
-                       if s isa FDOStream then out_fds.add( fd )
-
-                       fd_to_stream[fd] = s
-               end
-
-               var polled_fd = intern_poll( in_fds, out_fds )
-
-               if polled_fd == null then
-                       return null
-               else
-                       return fd_to_stream[polled_fd]
-               end
-       end
-
-       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]) : nullable Int is extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{
-               int in_len, out_len, total_len;
-               struct pollfd *c_fds;
-               sigset_t sigmask;
-               int i;
-               int first_polled_fd = -1;
-               int result;
-
-               in_len = Array_of_Int_length( in_fds );
-               out_len = Array_of_Int_length( out_fds );
-               total_len = in_len + out_len;
-               c_fds = malloc( sizeof(struct pollfd) * total_len );
-
-               /* input streams */
-               for ( i=0; i<in_len; i ++ ) {
-                       int fd;
-                       fd = Array_of_Int__index( in_fds, i );
-
-                       c_fds[i].fd = fd;
-                       c_fds[i].events = POLLIN;
-               }
-
-               /* output streams */
-               for ( i=0; i<out_len; i ++ ) {
-                       int fd;
-                       fd = Array_of_Int__index( out_fds, i );
-
-                       c_fds[i].fd = fd;
-                       c_fds[i].events = POLLOUT;
-               }
-
-               /* poll all fds, unlimited timeout */
-               result = poll( c_fds, total_len, -1 );
-
-               if ( result > 0 ) {
-                       /* analyse results */
-                       for ( i=0; i<total_len; i++ )
-                               if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
-                                        c_fds[i].revents & POLLHUP ) /* closed */
-                               {
-                                       first_polled_fd = c_fds[i].fd;
-                                       break;
-                               }
-
-                       return Int_as_nullable( first_polled_fd );
-               }
-               else if ( result < 0 )
-                       fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
-
-               return null_Int();
-       `}
 end
 
 # Stream to a String.
diff --git a/lib/standard/stream_nit.c b/lib/standard/stream_nit.c
deleted file mode 100644 (file)
index 14bfece..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* This file is part of NIT ( http://www.nitlanguage.org ).
- *
- * Copyright 2004-2008 Jean Privat <jean@pryen.org>
- *
- * This file is free software, which comes along with NIT.  This software is
- * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
- * PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
- * is kept unaltered, and a notification of the changes is added.
- * You  are  allowed  to  redistribute it and sell it, alone or is a part of
- * another product.
- */
-
-#include "stream_nit.h"
-
-int stream_FDStream_FDStream_native_read_char_1(void *s, int fd) {
-       int result;
-       char buf;
-       ssize_t r = read(fd, &buf, 1);
-       if (r == 0)
-               result = -1;
-       else
-               result = buf;
-       return result;
-}
diff --git a/lib/standard/stream_nit.h b/lib/standard/stream_nit.h
deleted file mode 100644 (file)
index 3a4d51e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef __STREAM_NIT_H
-#define __STREAM_NIT_H
-/* This file is part of NIT ( http://www.nitlanguage.org ).
- *
- * Copyright 2004-2008 Jean Privat <jean@pryen.org>
- *
- * This file is free software, which comes along with NIT.  This software is
- * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
- * PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
- * is kept unaltered, and a notification of the changes is added.
- * You  are  allowed  to  redistribute it and sell it, alone or is a part of
- * another product.
- */
-
-#include <unistd.h>
-
-int stream_FDStream_FDStream_native_read_char_1(void *s, int fd);
-
-#define stream_FDStream_FDStream_native_close_1(self, p0) (close(p0))
-#define stream_FDStream_FDStream_native_read_3(s, i, b, l) read((i), ((b)), ((l)))
-#define stream_FDStream_FDStream_native_write_3(s, i, b, l) write((i), ((b)), ((l)))
-#define stream_FDStream_FDStream_native_write_char_2(s, i, c) write((i), (char[]){(c)}, 1 )
-
-#endif
index 07ed861..09e8389 100644 (file)
@@ -1,10 +1,11 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
+# Copyright 2014 Alexandre Terrasa <alexandre@moz-code.org>
 #
 # This file is free software, which comes along with NIT.  This software is
 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
+# without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A
 # PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
 # is kept unaltered, and a notification of the changes is added.
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
@@ -14,6 +15,7 @@
 module time
 
 import string_search
+import stream
 
 in "C Header" `{
        #include <time.h>
@@ -138,3 +140,101 @@ extern class Tm `{struct tm *`}
 
        redef fun to_s do return asctime.replace("\n", "")
 end
+
+# Date using the international format defined by ISO 8601.
+#
+# Usage:
+#
+#     # By default ISODate at today.
+#     var date = new ISODate
+#     var tm = new Tm.localtime
+#     assert date.year == tm.year + 1900
+#     assert date.month == tm.mon + 1
+#     assert date.day == tm.mday
+#
+#     # ISODate can be initialized from a String.
+#     date = new ISODate.from_string("1970-01-01T00:00:00Z")
+#     assert date.year == 1970
+#     assert date.month == 1
+#     assert date.day == 1
+#
+#     # ISODate can be printed as String following the ISO format.
+#     assert date.to_s == "1970-01-01T00:00:00Z"
+#
+# Note that only the `YYYY-MM-DDTHH:MM:SSZ` is supported for now.
+#
+# See <http://www.w3.org/QA/Tips/iso-date>
+class ISODate
+       super Comparable
+
+       # UTC years as Int.
+       var year: Int is noinit
+
+       # UTC months as Int (`1` for January).
+       var month: Int is noinit
+
+       # UTC days as Int (starting at `1`).
+       var day: Int is noinit
+
+       # UTC hours as Int.
+       var hours: Int is noinit
+
+       # UTC minutes as Int.
+       var minutes: Int is noinit
+
+       # UTC seconds as Int.
+       var seconds: Int is noinit
+
+       # UTC timezone marker.
+       #
+       # Note that I don't know what will happen if you change this value...
+       var timezone = "Z"
+
+       init do
+               var t = new Tm.localtime
+               year = 1900 + t.year
+               month = t.mon + 1
+               day = t.mday
+               hours = t.hour
+               minutes = t.min
+               seconds = t.sec
+       end
+
+       # Init `self` from a ISODate formatted string.
+       init from_string(str: String) do
+               year = str.substring(0, 4).to_i
+               month = str.substring(5, 2).to_i
+               day = str.substring(8, 2).to_i
+               hours = str.substring(11, 2).to_i
+               minutes = str.substring(14, 2).to_i
+               seconds = str.substring(17, 2).to_i
+               timezone = str.substring(19, str.length)
+       end
+
+       redef fun to_s do
+               var buff = new FlatBuffer
+               buff.append year.to_s
+               buff.add '-'
+               if month < 10 then buff.add '0'
+               buff.append month.to_s
+               buff.add '-'
+               if day < 10 then buff.add '0'
+               buff.append day.to_s
+               buff.add 'T'
+               if hours < 10 then buff.add '0'
+               buff.append hours.to_s
+               buff.add ':'
+               if minutes < 10 then buff.add '0'
+               buff.append minutes.to_s
+               buff.add ':'
+               if seconds < 10 then buff.add '0'
+               buff.append seconds.to_s
+               buff.append timezone
+               return buff.write_to_string
+       end
+
+       redef type OTHER: ISODate
+
+       # TODO handle timezones
+       redef fun <(o) do return to_s < o.to_s
+end
index 2796a3c..921ba28 100644 (file)
@@ -363,8 +363,8 @@ end
 private class TemplateStringIterator
        super MapIterator[String, nullable Streamable]
 
-       private var subject: TemplateString
-       private var key_it: Iterator[String] is noinit
+       var subject: TemplateString
+       var key_it: Iterator[String] is noinit
 
        init do
                self.key_it = subject.macro_names.iterator
index feb9671..d11898f 100644 (file)
@@ -93,6 +93,7 @@ class BinTreeMap[K: Comparable, E]
                return res.value
        end
 
+       # Search `key` in `from` and its children nodes.
        protected fun search_down(from: N, key: K): nullable N do
                var cmp = key <=> from.key
                if cmp == 0 then return from
@@ -115,6 +116,7 @@ class BinTreeMap[K: Comparable, E]
                return min_from(root.as(not null)).value
        end
 
+       # Get the left-most child from `node`.
        protected fun min_from(node: N): N do
                if node.left == null then return node
                return min_from(node.left.as(not null))
@@ -131,6 +133,7 @@ class BinTreeMap[K: Comparable, E]
                return max_from(root.as(not null)).value
        end
 
+       # Get the right-most child from `node`.
        protected fun max_from(node: N): N do
                if node.right == null then return node
                return max_from(node.right.as(not null))
@@ -148,6 +151,7 @@ class BinTreeMap[K: Comparable, E]
                insert_node(new BinTreeNode[K, E](key, item))
        end
 
+       # Add `node` in the tree.
        protected fun insert_node(node: N) do
                len += 1
                if root == null then
@@ -311,6 +315,7 @@ class BinTreeMap[K: Comparable, E]
                return sorted
        end
 
+       # Sort the tree from `node` and place results in `sorted`.
        protected fun sort_down(node: N, sorted: Array[E]) do
                if node.left != null then sort_down(node.left.as(not null), sorted)
                sorted.add(node.value)
@@ -323,6 +328,7 @@ class BinTreeMap[K: Comparable, E]
                return "[{print_tree(root)}]"
        end
 
+       # Print the tree starting from `node`.
        protected fun print_tree(node: N): String do
                var s = new FlatBuffer
                s.append(node.to_s)
@@ -340,6 +346,7 @@ class BinTreeMap[K: Comparable, E]
                f.close
        end
 
+       # Translate the tree in dot format starting from `node`.
        protected fun dot_down(node: N, f: OProcess) do
                if node.left != null then dot_down(node.left.as(not null), f)
                f.write node.to_dot
@@ -371,15 +378,11 @@ end
 class BinTreeNode[K: Comparable, E]
        super TreeNode[K, E]
 
-       private var prev: nullable BinTreeNode[K, E]
-       private var next: nullable BinTreeNode[K, E]
+       private var prev: nullable BinTreeNode[K, E] = null
+       private var next: nullable BinTreeNode[K, E] = null
 
        redef type N: BinTreeNode[K, E]
 
-       init(key: K, item: E) do
-               super(key, item)
-       end
-
        private var left_node: nullable N = null
 
        # `left` tree node child (null if node has no left child)
@@ -448,11 +451,10 @@ end
 private class BinTreeMapIterator[K: Comparable, E]
        super MapIterator[K, E]
 
-       var current: nullable BinTreeNode[K, E]
+       var tree: BinTreeMap[K, E]
+       var current: nullable BinTreeNode[K, E] = null
 
-       init(tree: BinTreeMap[K, E]) do
-               current = tree.first_node
-       end
+       init do current = tree.first_node
 
        redef fun is_ok do return not current == null
        redef fun next do current = current.next
index c1ad485..3f6e3ee 100644 (file)
@@ -110,7 +110,7 @@ end
 
 private extern class ConstPointer `{ const void * `}
        # Convert a C `char **` to a Nit `Array[String]`
-       private fun to_string_array: Array[String]
+       fun to_string_array: Array[String]
        import Array[String], Array[String].add, NativeString.to_s `{
                char **strings = (char**)recv;
 
index 6d464ec..fa48b79 100644 (file)
@@ -27,10 +27,10 @@ _nit_lazy() {
        # and setup a new `complete` for the command
        eval "$($1 --bash-completion)"
 
-       # Special case for `nitc` that uses the completion of `nitg`
-       if test "$cmd" = "nitc"; then
-               cmd=nitg
-               complete -F _nitg -o default nitc
+       # Special case for `nitg` that uses the completion of `nitc`
+       if test "$cmd" = "nitg"; then
+               cmd=nitc
+               complete -F _nitc -o default nitg
        fi
 
        if [[ $(type -t _$cmd) == function ]]; then
index 2f9e20f..c82f883 100755 (executable)
 hash=$1
 shift
 
-set +x
+set -x
 
 local_repo=nit/
-remote_repo=privat
-
 tools_dir=misc/jenkins/
 
 cd $local_repo
 git clean -fdxq .
+git fetch origin
 
-git fetch $remote_repo
-git checkout $hash
+if ! git checkout $hash; then
+       exit 1
+fi
 
-# Make nitg and tools
-$tools_dir/unitrun.sh "run-make-0initial_make" make
+# Make nitg (quickly)
+$tools_dir/unitrun.sh "run-make-csrc" make -C c_src
+$tools_dir/unitrun.sh "run-make-version" src/git-gen-version.sh
+$tools_dir/unitrun.sh "run-make-nitg_0" c_src/nitg -o bin/nitg_0 src/nitg.nit
+$tools_dir/unitrun.sh "run-make-nitg" bin/nitg_0 -o bin/nitg src/nitg.nit
 
 # Make nitester
 $tools_dir/unitrun.sh "run-make-nitester" make -C contrib/nitester/
index 10dfd38..ed4e2f7 100755 (executable)
@@ -28,7 +28,7 @@ shift
 if env time --quiet -f%U true 2>/dev/null; then
        TIME="env time --quiet -f%U -o '${name}.t.out'"
 elif env time -f%U true 2>/dev/null; then
-       TIME="env time -f%U -o '${name}.t.out'"
+       TIME="env time -f%U -o ${name}.t.out"
 else
        TIME=
 fi
index 737b4a1..18a579a 100644 (file)
@@ -131,25 +131,31 @@ hi def link NITFFIDelimiters              Keyword
 " FFI Python
 syntax include @FFIPython syntax/python.vim
 unlet b:current_syntax
-syn match NITFFILanguage       '"Python"' nextgroup=NITFFIBlockPython skipwhite
+syn match NITFFILanguage       /\c"Python"/ nextgroup=NITFFIBlockPython skipwhite
 syn region NITFFIBlockPython matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIPython
 
 " FFI Java
 syntax include @FFIJava syntax/java.vim
 unlet b:current_syntax
-syn match NITFFILanguage       '"Java"' nextgroup=NITFFIBlockJava skipwhite
+syn match NITFFILanguage       /\c"Java\(\| inner\)"/ nextgroup=NITFFIBlockJava skipwhite
 syn region NITFFIBlockJava matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIJava
 
 " FFI C++
 syntax include @FFICpp syntax/cpp.vim
 unlet b:current_syntax
-syn match NITFFILanguage       '"C++"' nextgroup=NITFFIBlockCpp skipwhite
+syn match NITFFILanguage       /\c"C++\(\| header\| body\)"/ nextgroup=NITFFIBlockCpp skipwhite
 syn region NITFFIBlockCpp matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFICpp
 
+" FFI Objective-C
+syntax include @FFIObjC syntax/objc.vim
+unlet b:current_syntax
+syn match NITFFILanguage       /\c"ObjC\(\| Header\| Body\)"/ nextgroup=NITFFIBlockObjC skipwhite
+syn region NITFFIBlockObjC matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIObjC
+
 " FFI C (the last one is the default)
 syntax include @FFIC syntax/c.vim
 unlet b:current_syntax
-syn match NITFFILanguage               '"C\(\| header\| body\)"'       nextgroup=NITFFIBlockC skipwhite
+syn match NITFFILanguage               /\c"C\(\| header\| body\)"/     nextgroup=NITFFIBlockC skipwhite
 syn region NITFFIBlockC matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIC
 
 hi def link NITFFILanguage             Define
index 71df3ee..f7f4b83 100644 (file)
@@ -9,7 +9,7 @@ See the `Makefile`.
 Once generated, manpages can then be checked individually with `man -l`
 
 ~~~
-man -l man1/nitg.1
+man -l man1/nitc.1
 ~~~
 
 For global access, one can set the `MANPATH` environment variable to this `man` directory (not the `man1` subdirectory).
@@ -17,5 +17,5 @@ Do not forget to append `$MANPATH` to keep existing manpages accessible.
 
 ~~~
 export MANPATH=/path/to/nit/share/man:$MANPATH
-man nitg
+man nitc
 ~~~
diff --git a/share/man/man1/nitc.1 b/share/man/man1/nitc.1
deleted file mode 120000 (symlink)
index e213084..0000000
+++ /dev/null
@@ -1 +0,0 @@
-nitg.1
\ No newline at end of file
index 75d357c..f1887db 100644 (file)
@@ -31,20 +31,20 @@ See the `DEBUGGER` section for details.
 The behavior of the interpreter may differs slightly from the compiler.
 
 First, the interpreted is the reference implementation for the specification of the Nit language.
-That means if `nitg` and `nit` have a different behavior on a same program, it is likely that `nit` is right and `nitg` is wrong.
+That means if `nitc` and `nit` have a different behavior on a same program, it is likely that `nit` is right and `nitc` is wrong.
 
 Second, the FFI is not yet implemented in the interpreter.
 Only a subset of the standard methods are implemented with some hard-coded behaviors.
 While it is enough to use most of the standard library, a lot of additional libraries may not be usable by the interpreter.
 
-Last, `nit` is the *Naive Interpretation Tool*, it means that it is slow and may take an average of 50.000% in overhead comparatively to `nitg`(it also means that `nitg` is fast).
+Last, `nit` is the *Naive Interpretation Tool*, it means that it is slow and may take an average of 50.000% in overhead comparatively to `nitc`(it also means that `nitc` is fast).
 In practice, the slowness is not an issue for simple Nit scripts;
-it is not a big deal if `nit` takes  millisecond to execute programs even if `nitg` only need microseconds.
+it is not a big deal if `nit` takes  millisecond to execute programs even if `nitc` only need microseconds.
 
 
 # OPTIONS
 
-Most options are the same than `nitg(1)`.
+Most options are the same than `nitc(1)`.
 Here, only the specific one are indicated.
 
 Note that, unlike in other Nit tools, the options *MUST* be indicated before the main module of a program.
similarity index 93%
rename from share/man/nitg.md
rename to share/man/nitc.md
index f35be1c..9c1d562 100644 (file)
@@ -2,23 +2,23 @@
 
 # NAME
 
-nitg - compiles Nit programs.
+nitc - compiles Nit programs.
 
 
 # SYNOPSIS
 
-nitg [*options*] FILE...
+nitc [*options*] FILE...
 
 
 # DESCRIPTION
 
-nitg is the current official Nit compiler.
+nitc 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).
+Internally, nitc rely on the presence of a C compiler. Usually gcc (but nitc 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.)
@@ -31,13 +31,13 @@ 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.
 
-    $ nitg prog1.nit prog2.nit
+    $ nitc prog1.nit prog2.nit
 
 To combine files into a single program, use the `-m` option.
 
-    $ nitg prog1.nit -m other_module.nit
+    $ nitc prog1.nit -m other_module.nit
 
-nitg can produces executables for various platforms when specific modules are used.
+nitc 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.
 
@@ -79,15 +79,15 @@ See the documentation of these specific modules for details.
 
     To show only `missing-doc` warnings in standard"
 
-        $ nitg -q -w missing-doc standard
+        $ nitc -q -w missing-doc standard
 
     To show all warnings and advices, except `missing-doc`:
 
-        $ nitg -W -w no-missing-doc standard
+        $ nitc -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
+        $ nitc -w missing-doc -w no-useless-type-test standard
 
 `-q`, `--quiet`
 :   Do not show warnings.
@@ -96,7 +96,7 @@ See the documentation of these specific modules for details.
 `--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.
+    By default, nitc tries to detect and display more than one error before aborting the compilation.
 
 `--no-color`
 :   Do not use color to display errors and warnings.
@@ -175,7 +175,7 @@ See the documentation of these specific modules for details.
     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`.
 
-        $ nitg examples/hello_world.nit --no-cc --dir hello --compile-dir hello --semi-global
+        $ nitc examples/hello_world.nit --no-cc --dir hello --compile-dir hello --semi-global
 
     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.
@@ -196,7 +196,7 @@ See the documentation of these specific modules for details.
 
     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.
 
-        $ nitg prog_vanilla.nit -m feature_chocolate.nit -m feature_cherry.nit
+        $ nitc prog_vanilla.nit -m feature_chocolate.nit -m feature_cherry.nit
 
 `-D`, `--define`
 :   Define a specific property.
@@ -209,7 +209,7 @@ See the documentation of these specific modules for details.
     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
+        $ nitc foo.nit -D prefix=/opt/foo -D port=8080 -D with_ssl
 
 `--release`
 :   Compile in release mode and finalize application.
@@ -218,7 +218,7 @@ See the documentation of these specific modules for details.
 
 ## COMPILATION MODES
 
-`nitg` includes distinct compilation modes.
+`nitc` includes distinct compilation modes.
 
 `--separate`
 :   Use separate compilation (default mode).
@@ -365,7 +365,7 @@ They are useless for a normal user.
 `--make-flags`
 :   Additional options to the `make` command.
 
-          $ nitg foo.nit --make-flags 'CC=clang' --make-flags 'CFLAGS="-O0 -g"'
+          $ nitc foo.nit --make-flags 'CC=clang' --make-flags 'CFLAGS="-O0 -g"'
 
 `--typing-test-metrics`
 :   Enable static and dynamic count of all type tests.
@@ -413,7 +413,7 @@ They are useless for a normal user.
 `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 behavior of the GC of the executables produced by nitc 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.
index 2d1f0a7..b8273f4 100644 (file)
@@ -6,7 +6,7 @@ nitls - lists the projects, groups and paths of Nit sources files.
 
 # SYNOPSIS
 
-nitls [*options*] FILE...
+nitls [*options*] [*FILE*]...
 
 # DESCRIPTION
 
@@ -14,11 +14,24 @@ nitls [*options*] FILE...
 
 It is basically a `ls` or a simple `find` specialized on `.nit` source files.
 
+By default `nitls` works with the current directory (`.`).
+
+Each file can then be:
+
+* A Nit module (file).
+  In this case, only this single module is considered
+* A Nit group (directory).
+  In this case, all the modules of the groups (and recursively the sub-groups) are considered
+* A normal directory.
+  In this case, all its entries are analysed.
+  Files that are Nit modules and directories that are Nit groups are considered.
+  Other files and directories are ignored.
+
 # EXAMPLES
 
-Show the tree of modules from the current directory and subdirectories.
+Show the tree of modules from the current directory.
 
-    $ nitls -t -r .
+    $ nitls -t
 
 Show the list of projects imported by the modules of the current directory.
 
@@ -26,25 +39,7 @@ Show the list of projects imported by the modules of the current directory.
 
 # OPTIONS
 
-## COLLECT
-
-`-r`, `--recursive`
-:   Process directories recursively.
-
-    All `.nit` files found in the specified directory and subdirectories are considered.
-
-`-d`, `--depends`
-:   List dependencies of given modules
-
-    All imported modules are also considered.
-
-`-k`, `--keep`
-:   Ignore errors and files that are not a Nit source file.
-
-    When a file that is not a valit Nit module is encoutered, it is ignored and the rest of the file are
-    processed.
-
-    Without this option, a error message is displayed and nitls terminates on such a case.
+Each combination of option
 
 ## PRESENTATION MODE
 
@@ -65,11 +60,39 @@ Three presentation modes are available.
 
     Each `.nit` file is presented indivitually.
 
+The three modes are exclusives.
+
+The default mode is `--project` unless one on the argument is a group, then it is `--group`.
+
+## COLLECT
+
+`-r`, `--recursive`
+:   Process directories recursively.
+
+    All `.nit` files found in the specified directory and subdirectories are considered.
+
+`-d`, `--depends`
+:   List dependencies of given modules
+
+    All imported modules are also considered.
+
+    In --tree and --source modes, the modules direclty imported are also displayed.
+
+`-k`, `--keep`
+:   Ignore errors and files that are not a Nit source file.
+
+    When a file that is not a valid Nit module is encoutered, it is ignored and the rest of the files are
+    processed.
+
+    Without this option, an error message is displayed and nitls terminates on such a case.
+
 ## PRESENTATION OPTIONS
 
 `-p`, `--path`
 :   List only path (instead of name + path).
 
+    Paths are displayed uncolored.
+
 `-M`
 :   List dependencies suitable for a rule in a Makefile.
 
index 5fac6a4..0864629 100644 (file)
@@ -25,6 +25,178 @@ nitpretty [*options*]... FILE
 `--check`
 :   Check format of Nit source files
 
+# SPECIFICATION
+
+The specification of the pretty printing is described here.
+
+* Default indentation level is one `'\t'` character and is increased by one for
+  each indentation level.
+* Default line max-size is 80.
+
+### COMMENTS
+
+There is many categories of comments:
+
+`Licence comments` are attached to the top of the file no blank line before,
+one after.
+
+~~~nitish
+# This is a licence comment
+
+# Documentation for module `foo`
+module foo
+~~~
+
+`ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
+
+They are printed before the definition with a blank line before and no after
+at the same indentation level than the definition.
+
+~~~nitish
+# Documentation for module `foo`
+module foo
+
+# Documentation for class `Bar`
+class Bar
+     # Documentation for method `baz`
+     fun baz do end
+end
+~~~
+
+`Block comments` are comments composed of one or more line rattached to nothing.
+They are displayed with one blank line before and after at current indent level.
+
+~~~nitish
+<blank>
+# block
+# comment
+<blank>
+~~~
+
+`Attached comments` are comments attached to a production.
+They are printed as this.
+
+~~~nitish
+fun foo do # attached comment
+end
+~~~
+
+`nitpretty` automatically remove multiple blanks between comments:
+
+~~~nitish
+# Licence
+# ...
+<blank>
+# Block comment
+~~~
+
+### INLINING
+
+Productions are automatically inlined when possible.
+
+Conditions:
+
+* The production must be syntactically inlinable
+* The inlined production length is less than `PrettyPrinterVisitor::max-size`
+* The production do not contains any comments
+
+### MODULES
+
+* There is a blank between the module declaration and its imports
+* There is no blank between imports and only one after
+* There is a blank between each extern block definition
+* There is a blank between each class definition
+* There is no blank line at the end of the module
+
+~~~nitish
+# Documentation for module `foo`
+module foo
+
+import a
+import b
+import c
+
+# Documentation for class `Bar`
+class Bar end
+
+class Baz end # not a `ADoc` comment
+~~~
+
+### CLASSES
+
+* There is no blank between the class definition and its super-classes declarations
+* There is no blank between two inlined property definition
+* There is a blank between each block definition
+* There no blank line at the end of the class definition
+
+~~~nitish
+# Documentation for class `Bar`
+class Bar end
+
+class Baz
+    super Bar
+
+    fun a is abstract
+    private fun b do end
+
+    fun c do
+        # ...
+    end
+end
+~~~
+
+Generic types have no space after or before brackets and are separated by a comma and a space:
+
+~~~nitish
+class A[E: Type1, F: Type1] end
+~~~
+
+### BLOCKS
+
+* Inlined productions have no blank lines between them
+* Block productions have a blank before and after
+
+~~~nitish
+var a = 10
+var b = 0
+
+if a > b then
+     is positive
+     print "positive"
+end
+
+print "end"
+~~~
+
+### CALLS AND BINARY OPS
+
+Arguments are always printed separated with a comma and a space:
+
+~~~nitish
+foo(a, b, c)
+~~~
+
+Binary ops are always printed wrapped with spaces:
+
+~~~nitish
+var c = 1 + 2
+~~~
+
+Calls and binary ops can be splitted to fit the `max-size` constraint.
+Breaking priority is given to arguments declaration after the comma.
+
+~~~nitish
+return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
+    "cccccccccccccccccccccccccc")
+~~~
+
+Binary ops can also be broken to fit the `max-size` limit:
+
+~~~nitish
+return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
+    "cccccccccccccccccccccccccc"
+~~~
+
 # SEE ALSO
 
 The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
index 13f500c..df006f9 100644 (file)
@@ -52,17 +52,21 @@ define([
                },
 
                _create: function() {
-                       this.element
-                               .attr(this.options.fieldAttrs)
-                               .keydown($.proxy(this._doKeyDown, this))
-                               .keyup($.proxy(this._doKeyUp, this))
-
+                       // set widget options
+                       this.element.attr(this.options.fieldAttrs);
+                       // event dispatch
+                       this._on(this.element, {
+                               "keydown": this._doKeyDown,
+                               "keyup": this._doKeyUp,
+                               "input": this._doInput
+                       });
+                       // add result table element once
                        this._table = $("<table/>")
                                .attr("id", this.options.tableID)
                                .css(this.options.tableCSS)
                                .css("min-width", this.element.outerWidth());
                        $("body").append(this._table);
-
+                       // make table disappear when a click occurs outside
                        $(document).click($.proxy(this.closeTable, this));
                },
 
@@ -94,11 +98,14 @@ define([
                                        this.closeTable();
                                        return true;
                                default: // Other keys
-                                       utils.delayEvent($.proxy(this.search, this));
                                        return true;
                        }
                },
 
+               _doInput: function(event) {
+                       utils.delayEvent($.proxy(this.search, this));
+               },
+
                /* Result lookup */
 
                _getResults: function(query) {
index 2f3d7a1..8c9742c 100644 (file)
 
 NITCOPT=
 OLDNITCOPT=
-OBJS=nitg nitpick nit nitdoc nitls nitunit nitpretty nitmetrics nitx nitlight nitdbg_client nitserial
+OBJS=nitc nitpick nit nitdoc nitls nitunit nitpretty nitmetrics nitx nitlight nitdbg_client nitserial
 SRCS=$(patsubst %,%.nit,$(OBJS))
 BINS=$(patsubst %,../bin/%,$(OBJS))
 
 all: $(BINS)
 
-nitg_0: ../c_src/nitg parser/parser.nit
+nitc_0: ../c_src/nitg parser/parser.nit
        @echo '***************************************************************'
-       @echo '* Compile nitg_0 from NIT source files                        *'
+       @echo '* Compile nitc_0 from NIT source files                        *'
        @echo '***************************************************************'
        ./git-gen-version.sh
-       ../c_src/nitg ${OLDNITCOPT} -o nitg_0 -v nitg.nit
+       ../c_src/nitg ${OLDNITCOPT} -o nitc_0 -v nitc.nit
 
-$(BINS): nitg_0 parser/parser.nit
+$(BINS): nitc_0 parser/parser.nit
        @echo '***************************************************************'
        @echo '* Compile binaries from NIT source files                      *'
        @echo '***************************************************************'
        ./git-gen-version.sh
-       ./nitg_0 ${NITCOPT} -v --dir ../bin $(SRCS)
+       ./nitc_0 ${NITCOPT} -v --dir ../bin $(SRCS)
 
-$(OBJS): nitg_0 parser/parser.nit
+$(OBJS): nitc_0 parser/parser.nit
        ./git-gen-version.sh
-       ./nitg_0 ${NITCOPT} -v $@.nit
+       ./nitc_0 ${NITCOPT} -v $@.nit
 
 ../c_src/nitg: ../c_src/*.c ../c_src/*.h ../c_src/Makefile
        @echo '***************************************************************'
-       @echo '* Compile nitg from C source files                            *'
+       @echo '* Compile nitc from C source files                            *'
        @echo '***************************************************************'
        cd ../c_src; make
 
@@ -53,5 +53,5 @@ parser/parser.nit:
        cd parser; make
 
 clean:
-       rm -rf -- .nit_compile* version.nit nitg_0 2> /dev/null || true
+       rm -rf -- .nit_compile* version.nit nitc_0 2> /dev/null || true
        cd parser; make clean
index 6329dac..caf6622 100644 (file)
@@ -3067,7 +3067,7 @@ end
 # Create a tool context to handle options and paths
 var toolcontext = new ToolContext
 
-toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
+toolcontext.tooldescription = "Usage: nitc [OPTION]... file.nit...\nCompiles Nit programs."
 
 # We do not add other options, so process them now!
 toolcontext.process_options(args)
index 9794beb..09883db 100644 (file)
@@ -237,7 +237,7 @@ redef class MModule
        redef fun tpl_declaration do
                var tpl = new Template
                tpl.add "<span>module "
-               tpl.add tpl_link
+               tpl.add tpl_namespace
                tpl.add "</span>"
                return tpl
        end
index 1780167..248a8ee 100644 (file)
@@ -905,7 +905,7 @@ class NitdocModule
 
                # Graph
                var mmodules = new HashSet[MModule]
-               mmodules.add_all mmodule.in_nesting.direct_greaters
+               mmodules.add_all mmodule.nested_mmodules
                mmodules.add_all imports
                if clients.length < 10 then mmodules.add_all clients
                mmodules.add mmodule
@@ -1498,7 +1498,12 @@ class NitdocProperty
 
        private fun tpl_properties(parent: TplSection) do
                # intro title
-               var section = new TplSection.with_title("intro", "Introduction")
+               var ns = mproperty.intro.mclassdef.mmodule.tpl_namespace
+               var section = new TplSection("intro")
+               var title = new Template
+               title.add "Introduction in "
+               title.add ns
+               section.title = title
                section.summary_title = "Introduction"
                section.add_child tpl_mpropdef_article(mproperty.intro)
                parent.add_child section
@@ -1516,7 +1521,7 @@ class NitdocProperty
                                parent.add_child new TplSection(mentity.nitdoc_id)
                        else if mentity isa MModule then
                                var ssection = new TplSection(mentity.nitdoc_id)
-                               var title = new Template
+                               title = new Template
                                title.add "in "
                                title.add mentity.tpl_namespace
                                ssection.title = title
index 353ecdf..7624626 100644 (file)
@@ -60,28 +60,28 @@ class TplPage
                var css = (self.shareurl / "css").html_escape
                var vendors = (self.shareurl / "vendors").html_escape
 
-               add "<!DOCTYPE html>"
-               add "<head>"
-               add " <meta charset='utf-8'/>"
-               add " <!--link rel='stylesheet' href='{css}/Nitdoc.UI.css' type='text/css'/-->"
-               add " <link rel='stylesheet' href='{vendors}/bootstrap/css/bootstrap.min.css'/>"
-               add " <link rel='stylesheet' href='{css}/nitdoc.bootstrap.css'/>"
-               add " <link rel='stylesheet' href='{css}/nitdoc.css'/>"
-               add " <link rel='stylesheet' href='{css}/Nitdoc.QuickSearch.css'/>"
-               add " <link rel='stylesheet' href='{css}/Nitdoc.ModalBox.css'/>"
-               add " <link rel='stylesheet' href='{css}/Nitdoc.GitHub.css'/>"
-               add " <title>{title}</title>"
-               add "</head>"
+               addn "<!DOCTYPE html>"
+               addn "<head>"
+               addn " <meta charset='utf-8'/>"
+               addn " <!--link rel='stylesheet' href='{css}/Nitdoc.UI.css' type='text/css'/-->"
+               addn " <link rel='stylesheet' href='{vendors}/bootstrap/css/bootstrap.min.css'/>"
+               addn " <link rel='stylesheet' href='{css}/nitdoc.bootstrap.css'/>"
+               addn " <link rel='stylesheet' href='{css}/nitdoc.css'/>"
+               addn " <link rel='stylesheet' href='{css}/Nitdoc.QuickSearch.css'/>"
+               addn " <link rel='stylesheet' href='{css}/Nitdoc.ModalBox.css'/>"
+               addn " <link rel='stylesheet' href='{css}/Nitdoc.GitHub.css'/>"
+               addn " <title>{title}</title>"
+               addn "</head>"
                add "<body"
                for attr in body_attrs do add attr
-               add ">"
+               addn ">"
        end
 
        # Render the topmenu template
        private fun render_topmenu do
-               add " <div class='row'>"
+               addn " <div class='row'>"
                add topmenu
-               add " </div>"
+               addn " </div>"
        end
 
        # Render the sidebar
@@ -99,9 +99,9 @@ class TplPage
        private fun render_content do
                for section in sections do add section
                if footer != null then
-                       add "<div class='well footer'>"
+                       addn "<div class='well footer'>"
                        add footer.as(not null)
-                       add "</div>"
+                       addn "</div>"
                end
        end
 
@@ -110,41 +110,41 @@ class TplPage
                var vendors = (self.shareurl / "vendors").html_escape
                var js = (self.shareurl / "js").html_escape
 
-               add "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
-               add "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
-               add "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
-               add "<script data-main='{js}/nitdoc' src='{js}/lib/require.js'></script>"
+               addn "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
+               addn "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
+               addn "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
+               addn "<script data-main='{js}/nitdoc' src='{js}/lib/require.js'></script>"
                for script in scripts do add script
-               add """<script>
+               addn """<script>
                        $(function () {
                                $("[data-toggle='tooltip']").tooltip();
                                $("[data-toggle='popover']").popover();
                        });
                </script>"""
-               add "</body>"
-               add "</html>"
+               addn "</body>"
+               addn "</html>"
        end
 
        # Render the whole page
        redef fun rendering do
                render_head
-               add "<div class='container-fluid'>"
+               addn "<div class='container-fluid'>"
                render_topmenu
-               add " <div class='row' id='content'>"
+               addn " <div class='row' id='content'>"
                if sidebar != null then
-                       add "<div class='col col-xs-3 col-lg-2'>"
+                       addn "<div class='col col-xs-3 col-lg-2'>"
                        render_sidebar
-                       add "</div>"
-                       add "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
+                       addn "</div>"
+                       addn "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
                        render_content
-                       add "</div>"
+                       addn "</div>"
                else
-                       add "<div class='col col-xs-12'>"
+                       addn "<div class='col col-xs-12'>"
                        render_content
-                       add "</div>"
+                       addn "</div>"
                end
-               add " </div>"
-               add "</div>"
+               addn " </div>"
+               addn "</div>"
                render_footer
        end
 end
@@ -182,7 +182,7 @@ class TplTopMenu
                end
                tpl.add ">"
                tpl.add content
-               tpl.add "</li>"
+               tpl.addn "</li>"
                add_raw(tpl)
        end
 
@@ -193,27 +193,27 @@ class TplTopMenu
 
        redef fun rendering do
                if brand == null and elts.is_empty then return
-               add "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
-               add " <div class='container-fluid'>"
-               add "  <div class='navbar-header'>"
+               addn "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
+               addn " <div class='container-fluid'>"
+               addn "  <div class='navbar-header'>"
                add "   <button type='button' class='navbar-toggle' "
-               add "       data-toggle='collapse' data-target='#topmenu-collapse'>"
-               add "    <span class='sr-only'>Toggle menu</span>"
-               add "    <span class='icon-bar'></span>"
-               add "    <span class='icon-bar'></span>"
-               add "    <span class='icon-bar'></span>"
-               add "   </button>"
+               addn "       data-toggle='collapse' data-target='#topmenu-collapse'>"
+               addn "    <span class='sr-only'>Toggle menu</span>"
+               addn "    <span class='icon-bar'></span>"
+               addn "    <span class='icon-bar'></span>"
+               addn "    <span class='icon-bar'></span>"
+               addn "   </button>"
                if brand != null then add brand.as(not null)
-               add "  </div>"
-               add "  <div class='collapse navbar-collapse' id='topmenu-collapse'>"
+               addn "  </div>"
+               addn "  <div class='collapse navbar-collapse' id='topmenu-collapse'>"
                if not elts.is_empty then
-                       add "<ul class='nav navbar-nav'>"
+                       addn "<ul class='nav navbar-nav'>"
                        for elt in elts do add elt
-                       add "</ul>"
+                       addn "</ul>"
                end
-               add "  </div>"
-               add " </div>"
-               add "</nav>"
+               addn "  </div>"
+               addn " </div>"
+               addn "</nav>"
        end
 end
 
@@ -233,9 +233,9 @@ class TplSidebar
        redef fun rendering do
                if boxes.is_empty then return
                order_boxes
-               add "<div id='sidebar'>"
+               addn "<div id='sidebar'>"
                for box in boxes do add box
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -296,16 +296,16 @@ class TplSideBox
                if content == null then return
                var open = ""
                if is_open then open = "in"
-               add "<div class='panel'>"
-               add " <div class='panel-heading'>"
+               addn "<div class='panel'>"
+               addn " <div class='panel-heading'>"
                add "  <a data-toggle='collapse' data-parent='#sidebar' data-target='#box_{id}' href='#'>"
                add title
-               add "  </a>"
-               add " </div>"
-               add " <div id='box_{id}' class='panel-body collapse {open}'>"
+               addn "  </a>"
+               addn " </div>"
+               addn " <div id='box_{id}' class='panel-body collapse {open}'>"
                add content.as(not null)
-               add " </div>"
-               add "</div>"
+               addn " </div>"
+               addn "</div>"
        end
 end
 
@@ -332,18 +332,18 @@ class TplSummary
 
        redef fun rendering do
                if children.is_empty then return
-               add "<div class='panel'>"
-               add " <div class='panel-heading'>"
+               addn "<div class='panel'>"
+               addn " <div class='panel-heading'>"
                add "  <a data-toggle='collapse' data-parent='#sidebar' data-target='#box-sum' href='#'>"
                add "Summary"
-               add "  </a>"
-               add " </div>"
-               add " <div id='box-sum' class='summary collapse in'>"
-               add " <ul class='nav'>"
+               addn "  </a>"
+               addn " </div>"
+               addn " <div id='box-sum' class='summary collapse in'>"
+               addn " <ul class='nav'>"
                for entry in children do add entry
-               add " </ul>"
-               add " </div>"
-               add "</div>"
+               addn " </ul>"
+               addn " </div>"
+               addn "</div>"
        end
 end
 
@@ -364,11 +364,11 @@ class TplSummaryEntry
                add "<li>"
                add text
                if not children.is_empty then
-                       add "<ul class='nav'>"
+                       addn "\n<ul class='nav'>"
                        for entry in children do add entry
-                       add "</ul>"
+                       addn "</ul>"
                end
-               add "</li>"
+               addn  "</li>"
        end
 end
 
@@ -444,23 +444,23 @@ class TplSection
        super TplSectionElt
 
        redef fun rendering do
-               add "<section id='{id}' class='{css_classes.join(" ")}'>"
+               addn "<section id='{id}' class='{css_classes.join(" ")}'>"
                if title != null then
                        var lvl = hlvl
                        if lvl == 2 then title_classes.add "well well-sm"
-                       add "<h{lvl} class='{title_classes.join(" ")}'>"
-                       add title.as(not null)
-                       add "</h{lvl}>"
+                       addn "<h{lvl} class='{title_classes.join(" ")}'>"
+                       addn title.as(not null)
+                       addn "</h{lvl}>"
                end
                if subtitle != null then
-                       add "<div class='info subtitle'>"
-                       add subtitle.as(not null)
-                       add "</div>"
+                       addn "<div class='info subtitle'>"
+                       addn subtitle.as(not null)
+                       addn "</div>"
                end
                for child in children do
                        add child
                end
-               add "</section>"
+               addn "</section>"
        end
 end
 
@@ -488,23 +488,23 @@ class TplArticle
 
        redef fun rendering do
                if is_empty then return
-               add "<article id='{id}' class='{css_classes.join(" ")}'>"
+               addn "<article id='{id}' class='{css_classes.join(" ")}'>"
                if source_link != null then
                        add "<div class='source-link'>"
                        add source_link.as(not null)
-                       add "</div>"
+                       addn "</div>"
                end
                if title != null then
                        var lvl = hlvl
                        if lvl == 2 then title_classes.add "well well-sm"
                        add "<h{lvl} class='{title_classes.join(" ")}'>"
                        add title.as(not null)
-                       add "</h{lvl}>"
+                       addn "</h{lvl}>"
                end
                if subtitle != null then
                        add "<div class='info subtitle'>"
                        add subtitle.as(not null)
-                       add "</div>"
+                       addn "</div>"
                end
                if content != null then
                        add content.as(not null)
@@ -512,7 +512,7 @@ class TplArticle
                for child in children do
                        add child
                end
-               add """</article>"""
+               addn """</article>"""
        end
 
        redef fun is_empty: Bool do
@@ -534,7 +534,7 @@ class TplDefinition
        var location: nullable Streamable = null is writable
 
        private fun render_info do
-               add "<div class='info text-right'>"
+               addn "<div class='info text-right'>"
                if namespace != null then
                        if comment == null then
                                add "<span class=\"noComment\">no comment for </span>"
@@ -545,7 +545,7 @@ class TplDefinition
                        add " "
                        add location.as(not null)
                end
-               add "</div>"
+               addn "</div>"
        end
 
        private fun render_comment do
@@ -553,10 +553,10 @@ class TplDefinition
        end
 
        redef fun rendering do
-               add "<div class='definition'>"
+               addn "<div class='definition'>"
                render_comment
                render_info
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -570,20 +570,20 @@ class TplClassDefinition
        init do end
 
        redef fun rendering do
-               add "<div class='definition'>"
+               addn "<div class='definition'>"
                render_comment
                render_info
                render_list("Introduces", intros)
                render_list("Redefines", redefs)
-               add "</div>"
+               addn "</div>"
        end
 
        private fun render_list(name: String, elts: Array[TplListElt]) do
                if elts.is_empty then return
-               add "<h5>{name.html_escape}</h5>"
-               add "<ul class='list-unstyled list-definition'>"
+               addn "<h5>{name.html_escape}</h5>"
+               addn "<ul class='list-unstyled list-definition'>"
                for elt in elts do add elt
-               add "</ul>"
+               addn "</ul>"
        end
 end
 
@@ -597,47 +597,47 @@ class TplSearchPage
 
        redef fun rendering do
                var title = self.title
-               if title != null then add "<h1>{title.to_s.html_escape}</h1>"
-               add "<div class='container-fluid'>"
-               add " <div class='row'>"
+               if title != null then addn "<h1>{title.to_s.html_escape}</h1>"
+               addn "<div class='container-fluid'>"
+               addn " <div class='row'>"
                if not modules.is_empty then
-                       add "<div class='col-xs-4'>"
-                       add "<h3>Modules</h3>"
-                       add "<ul>"
+                       addn "<div class='col-xs-4'>"
+                       addn "<h3>Modules</h3>"
+                       addn "<ul>"
                        for m in modules do
                                add "<li>"
                                add m
-                               add "</li>"
+                               addn "</li>"
                        end
-                       add "</ul>"
-                       add "</div>"
+                       addn "</ul>"
+                       addn "</div>"
                end
                if not classes.is_empty then
-                       add "<div class='col-xs-4'>"
-                       add "<h3>Classes</h3>"
-                       add "<ul>"
+                       addn "<div class='col-xs-4'>"
+                       addn "<h3>Classes</h3>"
+                       addn "<ul>"
                        for c in classes do
                                add "<li>"
                                add c
-                               add "</li>"
+                               addn "</li>"
                        end
-                       add "</ul>"
-                       add "</div>"
+                       addn "</ul>"
+                       addn "</div>"
                end
                if not props.is_empty then
-                       add "<div class='col-xs-4'>"
-                       add "<h3>Properties</h3>"
-                       add "<ul>"
+                       addn "<div class='col-xs-4'>"
+                       addn "<h3>Properties</h3>"
+                       addn "<ul>"
                        for p in props do
                                add "<li>"
                                add p
-                               add "</li>"
+                               addn "</li>"
                        end
-                       add "</ul>"
-                       add "</div>"
+                       addn "</ul>"
+                       addn "</div>"
                end
-               add " </div>"
-               add "</div>"
+               addn " </div>"
+               addn "</div>"
        end
 end
 
@@ -700,9 +700,9 @@ class TplList
 
        redef fun rendering do
                if elts.is_empty then return
-               add "<ul class='{css_classes.join(" ")}'>"
+               addn "<ul class='{css_classes.join(" ")}'>"
                for elt in elts do add elt
-               add "</ul>"
+               addn "</ul>"
        end
 end
 
@@ -737,7 +737,7 @@ class TplListItem
        redef fun rendering do
                add "<li class='{css_classes.join(" ")}'>"
                add content
-               add "</li>"
+               addn "</li>"
        end
 end
 
@@ -755,9 +755,9 @@ class TplTab
        var css_classes = new Array[String]
 
        redef fun rendering do
-               add "<div class='tab-content'>"
+               addn "<div class='tab-content'>"
                for panel in panels do add panel
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -796,9 +796,9 @@ class TplTabPanel
        redef fun rendering do
                add "<div class='tab-pane {css_classes.join(" ")}"
                if is_active then add "active"
-               add "' id='{id}'>"
+               addn "' id='{id}'>"
                if content != null then add content.as(not null)
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -886,9 +886,9 @@ class TplScript
        redef fun rendering do
                add "<script"
                for attr in attrs do add attr
-               add ">"
+               addn ">"
                render_content
-               add "</script>"
+               addn "</script>"
        end
 end
 
@@ -905,17 +905,17 @@ class TplPiwikScript
                if tracker_url.chars.last != '/' then tracker_url += "/"
                tracker_url = "://{tracker_url}".to_json
 
-               add "<!-- Piwik -->"
-               add "var _paq = _paq || [];"
-               add " _paq.push([\"trackPageView\"]);"
-               add " _paq.push([\"enableLinkTracking\"]);"
-               add "(function() \{"
-               add " var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + {tracker_url};"
-               add " _paq.push([\"setTrackerUrl\", u+\"piwik.php\"]);"
-               add " _paq.push([\"setSiteId\", {site_id}]);"
-               add " var d=document, g=d.createElement(\"script\"), s=d.getElementsByTagName(\"script\")[0]; g.type=\"text/javascript\";"
-               add " g.defer=true; g.async=true; g.src=u+\"piwik.js\"; s.parentNode.insertBefore(g,s);"
-               add "\})();"
+               addn "<!-- Piwik -->"
+               addn "var _paq = _paq || [];"
+               addn " _paq.push([\"trackPageView\"]);"
+               addn " _paq.push([\"enableLinkTracking\"]);"
+               addn "(function() \{"
+               addn " var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + {tracker_url};"
+               addn " _paq.push([\"setTrackerUrl\", u+\"piwik.php\"]);"
+               addn " _paq.push([\"setSiteId\", {site_id}]);"
+               addn " var d=document, g=d.createElement(\"script\"), s=d.getElementsByTagName(\"script\")[0]; g.type=\"text/javascript\";"
+               addn " g.defer=true; g.async=true; g.src=u+\"piwik.js\"; s.parentNode.insertBefore(g,s);"
+               addn "\})();"
        end
 end
 
index a2dda26..019d201 100644 (file)
@@ -32,6 +32,7 @@ import c
 import cpp
 import java
 import extra_java_files
+import objc
 
 redef class MModule
        # Does this module uses the FFI?
diff --git a/src/ffi/objc.nit b/src/ffi/objc.nit
new file mode 100644 (file)
index 0000000..9ab6f7f
--- /dev/null
@@ -0,0 +1,220 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+# FFI support for Objective-C
+#
+# Compiles all Objective-C code with clang. The user module must define
+# the framework used.
+#
+# This module is heavily based on the C++ FFI.
+module objc
+
+import extern_classes
+import c
+
+redef class FFILanguageAssignationPhase
+       # The Objective-C language visitor
+       var objc_language: FFILanguage = new ObjCLanguage(self)
+end
+
+redef class MModule
+       private var objc_file: nullable ObjCCompilationUnit = null
+end
+
+# The Objective-C langugage visitor
+class ObjCLanguage
+       super FFILanguage
+
+       redef fun identify_language(n) do return n.is_objc
+
+       redef fun compile_module_block(block, ecc, mmodule)
+       do
+               if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
+
+               if block.is_objc_header then
+                       mmodule.objc_file.header_custom.add block.location.as_line_pragma
+                       mmodule.objc_file.header_custom.add block.code
+               else if block.is_objc_body then
+                       mmodule.objc_file.body_custom.add block.location.as_line_pragma
+                       mmodule.objc_file.body_custom.add block.code
+               end
+       end
+
+       redef fun compile_extern_method(block, m, ecc, mmodule)
+       do
+               if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
+
+               var mpropdef = m.mpropdef
+               var recv_mtype = mpropdef.mclassdef.bound_mtype
+               var csignature = mpropdef.mproperty.build_csignature(
+                       recv_mtype, mmodule, "___impl", long_signature, from_objc_call_context)
+
+               var fc = new CFunction(csignature)
+               fc.decls.add block.location.as_line_pragma
+               fc.exprs.add block.code
+               mmodule.objc_file.add_exported_function fc
+       end
+
+       redef fun compile_extern_class(block, m, ecc, mmodule) do end
+
+       redef fun get_ftype(block, m) do return new ForeignObjCType(block.code)
+
+       redef fun compile_to_files(mmodule, compdir)
+       do
+               var objc_file = mmodule.objc_file
+               assert objc_file != null
+
+               # write .m and _m.h file
+               mmodule.objc_file.header_c_types.add """
+       #include "{{{mmodule.cname}}}._ffi.h"
+"""
+
+               var file = objc_file.write_to_files(mmodule, compdir)
+
+               # add compilation to makefile
+               mmodule.ffi_files.add file
+       end
+
+       redef fun compile_callback(callback, mmodule, mainmodule, ecc)
+       do
+               callback.compile_callback_to_objc(mmodule, mainmodule)
+       end
+end
+
+redef class AExternCodeBlock
+       # Is this Objective-C code?
+       fun is_objc : Bool do return language_name != null and
+               (language_name_lowered == "objc" or language_name_lowered.has_prefix("objc "))
+
+       # Is this Objective-C code for the body file?
+       fun is_objc_body : Bool do return language_name != null and
+               (language_name_lowered == "objc" or language_name_lowered == "objc body")
+
+       # Is this Objective-C code for the header file?
+       fun is_objc_header : Bool do return language_name != null and
+               (language_name_lowered == "objc header")
+end
+
+private class ObjCCompilationUnit
+       super CCompilationUnit
+
+       # Write this compilation unit to Objective-C source files
+       fun write_to_files(mmodule: MModule, compdir: String): ExternObjCFile
+       do
+               var base_name = "{mmodule.cname}._ffi"
+
+               var h_file = "{base_name}_m.h"
+               var guard = "{mmodule.cname.to_s.to_upper}_NIT_OBJC_H"
+               write_header_to_file(mmodule, compdir/h_file, new Array[String], guard)
+
+               var c_file = "{base_name}.m"
+               write_body_to_file(mmodule, compdir/c_file, ["\"{h_file}\""])
+
+               files.add compdir/c_file
+
+               mmodule.c_linker_options = "{mmodule.c_linker_options} -lobjc"
+
+               return new ExternObjCFile(compdir/c_file, mmodule)
+       end
+end
+
+# A Objective-C file
+class ExternObjCFile
+       super ExternFile
+
+       # Associated `MModule`
+       var mmodule: MModule
+
+       redef fun makefile_rule_name do return "{filename.basename(".m")}_m.o"
+       redef fun makefile_rule_content do
+               return "clang $(CFLAGS) -c {filename.basename("")} -o {makefile_rule_name}"
+       end
+       redef fun compiles_to_o_file do return true
+end
+
+# An Objective-C type
+class ForeignObjCType
+       super ForeignType
+
+       # Type name
+       var objc_type: String
+end
+
+redef class NitniCallback
+       # Compile this callback to be callable from Objective-C
+       fun compile_callback_to_objc(mmodule: MModule, mainmodule: MModule) do end
+end
+
+redef class MExplicitCall
+       redef fun compile_callback_to_objc(mmodule, mainmodule)
+       do
+               var mproperty = mproperty
+               assert mproperty isa MMethod
+
+               var objc_signature = mproperty.build_csignature(recv_mtype, mainmodule, null, short_signature, from_objc_call_context)
+               var ccall = mproperty.build_ccall(recv_mtype, mainmodule, null, long_signature, from_objc_call_context, null)
+               var fc = new CFunction(objc_signature)
+               fc.exprs.add ccall
+               mmodule.objc_file.add_local_function fc
+       end
+end
+
+# Calls withing Objective-C code
+private fun objc_call_context: ObjCCallContext do return once new ObjCCallContext
+
+# Calls from C to Objective-C
+private fun to_objc_call_context: ToObjCCallContext do return once new ToObjCCallContext
+
+# Calls from Objective-C to C
+private fun from_objc_call_context: FromObjCCallContext do return once new FromObjCCallContext
+
+private class ObjCCallContext
+       super CallContext
+
+       redef fun name_mtype(mtype)
+       do
+               if mtype isa MClassType then
+                       var ftype = mtype.mclass.ftype
+                       if ftype isa ForeignObjCType then
+                               return ftype.objc_type
+                       end
+               end
+
+               return mtype.cname
+       end
+end
+
+private class ToObjCCallContext
+       super ObjCCallContext
+
+       redef fun cast_to(mtype, name)
+       do
+               if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
+                       return "(void*)({name})"
+               else return name
+       end
+end
+
+private class FromObjCCallContext
+       super ObjCCallContext
+
+       redef fun cast_from(mtype, name)
+       do
+               if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
+                       return "({name_mtype(mtype)})({name})"
+               else return name
+       end
+end
index 28b9127..a035c8e 100644 (file)
@@ -73,7 +73,7 @@ private class SerializationPhasePreModel
                end
        end
 
-       private fun generate_serialization_method(nclassdef: AClassdef)
+       fun generate_serialization_method(nclassdef: AClassdef)
        do
                var npropdefs = nclassdef.n_propdefs
 
@@ -94,7 +94,7 @@ private class SerializationPhasePreModel
        end
 
        # Add a constructor to the automated nclassdef
-       private fun generate_deserialization_init(nclassdef: AClassdef)
+       fun generate_deserialization_init(nclassdef: AClassdef)
        do
                var npropdefs = nclassdef.n_propdefs
 
@@ -128,7 +128,7 @@ private class SerializationPhasePreModel
        end
 
        # Added to the abstract serialization service
-       private fun generate_deserialization_method(nmodule: AModule, nclassdefs: Array[AStdClassdef])
+       fun generate_deserialization_method(nmodule: AModule, nclassdefs: Array[AStdClassdef])
        do
                var code = new Array[String]
 
index aa6046b..73b5dbe 100755 (executable)
@@ -4,8 +4,8 @@ set -x
 # Check c_src is up-to-date
 make -C ../c_src
 
-# Compile nitg
-time ../c_src/nitg nitg.nit
+# Compile nitc
+time ../c_src/nitg nitc.nit
 
 # delete old c_src
 rm -rf ../c_src
index bf1a365..5d24e6c 100644 (file)
@@ -21,6 +21,7 @@ import literal
 import semantize
 private import parser::tables
 import mixin
+import primitive_types
 
 redef class ToolContext
        # --discover-call-trace
@@ -71,11 +72,13 @@ class NaiveInterpreter
 
        init
        do
-               self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
-               init_instance_primitive(self.true_instance)
-               self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
-               init_instance_primitive(self.false_instance)
-               self.null_instance = new MutableInstance(mainmodule.model.null_type)
+               if mainmodule.model.get_mclasses_by_name("Bool") != null then
+                       self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
+                       init_instance_primitive(self.true_instance)
+                       self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
+                       init_instance_primitive(self.false_instance)
+               end
+               self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null)
        end
 
        # Starts the interpreter on the main module of a program
@@ -100,13 +103,14 @@ class NaiveInterpreter
        # Subtype test in the context of the mainmodule
        fun is_subtype(sub, sup: MType): Bool
        do
-               return sub.is_subtype(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType), sup)
+               return sub.is_subtype(self.mainmodule, current_receiver_class, sup)
        end
 
+       # Get a primitive method in the context of the main module
        fun force_get_primitive_method(name: String, recv: MType): MMethod
        do
                assert recv isa MClassType
-               return self.modelbuilder.force_get_primitive_method(self.frame.current_node, name, recv.mclass, self.mainmodule)
+               return self.modelbuilder.force_get_primitive_method(current_node, name, recv.mclass, self.mainmodule)
        end
 
        # Is a return executed?
@@ -238,6 +242,8 @@ class NaiveInterpreter
                return res
        end
 
+       # Return a instance associated to a primitive class
+       # Current primitive classes are `Int`, `Bool`, and `String`
        fun value_instance(object: Object): Instance
        do
                if object isa Int then
@@ -289,13 +295,27 @@ class NaiveInterpreter
                return b.to_s
        end
 
+       # The current node, used to print errors, debug and stack-traces
+       fun current_node: nullable ANode
+       do
+               if frames.is_empty then return null
+               return frames.first.current_node
+       end
+
+       # The dynamic type of the current `self`
+       fun current_receiver_class: MClassType
+       do
+               return frames.first.arguments.first.mtype.as(MClassType)
+       end
+
        # Exit the program with a message
        fun fatal(message: String)
        do
-               if frames.is_empty then
+               var node = current_node
+               if node == null then
                        print message
                else
-                       self.frame.current_node.fatal(self, message)
+                       node.fatal(self, message)
                end
                exit(1)
        end
@@ -303,10 +323,11 @@ class NaiveInterpreter
        # Debug on the current node
        fun debug(message: String)
        do
-               if frames.is_empty then
+               var node = current_node
+               if node == null then
                        print message
                else
-                       self.frame.current_node.debug(message)
+                       node.debug(message)
                end
        end
 
@@ -403,7 +424,7 @@ class NaiveInterpreter
                end
        end
 
-       # Generate type checks in the C code to check covariant parameters
+       # Execute type checks of covariant parameters
        fun parameter_check(node: ANode, mpropdef: MMethodDef, args: Array[Instance])
        do
                var msignature = mpropdef.msignature
@@ -415,6 +436,8 @@ class NaiveInterpreter
                        var origmtype =  mpropdef.mproperty.intro.msignature.mparameters[i].mtype
                        if not origmtype.need_anchor then continue
 
+                       #print "{mpropdef}: {mpropdef.mproperty.intro.msignature.mparameters[i]}"
+
                        # get the parameter type
                        var mtype = msignature.mparameters[i].mtype
                        var anchor = args.first.mtype.as(MClassType)
@@ -458,7 +481,7 @@ class NaiveInterpreter
                                        self.send(p, args)
                                else if p isa MAttribute then
                                        assert recv isa MutableInstance
-                                       recv.attributes[p] = arguments[i]
+                                       write_attribute(p, recv, arguments[i])
                                        i += 1
                                else abort
                        end
@@ -546,10 +569,10 @@ class NaiveInterpreter
                return mainmodule.get_primitive_class(name)
        end
 
-       # This function determine the correct type according the reciever of the current definition (self).
+       # This function determines the correct type according to the receiver of the current propdef (self).
        fun unanchor_type(mtype: MType): MType
        do
-               return mtype.anchor_to(self.mainmodule, self.frame.arguments.first.mtype.as(MClassType))
+               return mtype.anchor_to(self.mainmodule, current_receiver_class)
        end
 
        # Placebo instance used to mark internal error result when `null` already have a meaning.
@@ -584,7 +607,7 @@ abstract class Instance
 
        # The real value encapsulated if the instance is primitive.
        # Else aborts.
-       fun val: Object do abort
+       fun val: nullable Object do abort
 end
 
 # A instance with attribute (standards objects)
@@ -597,7 +620,7 @@ end
 
 # Special instance to handle primitives values (int, bool, etc.)
 # The trick it just to encapsulate the <<real>> value
-class PrimitiveInstance[E: Object]
+class PrimitiveInstance[E]
        super Instance
 
        # The real value encapsulated
@@ -612,17 +635,17 @@ class PrimitiveInstance[E: Object]
 
        redef fun ==(o)
        do
-               if not o isa PrimitiveInstance[Object] then return false
+               if not o isa PrimitiveInstance[nullable Object] then return false
                return self.val == o.val
        end
 
        redef fun eq_is(o)
        do
-               if not o isa PrimitiveInstance[Object] then return false
+               if not o isa PrimitiveInstance[nullable Object] then return false
                return self.val.is_same_instance(o.val)
        end
 
-       redef fun to_s do return "{mtype}#{val.object_id}({val})"
+       redef fun to_s do return "{mtype}#{val.object_id}({val or else "null"})"
 
        redef fun to_i do return val.as(Int)
 
@@ -776,6 +799,12 @@ redef class AMethPropdef
                else if pname == "exit" then
                        exit(args[1].to_i)
                        abort
+               else if pname == "buffer_mode_full" then
+                       return v.int_instance(sys.buffer_mode_full)
+               else if pname == "buffer_mode_line" then
+                       return v.int_instance(sys.buffer_mode_line)
+               else if pname == "buffer_mode_none" then
+                       return v.int_instance(sys.buffer_mode_none)
                else if pname == "sys" then
                        return v.mainobj
                else if cname == "Int" then
@@ -959,6 +988,14 @@ redef class AMethPropdef
                        else if pname == "atof" then
                                return v.float_instance(recvval.to_f)
                        end
+               else if cname == "String" then
+                       var cs = v.send(v.force_get_primitive_method("to_cstring", args.first.mtype), [args.first])
+                       var str = cs.val.to_s
+                       if pname == "files" then
+                               var res = new Array[Instance]
+                               for f in str.files do res.add v.string_instance(f)
+                               return v.array_instance(res, v.get_primitive_class("String").mclass_type)
+                       end
                else if pname == "calloc_string" then
                        return v.native_string_instance("!" * args[1].to_i)
                else if cname == "NativeArray" then
@@ -980,48 +1017,56 @@ redef class AMethPropdef
                        else if pname == "length" then
                                return v.int_instance(recvval.length)
                        else if pname == "copy_to" then
-                               recvval.copy(0, args[2].to_i, args[1].val.as(Array[Instance]), 0)
+                               recvval.copy_to(0, args[2].to_i, args[1].val.as(Array[Instance]), 0)
                                return null
                        end
                else if cname == "NativeFile" then
                        if pname == "native_stdout" then
-                               var instance = new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdout)
+                               var inst = new PrimitiveNativeFile.native_stdout
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "native_stdin" then
-                               var instance = new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdin)
+                               var inst = new PrimitiveNativeFile.native_stdin
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "native_stderr" then
-                               var instance = new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stderr)
+                               var inst = new PrimitiveNativeFile.native_stderr
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "io_open_read" then
                                var a1 = args[1].val.as(Buffer)
-                               var instance = new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, new IFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_read(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "io_open_write" then
                                var a1 = args[1].val.as(Buffer)
-                               var instance = new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, new OFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_write(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        end
                        var recvval = args.first.val
                        if pname == "io_write" then
                                var a1 = args[1].val.as(Buffer)
-                               recvval.as(OStream).write(a1.substring(0, args[2].to_i).to_s)
-                               return args[2]
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_write(a1.to_cstring, args[2].to_i))
                        else if pname == "io_read" then
-                               var str = recvval.as(IStream).read(args[2].to_i)
                                var a1 = args[1].val.as(Buffer)
-                               new FlatBuffer.from(str).copy(0, str.length, a1.as(FlatBuffer), 0)
-                               return v.int_instance(str.length)
+                               var ns = new NativeString(a1.length)
+                               var len = recvval.as(PrimitiveNativeFile).io_read(ns, args[2].to_i)
+                               a1.clear
+                               a1.append(ns.to_s_with_length(len))
+                               return v.int_instance(len)
+                       else if pname == "flush" then
+                               recvval.as(PrimitiveNativeFile).flush
+                               return null
                        else if pname == "io_close" then
-                               recvval.as(IOS).close
-                               return v.int_instance(0)
-                       else if pname == "address_is_null" then
-                               return v.false_instance
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_close)
+                       else if pname == "set_buffering_type" then
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).set_buffering_type(args[1].to_i, args[2].to_i))
                        end
                else if pname == "calloc_array" then
                        var recvtype = args.first.mtype.as(MClassType)
@@ -1067,19 +1112,16 @@ redef class AMethPropdef
                else if pname == "errno" then
                        return v.int_instance(sys.errno)
                else if pname == "address_is_null" then
+                       var recv = args[0]
+                       if recv isa PrimitiveInstance[PrimitiveNativeFile] then
+                               return v.bool_instance(recv.val.address_is_null)
+                       end
                        return v.false_instance
                end
                return v.error_instance
        end
 end
 
-redef class AbstractArray[E]
-       fun copy(start: Int, len: Int, dest: AbstractArray[E], new_start: Int)
-       do
-               self.copy_to(start, len, dest, new_start)
-       end
-end
-
 redef class AAttrPropdef
        redef fun call(v, mpropdef, args)
        do
diff --git a/src/interpreter/primitive_types.nit b/src/interpreter/primitive_types.nit
new file mode 100644 (file)
index 0000000..8fe483d
--- /dev/null
@@ -0,0 +1,61 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT. This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. You can modify it is you want, provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You are allowed to redistribute it and sell it, alone or is a part of
+# another product.
+
+# Encapsulates all primitive data types of nit
+#
+# Ensures that the use in the interpreter is independant of the
+# underlying implementation and that the services are semantically correct.
+module primitive_types
+
+intrude import standard::file
+
+# Wrapper for `NativeFile`
+class PrimitiveNativeFile
+
+       var file: FStream
+
+       init native_stdin do
+               file = new IFStream.from_fd(0)
+       end
+
+       init native_stdout do
+               file = new OFStream.from_fd(1)
+       end
+
+       init native_stderr do
+               file = new OFStream.from_fd(2)
+       end
+
+       init io_open_read(path: String) do
+               file = new IFStream.open(path.to_s)
+       end
+
+       init io_open_write(path: String) do
+               file = new OFStream.open(path.to_s)
+       end
+
+       fun address_is_null: Bool do return file._file.address_is_null
+
+       fun io_read(buf: NativeString, len: Int): Int do return file._file.io_read(buf, len)
+
+       fun io_write(buf: NativeString, len: Int): Int do return file._file.io_write(buf, len)
+
+       fun io_close: Int do return file._file.io_close
+
+       fun file_stat: FileStat do return file._file.file_stat
+
+       fun fileno: Int do return file._file.fileno
+
+       fun flush: Int do return file._file.flush
+
+       fun set_buffering_type(size, mode: Int): Int do
+               return file._file.set_buffering_type(size, mode)
+       end
+end
diff --git a/src/loader.nit b/src/loader.nit
new file mode 100644 (file)
index 0000000..14ec4fe
--- /dev/null
@@ -0,0 +1,607 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Loading of Nit source files
+module loader
+
+import modelbuilder_base
+
+redef class ToolContext
+       # Option --path
+       var opt_path = new OptionArray("Set include path for loaders (may be used more than once)", "-I", "--path")
+
+       # Option --only-metamodel
+       var opt_only_metamodel = new OptionBool("Stop after meta-model processing", "--only-metamodel")
+
+       # Option --only-parse
+       var opt_only_parse = new OptionBool("Only proceed to parse step of loaders", "--only-parse")
+
+       redef init
+       do
+               super
+               option_context.add_option(opt_path, opt_only_parse, opt_only_metamodel)
+       end
+end
+
+redef class ModelBuilder
+       redef init
+       do
+               super
+
+               # Setup the paths value
+               paths.append(toolcontext.opt_path.value)
+
+               var path_env = "NIT_PATH".environ
+               if not path_env.is_empty then
+                       paths.append(path_env.split_with(':'))
+               end
+
+               var nit_dir = toolcontext.nit_dir
+               var libname = "{nit_dir}/lib"
+               if libname.file_exists then paths.add(libname)
+       end
+
+       # Load a bunch of modules.
+       # `modules` can contains filenames or module names.
+       # Imported modules are automatically loaded and modelized.
+       # The result is the corresponding model elements.
+       # Errors and warnings are printed with the toolcontext.
+       #
+       # Note: class and property model elements are not analysed.
+       fun parse(modules: Sequence[String]): Array[MModule]
+       do
+               var time0 = get_time
+               # Parse and recursively load
+               self.toolcontext.info("*** PARSE ***", 1)
+               var mmodules = new ArraySet[MModule]
+               for a in modules do
+                       var nmodule = self.load_module(a)
+                       if nmodule == null then continue # Skip error
+                       # Load imported module
+                       build_module_importation(nmodule)
+
+                       mmodules.add(nmodule.mmodule.as(not null))
+               end
+               var time1 = get_time
+               self.toolcontext.info("*** END PARSE: {time1-time0} ***", 2)
+
+               self.toolcontext.check_errors
+
+               if toolcontext.opt_only_parse.value then
+                       self.toolcontext.info("*** ONLY PARSE...", 1)
+                       exit(0)
+               end
+
+               return mmodules.to_a
+       end
+
+       # The list of directories to search for top level modules
+       # The list is initially set with:
+       #
+       #   * the toolcontext --path option
+       #   * the NIT_PATH environment variable
+       #   * `toolcontext.nit_dir`
+       # Path can be added (or removed) by the client
+       var paths = new Array[String]
+
+       # Like (and used by) `get_mmodule_by_name` but just return the ModulePath
+       fun search_mmodule_by_name(anode: nullable ANode, mgroup: nullable MGroup, name: String): nullable ModulePath
+       do
+               # First, look in groups
+               var c = mgroup
+               while c != null do
+                       var dirname = c.filepath
+                       if dirname == null then break # virtual group
+                       if dirname.has_suffix(".nit") then break # singleton project
+
+                       # Second, try the directory to find a file
+                       var try_file = dirname + "/" + name + ".nit"
+                       if try_file.file_exists then
+                               var res = self.identify_file(try_file.simplify_path)
+                               assert res != null
+                               return res
+                       end
+
+                       # Third, try if the requested module is itself a group
+                       try_file = dirname + "/" + name + "/" + name + ".nit"
+                       if try_file.file_exists then
+                               var res = self.identify_file(try_file.simplify_path)
+                               assert res != null
+                               return res
+                       end
+
+                       c = c.parent
+               end
+
+               # Look at some known directories
+               var lookpaths = self.paths
+
+               # Look in the directory of the group project also (even if not explicitly in the path)
+               if mgroup != null then
+                       # path of the root group
+                       var dirname = mgroup.mproject.root.filepath
+                       if dirname != null then
+                               dirname = dirname.join_path("..").simplify_path
+                               if not lookpaths.has(dirname) and dirname.file_exists then
+                                       lookpaths = lookpaths.to_a
+                                       lookpaths.add(dirname)
+                               end
+                       end
+               end
+
+               var candidate = search_module_in_paths(anode.hot_location, name, lookpaths)
+
+               if candidate == null then
+                       if mgroup != null then
+                               error(anode, "Error: cannot find module {name} from {mgroup.name}. tried {lookpaths.join(", ")}")
+                       else
+                               error(anode, "Error: cannot find module {name}. tried {lookpaths.join(", ")}")
+                       end
+                       return null
+               end
+               return candidate
+       end
+
+       # Get a module by its short name; if required, the module is loaded, parsed and its hierarchies computed.
+       # If `mgroup` is set, then the module search starts from it up to the top level (see `paths`);
+       # if `mgroup` is null then the module is searched in the top level only.
+       # If no module exists or there is a name conflict, then an error on `anode` is displayed and null is returned.
+       fun get_mmodule_by_name(anode: nullable ANode, mgroup: nullable MGroup, name: String): nullable MModule
+       do
+               var path = search_mmodule_by_name(anode, mgroup, name)
+               if path == null then return null # Forward error
+               var res = self.load_module(path.filepath)
+               if res == null then return null # Forward error
+               # Load imported module
+               build_module_importation(res)
+               return res.mmodule.as(not null)
+       end
+
+       # Search a module `name` from path `lookpaths`.
+       # If found, the path of the file is returned
+       private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable ModulePath
+       do
+               var candidate: nullable String = null
+               for dirname in lookpaths do
+                       var try_file = (dirname + "/" + name + ".nit").simplify_path
+                       if try_file.file_exists then
+                               if candidate == null then
+                                       candidate = try_file
+                               else if candidate != try_file then
+                                       # try to disambiguate conflicting modules
+                                       var abs_candidate = module_absolute_path(candidate)
+                                       var abs_try_file = module_absolute_path(try_file)
+                                       if abs_candidate != abs_try_file then
+                                               toolcontext.error(location, "Error: conflicting module file for {name}: {candidate} {try_file}")
+                                       end
+                               end
+                       end
+                       try_file = (dirname + "/" + name + "/" + name + ".nit").simplify_path
+                       if try_file.file_exists then
+                               if candidate == null then
+                                       candidate = try_file
+                               else if candidate != try_file then
+                                       # try to disambiguate conflicting modules
+                                       var abs_candidate = module_absolute_path(candidate)
+                                       var abs_try_file = module_absolute_path(try_file)
+                                       if abs_candidate != abs_try_file then
+                                               toolcontext.error(location, "Error: conflicting module file for {name}: {candidate} {try_file}")
+                                       end
+                               end
+                       end
+               end
+               if candidate == null then return null
+               return identify_file(candidate)
+       end
+
+       # Cache for `identify_file` by realpath
+       private var identified_files_by_path = new HashMap[String, nullable ModulePath]
+
+       # All the currently identified modules.
+       # See `identify_file`.
+       var identified_files = new Array[ModulePath]
+
+       # Identify a source file
+       # Load the associated project and groups if required
+       #
+       # Silently return `null` if `path` is not a valid module path.
+       fun identify_file(path: String): nullable ModulePath
+       do
+               # special case for not a nit file
+               if path.file_extension != "nit" then
+                       # search dirless files in known -I paths
+                       if path.dirname == "" then
+                               var res = search_module_in_paths(null, path, self.paths)
+                               if res != null then return res
+                       end
+
+                       # Found nothing? maybe it is a group...
+                       var candidate = null
+                       if path.file_exists then
+                               var mgroup = get_mgroup(path)
+                               if mgroup != null then
+                                       var owner_path = mgroup.filepath.join_path(mgroup.name + ".nit")
+                                       if owner_path.file_exists then candidate = owner_path
+                               end
+                       end
+
+                       if candidate == null then
+                               return null
+                       end
+                       path = candidate
+               end
+
+               # Fast track, the path is already known
+               var pn = path.basename(".nit")
+               var rp = module_absolute_path(path)
+               if identified_files_by_path.has_key(rp) then return identified_files_by_path[rp]
+
+               # Search for a group
+               var mgrouppath = path.join_path("..").simplify_path
+               var mgroup = get_mgroup(mgrouppath)
+
+               if mgroup == null then
+                       # singleton project
+                       var mproject = new MProject(pn, model)
+                       mgroup = new MGroup(pn, mproject, null) # same name for the root group
+                       mgroup.filepath = path
+                       mproject.root = mgroup
+                       toolcontext.info("found project `{pn}` at {path}", 2)
+               end
+
+               var res = new ModulePath(pn, path, mgroup)
+               mgroup.module_paths.add(res)
+
+               identified_files_by_path[rp] = res
+               identified_files.add(res)
+               return res
+       end
+
+       # Groups by path
+       private var mgroups = new HashMap[String, nullable MGroup]
+
+       # Return the mgroup associated to a directory path.
+       # If the directory is not a group null is returned.
+       fun get_mgroup(dirpath: String): nullable MGroup
+       do
+               var rdp = module_absolute_path(dirpath)
+               if mgroups.has_key(rdp) then
+                       return mgroups[rdp]
+               end
+
+               # Hack, a group is determined by:
+               # * the presence of a honomymous nit file
+               # * the fact that the directory is named `src`
+               var pn = rdp.basename(".nit")
+               var mp = dirpath.join_path(pn + ".nit").simplify_path
+
+               var dirpath2 = dirpath
+               if not mp.file_exists then
+                       if pn == "src" then
+                               # With a src directory, the group name is the name of the parent directory
+                               dirpath2 = rdp.dirname
+                               pn = dirpath2.basename("")
+                       else
+                               return null
+                       end
+               end
+
+               # check parent directory
+               var parentpath = dirpath.join_path("..").simplify_path
+               var parent = get_mgroup(parentpath)
+
+               var mgroup
+               if parent == null then
+                       # no parent, thus new project
+                       var mproject = new MProject(pn, model)
+                       mgroup = new MGroup(pn, mproject, null) # same name for the root group
+                       mproject.root = mgroup
+                       toolcontext.info("found project `{mproject}` at {dirpath}", 2)
+               else
+                       mgroup = new MGroup(pn, parent.mproject, parent)
+                       toolcontext.info("found sub group `{mgroup.full_name}` at {dirpath}", 2)
+               end
+               var readme = dirpath2.join_path("README.md")
+               if not readme.file_exists then readme = dirpath2.join_path("README")
+               if readme.file_exists then
+                       var mdoc = new MDoc
+                       var s = new IFStream.open(readme)
+                       while not s.eof do
+                               mdoc.content.add(s.read_line)
+                       end
+                       mgroup.mdoc = mdoc
+                       mdoc.original_mentity = mgroup
+               end
+               mgroup.filepath = dirpath
+               mgroups[rdp] = mgroup
+               return mgroup
+       end
+
+       # Force the identification of all ModulePath of the group and sub-groups.
+       fun visit_group(mgroup: MGroup) do
+               var p = mgroup.filepath
+               for f in p.files do
+                       var fp = p/f
+                       var g = get_mgroup(fp)
+                       if g != null then visit_group(g)
+                       identify_file(fp)
+               end
+       end
+
+       # Transform relative paths (starting with '../') into absolute paths
+       private fun module_absolute_path(path: String): String do
+               return getcwd.join_path(path).simplify_path
+       end
+
+       # Try to load a module AST using a path.
+       # Display an error if there is a problem (IO / lexer / parser) and return null
+       fun load_module_ast(filename: String): nullable AModule
+       do
+               if filename.file_extension != "nit" then
+                       self.toolcontext.error(null, "Error: file {filename} is not a valid nit module.")
+                       return null
+               end
+               if not filename.file_exists then
+                       self.toolcontext.error(null, "Error: file {filename} not found.")
+                       return null
+               end
+
+               self.toolcontext.info("load module {filename}", 2)
+
+               # Load the file
+               var file = new IFStream.open(filename)
+               var lexer = new Lexer(new SourceFile(filename, file))
+               var parser = new Parser(lexer)
+               var tree = parser.parse
+               file.close
+
+               # Handle lexer and parser error
+               var nmodule = tree.n_base
+               if nmodule == null then
+                       var neof = tree.n_eof
+                       assert neof isa AError
+                       error(neof, neof.message)
+                       return null
+               end
+
+               return nmodule
+       end
+
+       # Try to load a module using a path.
+       # Display an error if there is a problem (IO / lexer / parser) and return null.
+       # Note: usually, you do not need this method, use `get_mmodule_by_name` instead.
+       #
+       # The MModule is created however, the importation is not performed,
+       # therefore you should call `build_module_importation`.
+       fun load_module(filename: String): nullable AModule
+       do
+               # Look for the module
+               var file = identify_file(filename)
+               if file == null then
+                       toolcontext.error(null, "Error: cannot find module `{filename}`.")
+                       return null
+               end
+
+               # Already known and loaded? then return it
+               var mmodule = file.mmodule
+               if mmodule != null then
+                       return mmodule2nmodule[mmodule]
+               end
+
+               # Load it manually
+               var nmodule = load_module_ast(file.filepath)
+               if nmodule == null then return null # forward error
+
+               # build the mmodule and load imported modules
+               mmodule = build_a_mmodule(file.mgroup, file.name, nmodule)
+
+               if mmodule == null then return null # forward error
+
+               # Update the file information
+               file.mmodule = mmodule
+
+               return nmodule
+       end
+
+       # Injection of a new module without source.
+       # Used by the interpreter.
+       fun load_rt_module(parent: nullable MModule, nmodule: AModule, mod_name: String): nullable AModule
+       do
+               # Create the module
+
+               var mgroup = null
+               if parent != null then mgroup = parent.mgroup
+               var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
+               nmodule.mmodule = mmodule
+               nmodules.add(nmodule)
+               self.mmodule2nmodule[mmodule] = nmodule
+
+               if parent!= null then
+                       var imported_modules = new Array[MModule]
+                       imported_modules.add(parent)
+                       mmodule.set_visibility_for(parent, intrude_visibility)
+                       mmodule.set_imported_mmodules(imported_modules)
+               else
+                       build_module_importation(nmodule)
+               end
+
+               return nmodule
+       end
+
+       # Visit the AST and create the `MModule` object
+       private fun build_a_mmodule(mgroup: nullable MGroup, mod_name: String, nmodule: AModule): nullable MModule
+       do
+               # Check the module name
+               var decl = nmodule.n_moduledecl
+               if decl == null then
+                       #warning(nmodule, "Warning: Missing 'module' keyword") #FIXME: NOT YET FOR COMPATIBILITY
+               else
+                       var decl_name = decl.n_name.n_id.text
+                       if decl_name != mod_name then
+                               error(decl.n_name, "Error: module name missmatch; declared {decl_name} file named {mod_name}")
+                       end
+               end
+
+               # Create the module
+               var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
+               nmodule.mmodule = mmodule
+               nmodules.add(nmodule)
+               self.mmodule2nmodule[mmodule] = nmodule
+
+               if decl != null then
+                       var ndoc = decl.n_doc
+                       if ndoc != null then
+                               var mdoc = ndoc.to_mdoc
+                               mmodule.mdoc = mdoc
+                               mdoc.original_mentity = mmodule
+                       else
+                               advice(decl, "missing-doc", "Documentation warning: Undocumented module `{mmodule}`")
+                       end
+               end
+
+               return mmodule
+       end
+
+       # Analyze the module importation and fill the module_importation_hierarchy
+       #
+       # Unless you used `load_module`, the importation is already done and this method does a no-op.
+       fun build_module_importation(nmodule: AModule)
+       do
+               if nmodule.is_importation_done then return
+               nmodule.is_importation_done = true
+               var mmodule = nmodule.mmodule.as(not null)
+               var stdimport = true
+               var imported_modules = new Array[MModule]
+               for aimport in nmodule.n_imports do
+                       stdimport = false
+                       if not aimport isa AStdImport then
+                               continue
+                       end
+                       var mgroup = mmodule.mgroup
+                       if aimport.n_name.n_quad != null then mgroup = null # Start from top level
+                       for grp in aimport.n_name.n_path do
+                               var path = search_mmodule_by_name(grp, mgroup, grp.text)
+                               if path == null then return # Skip error
+                               mgroup = path.mgroup
+                       end
+                       var mod_name = aimport.n_name.n_id.text
+                       var sup = self.get_mmodule_by_name(aimport.n_name, mgroup, mod_name)
+                       if sup == null then continue # Skip error
+                       aimport.mmodule = sup
+                       imported_modules.add(sup)
+                       var mvisibility = aimport.n_visibility.mvisibility
+                       if mvisibility == protected_visibility then
+                               error(aimport.n_visibility, "Error: only properties can be protected.")
+                               return
+                       end
+                       if sup == mmodule then
+                               error(aimport.n_name, "Error: Dependency loop in module {mmodule}.")
+                       end
+                       if sup.in_importation < mmodule then
+                               error(aimport.n_name, "Error: Dependency loop between modules {mmodule} and {sup}.")
+                               return
+                       end
+                       mmodule.set_visibility_for(sup, mvisibility)
+               end
+               if stdimport then
+                       var mod_name = "standard"
+                       var sup = self.get_mmodule_by_name(nmodule, null, mod_name)
+                       if sup != null then # Skip error
+                               imported_modules.add(sup)
+                               mmodule.set_visibility_for(sup, public_visibility)
+                       end
+               end
+               self.toolcontext.info("{mmodule} imports {imported_modules.join(", ")}", 3)
+               mmodule.set_imported_mmodules(imported_modules)
+
+               # Force standard to be public if imported
+               for sup in mmodule.in_importation.greaters do
+                       if sup.name == "standard" then
+                               mmodule.set_visibility_for(sup, public_visibility)
+                       end
+               end
+
+               # TODO: Correctly check for useless importation
+               # It is even doable?
+               var directs = mmodule.in_importation.direct_greaters
+               for nim in nmodule.n_imports do
+                       if not nim isa AStdImport then continue
+                       var im = nim.mmodule
+                       if im == null then continue
+                       if directs.has(im) then continue
+                       # This generates so much noise that it is simpler to just comment it
+                       #warning(nim, "Warning: possible useless importation of {im}")
+               end
+       end
+
+       # All the loaded modules
+       var nmodules = new Array[AModule]
+
+       # Register the nmodule associated to each mmodule
+       # FIXME: why not refine the `MModule` class with a nullable attribute?
+       var mmodule2nmodule = new HashMap[MModule, AModule]
+end
+
+# File-system location of a module (file) that is identified but not always loaded.
+class ModulePath
+       # The name of the module
+       # (it's the basename of the filepath)
+       var name: String
+
+       # The human path of the module
+       var filepath: String
+
+       # The group (and the project) of the possible module
+       var mgroup: MGroup
+
+       # The loaded module (if any)
+       var mmodule: nullable MModule = null
+
+       redef fun to_s do return filepath
+end
+
+redef class MGroup
+       # Modules paths associated with the group
+       var module_paths = new Array[ModulePath]
+
+       # Is the group interesting for a final user?
+       #
+       # Groups are mandatory in the model but for simple projects they are not
+       # always interesting.
+       #
+       # A interesting group has, at least, one of the following true:
+       #
+       # * it has 2 modules or more
+       # * it has a subgroup
+       # * it has a documentation
+       fun is_interesting: Bool
+       do
+               return module_paths.length > 1 or mmodules.length > 1 or not in_nesting.direct_smallers.is_empty or mdoc != null
+       end
+
+end
+
+redef class AStdImport
+       # The imported module once determined
+       var mmodule: nullable MModule = null
+end
+
+redef class AModule
+       # The associated MModule once build by a `ModelBuilder`
+       var mmodule: nullable MModule
+       # Flag that indicate if the importation is already completed
+       var is_importation_done: Bool = false
+end
index 6c63925..1ee1db2 100644 (file)
@@ -102,7 +102,8 @@ private class MendelMetricsPhase
                end
 
                if csv then
-                       var csvh = new CSVDocument
+                       var csvh = new CsvDocument
+                       csvh.format = new CsvFormat('"', ';', "\n")
                        csvh.header = ["povr", "ovr", "pext", "ext", "pspe", "spe", "prep", "rep", "eq"]
                        for mclass in mclasses do
                                var povr = mclass.is_pure_overrider(vis).object_id
@@ -114,7 +115,7 @@ private class MendelMetricsPhase
                                var prep = mclass.is_pure_replacer(vis).object_id
                                var rep = mclass.is_replacer(vis).object_id
                                var eq = mclass.is_equal(vis).object_id
-                               csvh.add_line(povr, ovr, pext, ext, pspe, spe, prep, rep, eq)
+                               csvh.add_record(povr, ovr, pext, ext, pspe, spe, prep, rep, eq)
                        end
                        csvh.save("{out}/inheritance_behaviour.csv")
                end
index b55e1b8..c51e656 100644 (file)
@@ -367,8 +367,10 @@ class MetricSet
        end
 
        # Export the metric set in CSV format
-       fun to_csv: CSVDocument do
-               var csv = new CSVDocument
+       fun to_csv: CsvDocument do
+               var csv = new CsvDocument
+
+               csv.format = new CsvFormat('"', ';', "\n")
 
                # set csv headers
                csv.header.add("entry")
@@ -390,7 +392,7 @@ class MetricSet
                                        line.add("n/a")
                                end
                        end
-                       csv.lines.add(line)
+                       csv.records.add(line)
                end
                return csv
        end
index 7734983..de71f37 100755 (executable)
@@ -1,9 +1,9 @@
 #!/bin/sh
 
-# Regeneration of c_src from the current nitg
+# Regeneration of c_src from the current nitc
 
 rm -r ../c_src
-./nitg nith.nit --semi-global --compile-dir ../c_src --output ../c_src/nitg --no-cc
+./nitc nith.nit --semi-global --compile-dir ../c_src --output ../c_src/nitg --no-cc
 
 # Remove old compilation flags
 sed -i -e 's/OLDNITCOPT=.*/OLDNITCOPT=/' Makefile
index a92d065..c634815 100644 (file)
@@ -27,12 +27,6 @@ redef class Model
        # All known modules
        var mmodules = new Array[MModule]
 
-       # placebo for old module nesting hierarchy.
-       # where mainmodule < mainmodule::nestedmodule
-       #
-       # TODO REMOVE, rely on mgroup instead
-       var mmodule_nesting_hierarchy = new POSet[MModule]
-
        # Full module importation hierarchy including private or nested links.
        var mmodule_importation_hierarchy = new POSet[MModule]
 
@@ -91,12 +85,6 @@ class MModule
        # Alias for `name`
        redef fun to_s do return self.name
 
-       # placebo for old module nesting hierarchy
-       # The view of the module in the `model.mmodule_nesting_hierarchy`
-       #
-       # TODO REMOVE, rely on mgroup instead
-       var in_nesting: POSetElement[MModule] is noinit
-
        # The view of the module in the `model.mmodule_importation_hierarchy`
        var in_importation: POSetElement[MModule] is noinit
 
@@ -117,7 +105,6 @@ class MModule
        do
                model.mmodules_by_name.add_one(name, self)
                model.mmodules.add(self)
-               self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
                if mgroup != null then
                        mgroup.mmodules.add(self)
                        if mgroup.name == name then
@@ -127,17 +114,9 @@ class MModule
                        # placebo for old module nesting hierarchy
                        var direct_owner = mgroup.default_mmodule
                        if direct_owner == self then
-                               # The module is the new owner of its own group, thus adopt the other modules
-                               for m in mgroup.mmodules do
-                                       if m == self then continue
-                                       model.mmodule_nesting_hierarchy.add_edge(self, m)
-                               end
                                # The potential owner is the default_mmodule of the parent group
                                if mgroup.parent != null then direct_owner = mgroup.parent.default_mmodule
                        end
-                       if direct_owner != self and direct_owner != null then
-                               model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
-                       end
                end
                self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
        end
index 899f023..9c7a877 100644 (file)
@@ -251,9 +251,13 @@ redef class MModule
        do
                var cla = self.model.get_mclasses_by_name(name)
                if cla == null then
-                       if name == "Bool" then
+                       if name == "Bool" and self.model.get_mclasses_by_name("Object") != null then
+                               # Bool is injected because it is needed by engine to code the result
+                               # of the implicit casts.
                                var c = new MClass(self, name, null, enum_kind, public_visibility)
                                var cladef = new MClassDef(self, c.mclass_type, new Location(null, 0,0,0,0))
+                               cladef.set_supertypes([object_type])
+                               cladef.add_in_hierarchy
                                return c
                        end
                        print("Fatal Error: no primitive class {name}")
index 7af444d..7302619 100644 (file)
@@ -163,44 +163,6 @@ redef class MModule
                return mclasses
        end
 
-       fun in_nesting_intro_mclasses(min_visibility: MVisibility): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mmodule in in_nesting.greaters do
-                       for mclass in mmodule.filter_intro_mclasses(min_visibility) do
-                               if mclass.visibility < min_visibility then continue
-                               res.add mclass
-                       end
-               end
-               return res
-       end
-
-       fun in_nesting_redef_mclasses(min_visibility: MVisibility): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mmodule in self.in_nesting.greaters do
-                       for mclass in mmodule.filter_redef_mclasses(min_visibility) do
-                               if mclass.visibility < min_visibility then continue
-                               res.add mclass
-                       end
-               end
-               return res
-       end
-
-       fun in_nesting_intro_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
-               var res = new HashSet[MClassDef]
-               for mmodule in in_nesting.greaters do
-                       res.add_all mmodule.intro_mclassdefs(min_visibility)
-               end
-               return res
-       end
-
-       fun in_nesting_redef_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
-               var res = new HashSet[MClassDef]
-               for mmodule in self.in_nesting.greaters do
-                       res.add_all mmodule.redef_mclassdefs(min_visibility)
-               end
-               return res
-       end
-
        redef fun concern_rank is cached do
                var max = 0
                for p in in_importation.direct_greaters do
@@ -209,6 +171,23 @@ redef class MModule
                end
                return max + 1
        end
+
+       # Find all mmodules nested in `self` if `self` is the default module of a `MGroup`.
+       fun nested_mmodules: Array[MModule] do
+               var res = new Array[MModule]
+               var mgroup = mgroup
+               if mgroup == null or self != mgroup.default_mmodule then return res
+               for mmodule in mgroup.mmodules do
+                       if mmodule == self then continue
+                       res.add mmodule
+               end
+               for nested in mgroup.in_nesting.direct_smallers do
+                       var default = nested.default_mmodule
+                       if default == null then continue
+                       res.add default
+               end
+               return res
+       end
 end
 
 redef class MClass
index 1ae7e45..b38bdac 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Load nit source files and build the associated model
-#
-# FIXME better doc
-#
-# FIXME split this module into submodules
-# FIXME add missing error checks
 module modelbuilder
 
-import model
+import loader
 import phase
 
 private import more_collections
@@ -30,29 +24,15 @@ private import more_collections
 ###
 
 redef class ToolContext
-       # Option --path
-       var opt_path = new OptionArray("Set include path for loaders (may be used more than once)", "-I", "--path")
-
-       # Option --only-metamodel
-       var opt_only_metamodel = new OptionBool("Stop after meta-model processing", "--only-metamodel")
-
-       # Option --only-parse
-       var opt_only_parse = new OptionBool("Only proceed to parse step of loaders", "--only-parse")
-
        # Option --ignore-visibility
        var opt_ignore_visibility = new OptionBool("Do not check, and produce errors, on visibility issues.", "--ignore-visibility")
 
        redef init
        do
                super
-               option_context.add_option(opt_path, opt_only_parse, opt_only_metamodel, opt_ignore_visibility)
+               option_context.add_option(opt_ignore_visibility)
        end
 
-       # The modelbuilder 1-to-1 associated with the toolcontext
-       fun modelbuilder: ModelBuilder do return modelbuilder_real.as(not null)
-
-       private var modelbuilder_real: nullable ModelBuilder = null
-
        # Combine module to make a single one if required.
        fun make_main_module(mmodules: Array[MModule]): MModule
        do
@@ -98,14 +78,7 @@ redef class Phase
 end
 
 
-# A model builder knows how to load nit source files and build the associated model
-class ModelBuilder
-       # The model where new modules, classes and properties are added
-       var model: Model
-
-       # The toolcontext used to control the interaction with the user (getting options and displaying messages)
-       var toolcontext: ToolContext
-
+redef class ModelBuilder
        # Run phases on all loaded modules
        fun run_phases
        do
@@ -123,725 +96,4 @@ class ModelBuilder
                end
        end
 
-       # Instantiate a modelbuilder for a model and a toolcontext
-       # Important, the options of the toolcontext must be correctly set (parse_option already called)
-       init
-       do
-               assert toolcontext.modelbuilder_real == null
-               toolcontext.modelbuilder_real = self
-
-               # Setup the paths value
-               paths.append(toolcontext.opt_path.value)
-
-               var path_env = "NIT_PATH".environ
-               if not path_env.is_empty then
-                       paths.append(path_env.split_with(':'))
-               end
-
-               var nit_dir = toolcontext.nit_dir
-               var libname = "{nit_dir}/lib"
-               if libname.file_exists then paths.add(libname)
-       end
-
-       # Load a bunch of modules.
-       # `modules` can contains filenames or module names.
-       # Imported modules are automatically loaded and modelized.
-       # The result is the corresponding model elements.
-       # Errors and warnings are printed with the toolcontext.
-       #
-       # Note: class and property model element are not analysed.
-       fun parse(modules: Sequence[String]): Array[MModule]
-       do
-               var time0 = get_time
-               # Parse and recursively load
-               self.toolcontext.info("*** PARSE ***", 1)
-               var mmodules = new ArraySet[MModule]
-               for a in modules do
-                       var nmodule = self.load_module(a)
-                       if nmodule == null then continue # Skip error
-                       mmodules.add(nmodule.mmodule.as(not null))
-               end
-               var time1 = get_time
-               self.toolcontext.info("*** END PARSE: {time1-time0} ***", 2)
-
-               self.toolcontext.check_errors
-
-               if toolcontext.opt_only_parse.value then
-                       self.toolcontext.info("*** ONLY PARSE...", 1)
-                       exit(0)
-               end
-
-               return mmodules.to_a
-       end
-
-       # Return a class named `name` visible by the module `mmodule`.
-       # Visibility in modules is correctly handled.
-       # If no such a class exists, then null is returned.
-       # If more than one class exists, then an error on `anode` is displayed and null is returned.
-       # FIXME: add a way to handle class name conflict
-       fun try_get_mclass_by_name(anode: ANode, mmodule: MModule, name: String): nullable MClass
-       do
-               var classes = model.get_mclasses_by_name(name)
-               if classes == null then
-                       return null
-               end
-
-               var res: nullable MClass = null
-               for mclass in classes do
-                       if not mmodule.in_importation <= mclass.intro_mmodule then continue
-                       if not mmodule.is_visible(mclass.intro_mmodule, mclass.visibility) then continue
-                       if res == null then
-                               res = mclass
-                       else
-                               error(anode, "Ambigous class name '{name}'; conflict between {mclass.full_name} and {res.full_name}")
-                               return null
-                       end
-               end
-               return res
-       end
-
-       # Return a property named `name` on the type `mtype` visible in the module `mmodule`.
-       # Visibility in modules is correctly handled.
-       # Protected properties are returned (it is up to the caller to check and reject protected properties).
-       # If no such a property exists, then null is returned.
-       # If more than one property exists, then an error on `anode` is displayed and null is returned.
-       # FIXME: add a way to handle property name conflict
-       fun try_get_mproperty_by_name2(anode: ANode, mmodule: MModule, mtype: MType, name: String): nullable MProperty
-       do
-               var props = self.model.get_mproperties_by_name(name)
-               if props == null then
-                       return null
-               end
-
-               var cache = self.try_get_mproperty_by_name2_cache[mmodule, mtype, name]
-               if cache != null then return cache
-
-               var res: nullable MProperty = null
-               var ress: nullable Array[MProperty] = null
-               for mprop in props do
-                       if not mtype.has_mproperty(mmodule, mprop) then continue
-                       if not mmodule.is_visible(mprop.intro_mclassdef.mmodule, mprop.visibility) then continue
-
-                       # new-factories are invisible outside of the class
-                       if mprop isa MMethod and mprop.is_new and (not mtype isa MClassType or mprop.intro_mclassdef.mclass != mtype.mclass) then
-                               continue
-                       end
-
-                       if res == null then
-                               res = mprop
-                               continue
-                       end
-
-                       # Two global properties?
-                       # First, special case for init, keep the most specific ones
-                       if res isa MMethod and mprop isa MMethod and res.is_init and mprop.is_init then
-                               var restype = res.intro_mclassdef.bound_mtype
-                               var mproptype = mprop.intro_mclassdef.bound_mtype
-                               if mproptype.is_subtype(mmodule, null, restype) then
-                                       # found a most specific constructor, so keep it
-                                       res = mprop
-                                       continue
-                               end
-                       end
-
-                       # Ok, just keep all prop in the ress table
-                       if ress == null then
-                               ress = new Array[MProperty]
-                               ress.add(res)
-                       end
-                       ress.add(mprop)
-               end
-
-               # There is conflict?
-               if ress != null and res isa MMethod and res.is_init then
-                       # special case forinit again
-                       var restype = res.intro_mclassdef.bound_mtype
-                       var ress2 = new Array[MProperty]
-                       for mprop in ress do
-                               var mproptype = mprop.intro_mclassdef.bound_mtype
-                               if not restype.is_subtype(mmodule, null, mproptype) then
-                                       ress2.add(mprop)
-                               else if not mprop isa MMethod or not mprop.is_init then
-                                       ress2.add(mprop)
-                               end
-                       end
-                       if ress2.is_empty then
-                               ress = null
-                       else
-                               ress = ress2
-                               ress.add(res)
-                       end
-               end
-
-               if ress != null then
-                       assert ress.length > 1
-                       var s = new Array[String]
-                       for mprop in ress do s.add mprop.full_name
-                       self.error(anode, "Ambigous property name '{name}' for {mtype}; conflict between {s.join(" and ")}")
-               end
-
-               self.try_get_mproperty_by_name2_cache[mmodule, mtype, name] = res
-               return res
-       end
-
-       private var try_get_mproperty_by_name2_cache = new HashMap3[MModule, MType, String, nullable MProperty]
-
-
-       # Alias for try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.mtype, name)
-       fun try_get_mproperty_by_name(anode: ANode, mclassdef: MClassDef, name: String): nullable MProperty
-       do
-               return try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.bound_mtype, name)
-       end
-
-       # The list of directories to search for top level modules
-       # The list is initially set with :
-       #   * the toolcontext --path option
-       #   * the NIT_PATH environment variable
-       #   * `toolcontext.nit_dir`
-       # Path can be added (or removed) by the client
-       var paths = new Array[String]
-
-       # Like (an used by) `get_mmodule_by_name` but just return the ModulePath
-       private fun search_mmodule_by_name(anode: ANode, mgroup: nullable MGroup, name: String): nullable ModulePath
-       do
-               # First, look in groups
-               var c = mgroup
-               while c != null do
-                       var dirname = c.filepath
-                       if dirname == null then break # virtual group
-                       if dirname.has_suffix(".nit") then break # singleton project
-
-                       # Second, try the directory to find a file
-                       var try_file = dirname + "/" + name + ".nit"
-                       if try_file.file_exists then
-                               var res = self.identify_file(try_file.simplify_path)
-                               assert res != null
-                               return res
-                       end
-
-                       # Third, try if the requested module is itself a group
-                       try_file = dirname + "/" + name + "/" + name + ".nit"
-                       if try_file.file_exists then
-                               var res = self.identify_file(try_file.simplify_path)
-                               assert res != null
-                               return res
-                       end
-
-                       c = c.parent
-               end
-
-               # Look at some known directories
-               var lookpaths = self.paths
-
-               # Look in the directory of the group project also (even if not explicitely in the path)
-               if mgroup != null then
-                       # path of the root group
-                       var dirname = mgroup.mproject.root.filepath
-                       if dirname != null then
-                               dirname = dirname.join_path("..").simplify_path
-                               if not lookpaths.has(dirname) and dirname.file_exists then
-                                       lookpaths = lookpaths.to_a
-                                       lookpaths.add(dirname)
-                               end
-                       end
-               end
-
-               var candidate = search_module_in_paths(anode.hot_location, name, lookpaths)
-
-               if candidate == null then
-                       if mgroup != null then
-                               error(anode, "Error: cannot find module {name} from {mgroup.name}. tried {lookpaths.join(", ")}")
-                       else
-                               error(anode, "Error: cannot find module {name}. tried {lookpaths.join(", ")}")
-                       end
-                       return null
-               end
-               return candidate
-       end
-
-       # Get a module by its short name; if required, the module is loaded, parsed and its hierarchies computed.
-       # If `mgroup` is set, then the module search starts from it up to the top level (see `paths`);
-       # if `mgroup` is null then the module is searched in the top level only.
-       # If no module exists or there is a name conflict, then an error on `anode` is displayed and null is returned.
-       fun get_mmodule_by_name(anode: ANode, mgroup: nullable MGroup, name: String): nullable MModule
-       do
-               var path = search_mmodule_by_name(anode, mgroup, name)
-               if path == null then return null # Forward error
-               var res = self.load_module(path.filepath)
-               if res == null then return null # Forward error
-               return res.mmodule.as(not null)
-       end
-
-       # Search a module `name` from path `lookpaths`.
-       # If found, the path of the file is returned
-       private fun search_module_in_paths(location: nullable Location, name: String, lookpaths: Collection[String]): nullable ModulePath
-       do
-               var candidate: nullable String = null
-               for dirname in lookpaths do
-                       var try_file = (dirname + "/" + name + ".nit").simplify_path
-                       if try_file.file_exists then
-                               if candidate == null then
-                                       candidate = try_file
-                               else if candidate != try_file then
-                                       # try to disambiguate conflicting modules
-                                       var abs_candidate = module_absolute_path(candidate)
-                                       var abs_try_file = module_absolute_path(try_file)
-                                       if abs_candidate != abs_try_file then
-                                               toolcontext.error(location, "Error: conflicting module file for {name}: {candidate} {try_file}")
-                                       end
-                               end
-                       end
-                       try_file = (dirname + "/" + name + "/" + name + ".nit").simplify_path
-                       if try_file.file_exists then
-                               if candidate == null then
-                                       candidate = try_file
-                               else if candidate != try_file then
-                                       # try to disambiguate conflicting modules
-                                       var abs_candidate = module_absolute_path(candidate)
-                                       var abs_try_file = module_absolute_path(try_file)
-                                       if abs_candidate != abs_try_file then
-                                               toolcontext.error(location, "Error: conflicting module file for {name}: {candidate} {try_file}")
-                                       end
-                               end
-                       end
-               end
-               if candidate == null then return null
-               return identify_file(candidate)
-       end
-
-       # cache for `identify_file` by realpath
-       private var identified_files = new HashMap[String, nullable ModulePath]
-
-       # Identify a source file
-       # Load the associated project and groups if required
-       private fun identify_file(path: String): nullable ModulePath
-       do
-               # special case for not a nit file
-               if path.file_extension != "nit" then
-                       # search in known -I paths
-                       var res = search_module_in_paths(null, path, self.paths)
-                       if res != null then return res
-
-                       # Found nothins? maybe it is a group...
-                       var candidate = null
-                       if path.file_exists then
-                               var mgroup = get_mgroup(path)
-                               if mgroup != null then
-                                       var owner_path = mgroup.filepath.join_path(mgroup.name + ".nit")
-                                       if owner_path.file_exists then candidate = owner_path
-                               end
-                       end
-
-                       if candidate == null then
-                               toolcontext.error(null, "Error: cannot find module `{path}`.")
-                               return null
-                       end
-                       path = candidate
-               end
-
-               # Fast track, the path is already known
-               var pn = path.basename(".nit")
-               var rp = module_absolute_path(path)
-               if identified_files.has_key(rp) then return identified_files[rp]
-
-               # Search for a group
-               var mgrouppath = path.join_path("..").simplify_path
-               var mgroup = get_mgroup(mgrouppath)
-
-               if mgroup == null then
-                       # singleton project
-                       var mproject = new MProject(pn, model)
-                       mgroup = new MGroup(pn, mproject, null) # same name for the root group
-                       mgroup.filepath = path
-                       mproject.root = mgroup
-                       toolcontext.info("found project `{pn}` at {path}", 2)
-               end
-
-               var res = new ModulePath(pn, path, mgroup)
-               mgroup.module_paths.add(res)
-
-               identified_files[rp] = res
-               return res
-       end
-
-       # groups by path
-       private var mgroups = new HashMap[String, nullable MGroup]
-
-       # return the mgroup associated to a directory path
-       # if the directory is not a group null is returned
-       private fun get_mgroup(dirpath: String): nullable MGroup
-       do
-               var rdp = module_absolute_path(dirpath)
-               if mgroups.has_key(rdp) then
-                       return mgroups[rdp]
-               end
-
-               # Hack, a group is determined by:
-               # * the presence of a honomymous nit file
-               # * the fact that the directory is named `src`
-               var pn = rdp.basename(".nit")
-               var mp = dirpath.join_path(pn + ".nit").simplify_path
-
-               var dirpath2 = dirpath
-               if not mp.file_exists then
-                       if pn == "src" then
-                               # With a src directory, the group name is the name of the parent directory
-                               dirpath2 = rdp.dirname
-                               pn = dirpath2.basename("")
-                       else
-                               return null
-                       end
-               end
-
-               # check parent directory
-               var parentpath = dirpath.join_path("..").simplify_path
-               var parent = get_mgroup(parentpath)
-
-               var mgroup
-               if parent == null then
-                       # no parent, thus new project
-                       var mproject = new MProject(pn, model)
-                       mgroup = new MGroup(pn, mproject, null) # same name for the root group
-                       mproject.root = mgroup
-                       toolcontext.info("found project `{mproject}` at {dirpath}", 2)
-               else
-                       mgroup = new MGroup(pn, parent.mproject, parent)
-                       toolcontext.info("found sub group `{mgroup.full_name}` at {dirpath}", 2)
-               end
-               var readme = dirpath2.join_path("README.md")
-               if not readme.file_exists then readme = dirpath2.join_path("README")
-               if readme.file_exists then
-                       var mdoc = new MDoc
-                       var s = new IFStream.open(readme)
-                       while not s.eof do
-                               mdoc.content.add(s.read_line)
-                       end
-                       mgroup.mdoc = mdoc
-                       mdoc.original_mentity = mgroup
-               end
-               mgroup.filepath = dirpath
-               mgroups[rdp] = mgroup
-               return mgroup
-       end
-
-       # Transform relative paths (starting with '../') into absolute paths
-       private fun module_absolute_path(path: String): String do
-               return getcwd.join_path(path).simplify_path
-       end
-
-       # Try to load a module AST using a path.
-       # Display an error if there is a problem (IO / lexer / parser) and return null
-       fun load_module_ast(filename: String): nullable AModule
-       do
-               if filename.file_extension != "nit" then
-                       self.toolcontext.error(null, "Error: file {filename} is not a valid nit module.")
-                       return null
-               end
-               if not filename.file_exists then
-                       self.toolcontext.error(null, "Error: file {filename} not found.")
-                       return null
-               end
-
-               self.toolcontext.info("load module {filename}", 2)
-
-               # Load the file
-               var file = new IFStream.open(filename)
-               var lexer = new Lexer(new SourceFile(filename, file))
-               var parser = new Parser(lexer)
-               var tree = parser.parse
-               file.close
-
-               # Handle lexer and parser error
-               var nmodule = tree.n_base
-               if nmodule == null then
-                       var neof = tree.n_eof
-                       assert neof isa AError
-                       error(neof, neof.message)
-                       return null
-               end
-
-               return nmodule
-       end
-
-       # Try to load a module and its imported modules using a path.
-       # Display an error if there is a problem (IO / lexer / parser / importation) and return null
-       # Note: usually, you do not need this method, use `get_mmodule_by_name` instead.
-       fun load_module(filename: String): nullable AModule
-       do
-               # Look for the module
-               var file = identify_file(filename)
-               if file == null then return null # forward error
-
-               # Already known and loaded? then return it
-               var mmodule = file.mmodule
-               if mmodule != null then
-                       return mmodule2nmodule[mmodule]
-               end
-
-               # Load it manually
-               var nmodule = load_module_ast(file.filepath)
-               if nmodule == null then return null # forward error
-
-               # build the mmodule and load imported modules
-               mmodule = build_a_mmodule(file.mgroup, file.name, nmodule)
-
-               if mmodule == null then return null # forward error
-
-               # Update the file information
-               file.mmodule = mmodule
-
-               # Load imported module
-               build_module_importation(nmodule)
-
-               return nmodule
-       end
-
-       # Injection of a new module without source.
-       # Used by the interpreted
-       fun load_rt_module(parent: nullable MModule, nmodule: AModule, mod_name: String): nullable AModule
-       do
-               # Create the module
-
-               var mgroup = null
-               if parent != null then mgroup = parent.mgroup
-               var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
-               nmodule.mmodule = mmodule
-               nmodules.add(nmodule)
-               self.mmodule2nmodule[mmodule] = nmodule
-
-               if parent!= null then
-                       var imported_modules = new Array[MModule]
-                       imported_modules.add(parent)
-                       mmodule.set_visibility_for(parent, intrude_visibility)
-                       mmodule.set_imported_mmodules(imported_modules)
-               else
-                       build_module_importation(nmodule)
-               end
-
-               return nmodule
-       end
-
-       # Visit the AST and create the `MModule` object
-       private fun build_a_mmodule(mgroup: nullable MGroup, mod_name: String, nmodule: AModule): nullable MModule
-       do
-               # Check the module name
-               var decl = nmodule.n_moduledecl
-               if decl == null then
-                       #warning(nmodule, "Warning: Missing 'module' keyword") #FIXME: NOT YET FOR COMPATIBILITY
-               else
-                       var decl_name = decl.n_name.n_id.text
-                       if decl_name != mod_name then
-                               error(decl.n_name, "Error: module name missmatch; declared {decl_name} file named {mod_name}")
-                       end
-               end
-
-               # Create the module
-               var mmodule = new MModule(model, mgroup, mod_name, nmodule.location)
-               nmodule.mmodule = mmodule
-               nmodules.add(nmodule)
-               self.mmodule2nmodule[mmodule] = nmodule
-
-               if decl != null then
-                       var ndoc = decl.n_doc
-                       if ndoc != null then
-                               var mdoc = ndoc.to_mdoc
-                               mmodule.mdoc = mdoc
-                               mdoc.original_mentity = mmodule
-                       else
-                               advice(decl, "missing-doc", "Documentation warning: Undocumented module `{mmodule}`")
-                       end
-               end
-
-               return mmodule
-       end
-
-       # Analysis the module importation and fill the module_importation_hierarchy
-       private fun build_module_importation(nmodule: AModule)
-       do
-               if nmodule.is_importation_done then return
-               nmodule.is_importation_done = true
-               var mmodule = nmodule.mmodule.as(not null)
-               var stdimport = true
-               var imported_modules = new Array[MModule]
-               for aimport in nmodule.n_imports do
-                       stdimport = false
-                       if not aimport isa AStdImport then
-                               continue
-                       end
-                       var mgroup = mmodule.mgroup
-                       if aimport.n_name.n_quad != null then mgroup = null # Start from top level
-                       for grp in aimport.n_name.n_path do
-                               var path = search_mmodule_by_name(grp, mgroup, grp.text)
-                               if path == null then return # Skip error
-                               mgroup = path.mgroup
-                       end
-                       var mod_name = aimport.n_name.n_id.text
-                       var sup = self.get_mmodule_by_name(aimport.n_name, mgroup, mod_name)
-                       if sup == null then continue # Skip error
-                       aimport.mmodule = sup
-                       imported_modules.add(sup)
-                       var mvisibility = aimport.n_visibility.mvisibility
-                       if mvisibility == protected_visibility then
-                               error(aimport.n_visibility, "Error: only properties can be protected.")
-                               return
-                       end
-                       if sup == mmodule then
-                               error(aimport.n_name, "Error: Dependency loop in module {mmodule}.")
-                       end
-                       if sup.in_importation < mmodule then
-                               error(aimport.n_name, "Error: Dependency loop between modules {mmodule} and {sup}.")
-                               return
-                       end
-                       mmodule.set_visibility_for(sup, mvisibility)
-               end
-               if stdimport then
-                       var mod_name = "standard"
-                       var sup = self.get_mmodule_by_name(nmodule, null, mod_name)
-                       if sup != null then # Skip error
-                               imported_modules.add(sup)
-                               mmodule.set_visibility_for(sup, public_visibility)
-                       end
-               end
-               self.toolcontext.info("{mmodule} imports {imported_modules.join(", ")}", 3)
-               mmodule.set_imported_mmodules(imported_modules)
-
-               # Force standard to be public if imported
-               for sup in mmodule.in_importation.greaters do
-                       if sup.name == "standard" then
-                               mmodule.set_visibility_for(sup, public_visibility)
-                       end
-               end
-
-               # TODO: Correctly check for useless importation
-               # It is even doable?
-               var directs = mmodule.in_importation.direct_greaters
-               for nim in nmodule.n_imports do
-                       if not nim isa AStdImport then continue
-                       var im = nim.mmodule
-                       if im == null then continue
-                       if directs.has(im) then continue
-                       # This generates so much noise that it is simpler to just comment it
-                       #warning(nim, "Warning: possible useless importation of {im}")
-               end
-       end
-
-       # All the loaded modules
-       var nmodules = new Array[AModule]
-
-       # Register the nmodule associated to each mmodule
-       # FIXME: why not refine the `MModule` class with a nullable attribute?
-       var mmodule2nmodule = new HashMap[MModule, AModule]
-
-       # Helper function to display an error on a node.
-       # Alias for `self.toolcontext.error(n.hot_location, text)`
-       fun error(n: ANode, text: String)
-       do
-               self.toolcontext.error(n.hot_location, text)
-       end
-
-       # Helper function to display a warning on a node.
-       # Alias for: `self.toolcontext.warning(n.hot_location, text)`
-       fun warning(n: ANode, tag, text: String)
-       do
-               self.toolcontext.warning(n.hot_location, tag, text)
-       end
-
-       # Helper function to display an advice on a node.
-       # Alias for: `self.toolcontext.advice(n.hot_location, text)`
-       fun advice(n: ANode, tag, text: String)
-       do
-               self.toolcontext.advice(n.hot_location, tag, text)
-       end
-
-       # Force to get the primitive method named `name` on the type `recv` or do a fatal error on `n`
-       fun force_get_primitive_method(n: nullable ANode, name: String, recv: MClass, mmodule: MModule): MMethod
-       do
-               var res = mmodule.try_get_primitive_method(name, recv)
-               if res == null then
-                       var l = null
-                       if n != null then l = n.hot_location
-                       self.toolcontext.fatal_error(l, "Fatal Error: {recv} must have a property named {name}.")
-                       abort
-               end
-               return res
-       end
-end
-
-# placeholder to a module file identified but not always loaded in a project
-private class ModulePath
-       # The name of the module
-       # (it's the basename of the filepath)
-       var name: String
-
-       # The human path of the module
-       var filepath: String
-
-       # The group (and the project) of the possible module
-       var mgroup: MGroup
-
-       # The loaded module (if any)
-       var mmodule: nullable MModule = null
-
-       redef fun to_s do return filepath
-end
-
-redef class MGroup
-       # modules paths associated with the group
-       private var module_paths = new Array[ModulePath]
-end
-
-redef class AStdImport
-       # The imported module once determined
-       var mmodule: nullable MModule = null
-end
-
-redef class AModule
-       # The associated MModule once build by a `ModelBuilder`
-       var mmodule: nullable MModule
-       # Flag that indicate if the importation is already completed
-       var is_importation_done: Bool = false
-end
-
-redef class AVisibility
-       # The visibility level associated with the AST node class
-       fun mvisibility: MVisibility is abstract
-end
-redef class AIntrudeVisibility
-       redef fun mvisibility do return intrude_visibility
-end
-redef class APublicVisibility
-       redef fun mvisibility do return public_visibility
-end
-redef class AProtectedVisibility
-       redef fun mvisibility do return protected_visibility
-end
-redef class APrivateVisibility
-       redef fun mvisibility do return private_visibility
-end
-
-redef class ADoc
-       private var mdoc_cache: nullable MDoc
-       fun to_mdoc: MDoc
-       do
-               var res = mdoc_cache
-               if res != null then return res
-               res = new MDoc
-               for c in n_comment do
-                       var text = c.text
-                       if text.length < 2 then
-                               res.content.add ""
-                               continue
-                       end
-                       assert text.chars[0] == '#'
-                       if text.chars[1] == ' ' then
-                               text = text.substring_from(2) # eat starting `#` and space
-                       else
-                               text = text.substring_from(1) # eat atarting `#` only
-                       end
-                       if text.chars.last == '\n' then text = text.substring(0, text.length-1) # drop \n
-                       res.content.add(text)
-               end
-               mdoc_cache = res
-               return res
-       end
 end
diff --git a/src/modelbuilder_base.nit b/src/modelbuilder_base.nit
new file mode 100644 (file)
index 0000000..d09b717
--- /dev/null
@@ -0,0 +1,260 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Load nit source files and build the associated model
+#
+# FIXME better doc
+#
+# FIXME split this module into submodules
+# FIXME add missing error checks
+module modelbuilder_base
+
+import model
+import toolcontext
+import parser
+
+private import more_collections
+
+###
+
+redef class ToolContext
+
+       # The modelbuilder 1-to-1 associated with the toolcontext
+       fun modelbuilder: ModelBuilder do return modelbuilder_real.as(not null)
+
+       private var modelbuilder_real: nullable ModelBuilder = null
+
+end
+
+# A model builder knows how to load nit source files and build the associated model
+class ModelBuilder
+       # The model where new modules, classes and properties are added
+       var model: Model
+
+       # The toolcontext used to control the interaction with the user (getting options and displaying messages)
+       var toolcontext: ToolContext
+
+       # Instantiate a modelbuilder for a model and a toolcontext
+       # Important, the options of the toolcontext must be correctly set (parse_option already called)
+       init
+       do
+               assert toolcontext.modelbuilder_real == null
+               toolcontext.modelbuilder_real = self
+       end
+
+       # Return a class named `name` visible by the module `mmodule`.
+       # Visibility in modules is correctly handled.
+       # If no such a class exists, then null is returned.
+       # If more than one class exists, then an error on `anode` is displayed and null is returned.
+       # FIXME: add a way to handle class name conflict
+       fun try_get_mclass_by_name(anode: ANode, mmodule: MModule, name: String): nullable MClass
+       do
+               var classes = model.get_mclasses_by_name(name)
+               if classes == null then
+                       return null
+               end
+
+               var res: nullable MClass = null
+               for mclass in classes do
+                       if not mmodule.in_importation <= mclass.intro_mmodule then continue
+                       if not mmodule.is_visible(mclass.intro_mmodule, mclass.visibility) then continue
+                       if res == null then
+                               res = mclass
+                       else
+                               error(anode, "Ambigous class name '{name}'; conflict between {mclass.full_name} and {res.full_name}")
+                               return null
+                       end
+               end
+               return res
+       end
+
+       # Return a property named `name` on the type `mtype` visible in the module `mmodule`.
+       # Visibility in modules is correctly handled.
+       # Protected properties are returned (it is up to the caller to check and reject protected properties).
+       # If no such a property exists, then null is returned.
+       # If more than one property exists, then an error on `anode` is displayed and null is returned.
+       # FIXME: add a way to handle property name conflict
+       fun try_get_mproperty_by_name2(anode: ANode, mmodule: MModule, mtype: MType, name: String): nullable MProperty
+       do
+               var props = self.model.get_mproperties_by_name(name)
+               if props == null then
+                       return null
+               end
+
+               var cache = self.try_get_mproperty_by_name2_cache[mmodule, mtype, name]
+               if cache != null then return cache
+
+               var res: nullable MProperty = null
+               var ress: nullable Array[MProperty] = null
+               for mprop in props do
+                       if not mtype.has_mproperty(mmodule, mprop) then continue
+                       if not mmodule.is_visible(mprop.intro_mclassdef.mmodule, mprop.visibility) then continue
+
+                       # new-factories are invisible outside of the class
+                       if mprop isa MMethod and mprop.is_new and (not mtype isa MClassType or mprop.intro_mclassdef.mclass != mtype.mclass) then
+                               continue
+                       end
+
+                       if res == null then
+                               res = mprop
+                               continue
+                       end
+
+                       # Two global properties?
+                       # First, special case for init, keep the most specific ones
+                       if res isa MMethod and mprop isa MMethod and res.is_init and mprop.is_init then
+                               var restype = res.intro_mclassdef.bound_mtype
+                               var mproptype = mprop.intro_mclassdef.bound_mtype
+                               if mproptype.is_subtype(mmodule, null, restype) then
+                                       # found a most specific constructor, so keep it
+                                       res = mprop
+                                       continue
+                               end
+                       end
+
+                       # Ok, just keep all prop in the ress table
+                       if ress == null then
+                               ress = new Array[MProperty]
+                               ress.add(res)
+                       end
+                       ress.add(mprop)
+               end
+
+               # There is conflict?
+               if ress != null and res isa MMethod and res.is_init then
+                       # special case forinit again
+                       var restype = res.intro_mclassdef.bound_mtype
+                       var ress2 = new Array[MProperty]
+                       for mprop in ress do
+                               var mproptype = mprop.intro_mclassdef.bound_mtype
+                               if not restype.is_subtype(mmodule, null, mproptype) then
+                                       ress2.add(mprop)
+                               else if not mprop isa MMethod or not mprop.is_init then
+                                       ress2.add(mprop)
+                               end
+                       end
+                       if ress2.is_empty then
+                               ress = null
+                       else
+                               ress = ress2
+                               ress.add(res)
+                       end
+               end
+
+               if ress != null then
+                       assert ress.length > 1
+                       var s = new Array[String]
+                       for mprop in ress do s.add mprop.full_name
+                       self.error(anode, "Ambigous property name '{name}' for {mtype}; conflict between {s.join(" and ")}")
+               end
+
+               self.try_get_mproperty_by_name2_cache[mmodule, mtype, name] = res
+               return res
+       end
+
+       private var try_get_mproperty_by_name2_cache = new HashMap3[MModule, MType, String, nullable MProperty]
+
+
+       # Alias for try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.mtype, name)
+       fun try_get_mproperty_by_name(anode: ANode, mclassdef: MClassDef, name: String): nullable MProperty
+       do
+               return try_get_mproperty_by_name2(anode, mclassdef.mmodule, mclassdef.bound_mtype, name)
+       end
+
+       # Helper function to display an error on a node.
+       # Alias for `self.toolcontext.error(n.hot_location, text)`
+       fun error(n: nullable ANode, text: String)
+       do
+               var l = null
+               if n != null then l = n.hot_location
+               self.toolcontext.error(l, text)
+       end
+
+       # Helper function to display a warning on a node.
+       # Alias for: `self.toolcontext.warning(n.hot_location, text)`
+       fun warning(n: nullable ANode, tag, text: String)
+       do
+               var l = null
+               if n != null then l = n.hot_location
+               self.toolcontext.warning(l, tag, text)
+       end
+
+       # Helper function to display an advice on a node.
+       # Alias for: `self.toolcontext.advice(n.hot_location, text)`
+       fun advice(n: nullable ANode, tag, text: String)
+       do
+               var l = null
+               if n != null then l = n.hot_location
+               self.toolcontext.advice(l, tag, text)
+       end
+
+       # Force to get the primitive method named `name` on the type `recv` or do a fatal error on `n`
+       fun force_get_primitive_method(n: nullable ANode, name: String, recv: MClass, mmodule: MModule): MMethod
+       do
+               var res = mmodule.try_get_primitive_method(name, recv)
+               if res == null then
+                       var l = null
+                       if n != null then l = n.hot_location
+                       self.toolcontext.fatal_error(l, "Fatal Error: {recv} must have a property named {name}.")
+                       abort
+               end
+               return res
+       end
+end
+
+redef class AVisibility
+       # The visibility level associated with the AST node class
+       fun mvisibility: MVisibility is abstract
+end
+redef class AIntrudeVisibility
+       redef fun mvisibility do return intrude_visibility
+end
+redef class APublicVisibility
+       redef fun mvisibility do return public_visibility
+end
+redef class AProtectedVisibility
+       redef fun mvisibility do return protected_visibility
+end
+redef class APrivateVisibility
+       redef fun mvisibility do return private_visibility
+end
+
+redef class ADoc
+       private var mdoc_cache: nullable MDoc
+       fun to_mdoc: MDoc
+       do
+               var res = mdoc_cache
+               if res != null then return res
+               res = new MDoc
+               for c in n_comment do
+                       var text = c.text
+                       if text.length < 2 then
+                               res.content.add ""
+                               continue
+                       end
+                       assert text.chars[0] == '#'
+                       if text.chars[1] == ' ' then
+                               text = text.substring_from(2) # eat starting `#` and space
+                       else
+                               text = text.substring_from(1) # eat atarting `#` only
+                       end
+                       if text.chars.last == '\n' then text = text.substring(0, text.length-1) # drop \n
+                       res.content.add(text)
+               end
+               mdoc_cache = res
+               return res
+       end
+end
index 07b7470..c996a0c 100644 (file)
@@ -708,6 +708,9 @@ redef class AMethPropdef
                        msignature = mpropdef.mproperty.intro.msignature
                        if msignature == null then return # Skip error
 
+                       # The local signature is adapted to use the local formal types, if any.
+                       msignature = msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+
                        # Check inherited signature arity
                        if param_names.length != msignature.arity then
                                var node: ANode
@@ -983,7 +986,11 @@ redef class AAttrPropdef
                        var msignature = mreadpropdef.mproperty.intro.msignature
                        if msignature == null then return # Error, thus skipped
                        inherited_type = msignature.return_mtype
-                       if mtype == null then mtype = inherited_type
+                       if inherited_type != null then
+                               # The inherited type is adapted to use the local formal types, if any.
+                               inherited_type = inherited_type.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+                               if mtype == null then mtype = inherited_type
+                       end
                end
 
                var nexpr = self.n_expr
diff --git a/src/ncall.sh b/src/ncall.sh
new file mode 100755 (executable)
index 0000000..db7d26d
--- /dev/null
@@ -0,0 +1,33 @@
+#!/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.
+
+# nitc all
+# fully build, time and check nitc
+
+rm nitc nitc_? hello_world 2>/dev/null
+set -x
+set -e
+make -C ../c_src
+sh git-gen-version.sh
+time ../c_src/nitg nitc.nit -v -o nitc_0
+time ./nitc_0 nitc.nit -v "$@" -o nitc_2
+cp nitc_2 nitc
+time ./nitc_2 nitc.nit -v "$@" -o nitc_3
+time ./nitc_3 nitc.nit -v "$@" -o nitc_4
+./nitc_4 ../examples/hello_world.nit "$@" -o hello_world
+./hello_world
+
+# save the last one; may be useful...
+cp ./nitc_4 nitc.good
diff --git a/src/ngall.sh b/src/ngall.sh
deleted file mode 100755 (executable)
index e6dd406..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-rm nitg nitg_? hello_world 2>/dev/null
-set -x
-set -e
-make -C ../c_src
-sh git-gen-version.sh
-time ../c_src/nitg nitg.nit -v -o nitg_0
-time ./nitg_0 nitg.nit -v "$@" -o nitg_2
-cp nitg_2 nitg
-time ./nitg_2 nitg.nit -v "$@" -o nitg_3
-time ./nitg_3 nitg.nit -v "$@" -o nitg_4
-./nitg_4 ../examples/hello_world.nit "$@" -o hello_world
-./hello_world
-
-# save the last one; may be useful...
-cp ./nitg_4 nitg.good
-
index 3785ff5..5e8697d 100644 (file)
@@ -51,12 +51,11 @@ if opt_eval.value then
 
        var parent = null
        if opt_loop.value then
-               var nruntime = modelbuilder.load_module("niti_runtime")
-               if nruntime == null then
+               parent = modelbuilder.get_mmodule_by_name(null, null, "niti_runtime")
+               if parent == null then
                        toolcontext.check_errors
                        abort
                end
-               parent = nruntime.mmodule
        end
 
        modelbuilder.load_rt_module(parent, amodule, "-")
similarity index 96%
rename from src/nitg.nit
rename to src/nitc.nit
index 69b99f0..cab5638 100644 (file)
@@ -14,8 +14,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# A global Nit compiler
-module nitg
+# A Nit compiler
+module nitc
 
 import frontend
 import compiler
index e680223..9412f5d 100644 (file)
 # Simple tool to list Nit source files
 module nitls
 
-intrude import modelbuilder
+import modelbuilder
 import ordered_tree
+import console
 
 class ProjTree
        super OrderedTree[Object]
 
        var opt_paths = false
+       var tc: ToolContext
 
        redef fun display(o)
        do
@@ -31,13 +33,53 @@ class ProjTree
                        if opt_paths then
                                return o.filepath.as(not null)
                        else
-                               return "{o.name} ({o.filepath.to_s})"
+                               var d = ""
+                               if o.mdoc != null then
+                                       if tc.opt_no_color.value then
+                                               d = ": {o.mdoc.content.first}"
+                                       else
+                                               d = ": {o.mdoc.content.first.green}"
+                                       end
+                               end
+                               if tc.opt_no_color.value then
+                                       return "{o.name}{d} ({o.filepath.to_s})"
+                               else
+                                       return "{o.name}{d} ({o.filepath.yellow})"
+                               end
                        end
                else if o isa ModulePath then
                        if opt_paths then
                                return o.filepath
                        else
-                               return "{o.name} ({o.filepath})"
+                               var d = ""
+                               var dd = ""
+                               if o.mmodule != null and o.mmodule.mdoc != null then
+                                       if tc.opt_no_color.value then
+                                               d = ": {o.mmodule.mdoc.content.first}"
+                                       else
+                                               d = ": {o.mmodule.mdoc.content.first.green}"
+                                       end
+                               end
+                               if o.mmodule != null and not o.mmodule.in_importation.direct_greaters.is_empty then
+                                       var ms = new Array[String]
+                                       for m in o.mmodule.in_importation.direct_greaters do
+                                               if m.mgroup.mproject == o.mmodule.mgroup.mproject then
+                                                       ms.add m.name
+                                               else
+                                                       ms.add m.full_name
+                                               end
+                                       end
+                                       if tc.opt_no_color.value then
+                                               dd = " ({ms.join(" ")})"
+                                       else
+                                               dd = " ({ms.join(" ")})".light_gray
+                                       end
+                               end
+                               if tc.opt_no_color.value then
+                                       return "{o.name.bold}{d} ({o.filepath.to_s}){dd}"
+                               else
+                                       return "{o.name.bold}{d} ({o.filepath.yellow}){dd}"
+                               end
                        end
                else
                        abort
@@ -45,6 +87,24 @@ class ProjTree
        end
 end
 
+class AlphaEntityComparator
+       super Comparator
+       fun nameof(a: COMPARED): String
+       do
+               if a isa MGroup then
+                       return a.name
+               else if a isa ModulePath then
+                       return a.name
+               else
+                       abort
+               end
+       end
+       redef fun compare(a,b)
+       do
+               return nameof(a) <=> nameof(b)
+       end
+end
+
 var tc = new ToolContext
 
 var opt_keep = new OptionBool("Ignore errors and files that are not a Nit source file", "-k", "--keep")
@@ -58,6 +118,7 @@ var opt_paths = new OptionBool("List only path (instead of name + path)", "-p",
 
 tc.option_context.add_option(opt_keep, opt_recursive, opt_tree, opt_source, opt_project, opt_depends, opt_paths, opt_make)
 tc.tooldescription = "Usage: nitls [OPTION]... <file.nit|directory>...\nLists the projects and/or paths of Nit sources files."
+tc.accept_no_arguments = true
 tc.process_options(args)
 
 if opt_make.value then
@@ -72,90 +133,140 @@ if sum > 1 then
        print tc.tooldescription
        exit 1
 end
-
+if sum == 0 then opt_project.value = true
 tc.keep_going = opt_keep.value
 
 var model = new Model
 var mb = new ModelBuilder(model, tc)
 
-if opt_depends.value then
-       if opt_recursive.value then
-               print "-M incompatible with -r"
-               exit 1
+if tc.option_context.rest.is_empty then tc.option_context.rest.add "."
+var files
+if opt_recursive.value then
+       files = new Array[String]
+       for d in tc.option_context.rest do
+               var pipe = new IProcess("find", d, "-name", "*.nit")
+               while not pipe.eof do
+                       var l = pipe.read_line
+                       if l == "" then break # last line
+                       files.add l
+               end
+               pipe.close
+               pipe.wait
+               if pipe.status != 0 and not opt_keep.value then exit 1
        end
-
-       mb.parse(tc.option_context.rest)
 else
-       var files
-       if opt_recursive.value then
-               files = new Array[String]
-               for d in tc.option_context.rest do
-                       var pipe = new IProcess("find", d, "-name", "*.nit")
-                       while not pipe.eof do
-                               var l = pipe.read_line
-                               if l == "" then break # last line
-                               files.add l
+       files = tc.option_context.rest
+end
+
+if sum == 0 then
+       # If one of the file is a group, default is `opt_tree` instead of `opt_project`
+       for a in files do
+               var g = mb.get_mgroup(a)
+               if g != null then
+                       opt_tree.value = true
+                       opt_project.value = false
+                       break
+               end
+       end
+end
+
+# Identify all relevant files
+for a in files do
+       var g = mb.get_mgroup(a)
+       var mp = mb.identify_file(a)
+       if g != null and not opt_project.value then
+               mb.visit_group(g)
+       end
+       if g == null and mp == null then
+               # not a group not a module, then look at files in the directory
+               var fs = a.files
+               for f in fs do
+                       g = mb.get_mgroup(a/f)
+                       if g != null and not opt_project.value then
+                               mb.visit_group(g)
                        end
-                       pipe.close
-                       pipe.wait
-                       if pipe.status != 0 and not opt_keep.value then exit 1
+                       mp = mb.identify_file(a/f)
+                       #print "{a/f}: {mp or else "?"}"
                end
-       else
-               files = tc.option_context.rest
        end
+end
 
-       for a in files do
-               var mp = mb.identify_file(a)
-               if mp == null then
-                       tc.check_errors
+# Load modules to get more informations
+for mp in mb.identified_files do
+       if not opt_paths.value or opt_depends.value then
+               var mm = mb.load_module(mp.filepath)
+               if mm != null and opt_depends.value then
+                       mb.build_module_importation(mm)
                end
        end
 end
+#tc.check_errors
 
-if sum == 0 then opt_project.value = true
 
+var ot = new ProjTree(tc)
+var sorter = new AlphaEntityComparator
 if opt_tree.value then
-       var ot = new ProjTree
        ot.opt_paths = opt_paths.value
        for p in model.mprojects do
                for g in p.mgroups do
-                       ot.add(g.parent, g)
+                       var pa = g.parent
+                       if g.is_interesting then
+                               ot.add(pa, g)
+                               pa = g
+                       end
                        for mp in g.module_paths do
-                               ot.add(g, mp)
+                               ot.add(pa, mp)
                        end
                end
        end
-       ot.sort_with(new CachedAlphaComparator)
+       ot.sort_with(sorter)
        ot.write_to(stdout)
 end
 
 if opt_source.value then
-       var list = new Array[String]
+       var list = new Array[ModulePath]
        for p in model.mprojects do
                for g in p.mgroups do
                        for mp in g.module_paths do
-                               if opt_paths.value then
-                                       list.add(mp.filepath)
-                               else
-                                       list.add("{g.full_name}/{mp.name} ({mp.filepath})")
-                               end
+                               list.add mp
                        end
                end
        end
-       alpha_comparator.sort(list)
-       for l in list do print l
+       sorter.sort(list)
+       for mp in list do
+               if opt_paths.value then
+                       print mp.filepath
+               else
+                       print "{mp.mgroup.full_name}/{ot.display(mp)}"
+               end
+       end
 end
 
 if opt_project.value then
-       var list = new Array[String]
+       var list = new Array[MGroup]
        for p in model.mprojects do
-               var path = p.root.filepath.as(not null)
+               list.add p.root.as(not null)
+       end
+       sorter.sort(list)
+       for g in list do
+               var path = g.filepath.as(not null)
                if opt_paths.value then
-                       list.add(path)
+                       print path
                else
-                       list.add("{p.name} ({path})")
+                       var d = ""
+                       var md = g.mdoc_or_fallback
+                       if md != null then
+                               if tc.opt_no_color.value then
+                                       d = ": {md.content.first}"
+                               else
+                                       d = ": {md.content.first.green}"
+                               end
+                       end
+                       if tc.opt_no_color.value then
+                               print "{g.name}{d} ({path})"
+                       else
+                               print "{g.name}{d} ({path.yellow})"
+                       end
                end
        end
-       alpha_comparator.sort(list)
-       for l in list do print l
 end
index cc0ffa2..4f31aaf 100644 (file)
@@ -22,6 +22,7 @@ module nitni_base
 
 import parser
 import modelbuilder # builder only for externcalls
+private import compiler::abstract_compiler
 
 redef class MMethod
        # Short name of this method in C (without the class name)
@@ -53,7 +54,8 @@ end
 
 redef class MModule
        # Mangled name of this module in C
-       fun cname: String do return name
+       fun cname: String do return c_name # FIXME this is a hack to keep the internal FFI
+       # API independent of the compilers while still using the `MModule::c_name` service.
 end
 
 redef class MMethodDef
index 141ef1b..7ff0b16 100644 (file)
 
 # `nitpretty` is a tool able to pretty print Nit files.
 #
-# Usage:
-#
-#      nitpretty source.nit
-#
-# Main options:
-#
-# * `-o res.nit` output result into `res.nit`
-# * `--diff` show diff between `source` and `res`
-# * `--meld` open diff with `meld`
-# * `--check` check the format of multiple source files
-# * `--check --meld` perform `--check` and open `meld` for each difference
-#
-# ## Specification
-#
-# The specification of the pretty printing is described here.
-#
-# * Default indentation level is one `'\t'` character and
-# is increased by one for each indentation level.
-# * Default line max-size is 80.
-#
-# ### Comments
-#
-# There is many categories of comments:
-#
-# `Licence comments` are attached to the top of the file
-# no blank line before, one after.
-#
-# ~~~nitish
-# # This is a licence comment
-#
-# # Documentation for module `foo`
-# module foo
-# ~~~
-#
-# `ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
-#
-# They are printed before the definition with a blank line before and no after
-# at the same indentation level than the definition.
-#
-# ~~~nitish
-# # Documentation for module `foo`
-# module foo
-#
-# # Documentation for class `Bar`
-# class Bar
-#      # Documentation for method `baz`
-#      fun baz do end
-# end
-# ~~~
-#
-# `Block comments` are comments composed of one or more line rattached to nothing.
-# They are displayed with one blank line before and after at current indent level.
-#
-# ~~~nitish
-# <blank>
-# # block
-# # comment
-# <blank>
-# ~~~
-#
-# `Attached comments` are comments attached to a production.
-# They are printed as this.
-#
-# ~~~nitish
-# fun foo do # attached comment
-# end
-# ~~~
-#
-# `nitpretty` automatically remove multiple blanks between comments:
-#
-# ~~~nitish
-# # Licence
-# # ...
-# <blank>
-# # Block comment
-# ~~~
-#
-# ### Inlining
-#
-# Productions are automatically inlined when possible.
-#
-# Conditions:
-#
-# * the production must be syntactically inlinable
-# * the inlined production length is less than `PrettyPrinterVisitor::max-size`
-# * the production do not contains any comments
-#
-# ### Modules
-#
-# * There is a blank between the module declaration and its imports
-# * There is no blank between imports and only one after
-# * There is a blank between each extern block definition
-# * There is a blank between each class definition
-# * There is no blank line at the end of the module
-#
-# ~~~nitish
-# # Documentation for module `foo`
-# module foo
-#
-# import a
-# # import b
-# import c
-#
-# # Documentation for class `Bar`
-# class Bar end
-#
-# class Baz end # not a `ADoc` comment
-# ~~~
-#
-#
-# ### Classes
-#
-# * There is no blank between the class definition and its super-classes declarations
-# * There is no blank between two inlined property definition
-# * There is a blank between each block definition
-# * There no blank line at the end of the class definition
-#
-# ~~~nitish
-# # Documentation for class `Bar`
-# class Bar end
-#
-# class Baz
-#     super Bar
-#
-#      fun a is abstract
-#      private fun b do end
-#
-#      fun c do
-#           # ...
-#      end
-# end
-# ~~~
-#
-# Generic types have no space after or before brackets and are separated by a comma and a space:
-#
-# ~~~nitish
-# class A[E: Type1, F: Type1] end
-# ~~~
-#
-# ### Blocks
-#
-# * Inlined productions have no blank lines between them
-# * Block productions have a blank before and after
-#
-# ~~~nitish
-# var a = 10
-# var b = 0
-#
-# if a > b then
-#      # is positive
-#      print "positive"
-# end
-#
-# print "end"
-# ~~~
-#
-# ### Calls and Binary Ops
-#
-# Arguments are always printed separated with a comma and a space:
-#
-# ~~~nitish
-# foo(a, b, c)
-# ~~~
-#
-# Binary ops are always printed wrapped with spaces:
-#
-# ~~~nitish
-# var c = 1 + 2
-# ~~~
-#
-# Calls and binary ops can be splitted to fit the `max-size` constraint.
-# Breaking priority is given to arguments declaration after the comma.
-#
-# ~~~nitish
-# return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
-#     "cccccccccccccccccccccccccc")
-# ~~~
-#
-# Binary ops can also be broken to fit the `max-size` limit:
-#
-# ~~~nitish
-# return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
-#     "cccccccccccccccccccccccccc"
-# ~~~
-module nitpretty
-
-import template
-import toolcontext
-import modelbuilder
-import astutil
-
-# The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
-#
-# The main method here is `visit` that performs the pretty printing of a `ANode`.
-#
-# Because some tokens like `TComment` are not stored in the AST,
-# we visit the AST like traditionnal visitor and also maintain a
-# pointer to the `current_token` (actually the next one to be printed).
-#
-# Visited productions are in charges to move the token pointer using methods such as:
-#
-# * `consume`: print `current_token` and move to the next one
-# * `skip`: move to the next token without printing the current one
-# * `skip_to`: move to a specified token skipping all the tokens before
-# * `catch_up`: consume all the tokens between `current_token` and a specified token
-# * `finish_line`: consume all the tokens between `current_token` and the end of line
-class PrettyPrinterVisitor
-       # Pretty print `n`.
-       fun pretty(n: ANode): Template do
-               clear
-               n.parentize_tokens
-
-               if n isa Prod then
-                       current_token = n.first_token
-                       visit n
-               else if n isa Token then
-                       var p = n.parent
-
-                       while p != null and not p isa Prod do
-                               p = p.parent
-                       end
-
-                       current_token = p.first_token
-                       visit p
-               end
-
-               return tpl.as(not null)
-       end
-
-       # Pretty print the whole `nmodule` with comments before and after.
-       fun pretty_nmodule(nmodule: AModule): Template do
-               clear
-               nmodule.parentize_tokens
-               current_token = nmodule.location.file.first_token
-               visit nmodule
-               catch_up nmodule.location.file.last_token
-               tpl.add "\n"
-               return tpl.as(not null)
-       end
-
-       # Prepare `self` for a new visit.
-       private fun clear do
-               tpl = new Template
-               current_token = null
-               indent = 0
-               current_length = 0
-               previous_length = 0
-               wait_addn = 0
-       end
-
-       # Visit `n` if not null.
-       fun visit(n: nullable ANode) do
-               if n == null then return
-               n.accept_pretty_printer self
-       end
-
-       # Visit a list of `Anode`.
-       fun visit_list(n: nullable ANodes[ANode]) do
-               if n == null then return
-               n.accept_pretty_printer self
-       end
-
-       # Is the node inlinable and can fit on the line.
-       fun can_inline(n: nullable ANode): Bool do
-               if n == null then return true
-               if n.must_be_inline then return true
-               if n.must_be_block then return false
-               # check length
-               if n.collect_length + current_length > max_size then return false
-               # check block is inlinable
-               return n.is_inlinable
-       end
-
-       # Collect all `TComment` between `from` and `to`.
-       fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
-               var res = new Array[TComment]
-               if from isa Prod then from = from.first_token
-               if to isa Prod then to = to.first_token
-               if from == null or to == null then return res
-
-               while from != to do
-                       if from isa TComment then res.add from
-                       from = from.as(Token).next_token
-               end
-
-               return res
-       end
-
-       # Token under cursor.
-       #
-       # This is the next token to visit.
-       var current_token: nullable Token = null
-
-       # Skip the `current_token`.
-       fun skip do current_token = current_token.next_token
-
-       # Skip `current_token` until the end of line.
-       fun skip_line do current_token = current_token.last_real_token_in_line
-
-       # Skip `current_token` until `target` is reached.
-       fun skip_to(target: nullable Token) do
-               if target == null then return
-               while current_token != target do skip
-       end
-
-       # Visit `current_token`.
-       fun consume(token: String) do
-               assert current_token.text == token
-               visit current_token
-       end
-
-       # Is there token to visit between `current_token` and `target`?
-       fun need_catch_up(target: nullable Token): Bool do
-               if target == null then return false
-               return current_token != target
-       end
-
-       # Visit all tokens between `current_token` and `target`.
-       fun catch_up(target: nullable ANode) do
-               if target == null then return
-               if current_token == null then return
-               var token: Token
-               if target isa Token then
-                       token = target
-               else if target isa Prod then
-                       token = target.first_token.as(not null)
-               else
-                       abort
-               end
-               assert current_token.location <= token.location
-               while current_token != token do visit current_token
-       end
-
-       # Visit all tokens between `current_token` and the end of line.
-       fun finish_line do
-               if current_token isa TComment then
-                       adds
-                       visit current_token
-               end
-
-               while current_token isa TEol do skip
-       end
-
-       # The template under construction.
-       private var tpl: nullable Template = null
-
-       # Current indent level.
-       var indent = 0
-
-       # Size of a tabulation in spaces.
-       var tab_size = 8
-
-       # Max line size.
-       var max_size = 80
-
-       # Length of the current line.
-       var current_length = 0
-
-       # Length of the previous line.
-       var previous_length = 0
-
-       # Is the last line a blank line?
-       fun last_line_is_blank: Bool do return previous_length == 0
-
-       # Add `t` to current template.
-       fun add(t: String) do
-               if t.is_empty then return
-               while wait_addn > 0 do
-                       tpl.add "\n"
-                       wait_addn -= 1
-               end
-               tpl.add t
-               current_length += t.length
-       end
-
-       # Add a `'\n'`.
-       fun addn do
-               if current_length == 0 and last_line_is_blank then return
-               previous_length = current_length
-               current_length = 0
-               wait_addn += 1
-       end
-
-       # End of line chars are stored until another char is added.
-       # This avoid empty files with only a '`\n`'.
-       private var wait_addn = 0
-
-       # Add `'\t' * indent`.
-       fun addt do add "\t" * indent
-
-       # Add a space.
-       fun adds do add " "
-
-       fun visit_recv(n_expr: AExpr) do
-               if not n_expr isa AImplicitSelfExpr then
-                       visit n_expr
-                       consume "."
-               end
-       end
-end
-
-# Base framework redefs
-
-redef class ANodes[E]
-       private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
-               for e in self do
-                       var e_can_inline = v.can_inline(e)
-
-                       if e != first then
-                               if not e_can_inline then
-                                       v.add ","
-                                       v.addn
-                                       v.addt
-                                       v.addt
-                               else
-                                       v.add ", "
-                               end
-                       end
-
-                       v.visit e
-               end
-       end
-end
-
-redef class ANode
-       # Start visit of `self` using a `PrettyPrinterVisitor`
-       private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract
-
-       # Collect the length (in `Char`) of the node.
-       private fun collect_length: Int is abstract
-
-       # Is `self` printable in one line?
-       private fun is_inlinable: Bool do return true
-
-       # Force `self` to be rendered as a block.
-       private var force_block = false
-
-       # Does `self` have to be rendered as a block?
-       private fun must_be_block: Bool do return force_block
-
-       # Force `self` to be rendered as a line.
-       private var force_inline = false
-
-       # Does `self` have be rendered as a line?
-       private fun must_be_inline: Bool do
-               if parent != null and parent.must_be_inline then return true
-               return force_inline
-       end
-
-       # Does `self` was written in one line before transformation?
-       private fun was_inline: Bool is abstract
-end
-
-redef class Token
-       redef fun accept_pretty_printer(v) do
-               v.add text.trim
-               v.current_token = next_token
-       end
-
-       redef fun collect_length do return text.length
-       redef fun is_inlinable do return true
-       redef fun was_inline do return true
-end
-
-redef class Prod
-       redef fun accept_pretty_printer(v) do v.visit first_token
-
-       # The token where the production really start (skipping ADoc).
-       private fun start_token: nullable Token do return first_token
-
-       # Collect all `TComment` contained in the production
-       # (between `start_token` and `end_token`).
-       private fun collect_comments: Array[TComment] do
-               var res = new Array[TComment]
-               if start_token == null or last_token == null then return res
-               var token = start_token
-
-               while token != last_token do
-                       if token isa TComment then res.add token
-                       token = token.next_token
-               end
-
-               return res
-       end
-
-       redef fun collect_length do
-               var res = 0
-               if start_token == null or last_token == null then return res
-               var token = start_token
-
-               while token != last_token do
-                       res += token.text.length
-                       token = token.next_token
-               end
-
-               res += token.text.length
-               return res
-       end
-
-       redef fun was_inline do
-               return first_token.location.line_start == last_token.location.line_end
-       end
-end
-
-# Comments
-
-redef class TComment
-       redef fun accept_pretty_printer(v) do
-               if is_adoc then
-                       v.addt
-                       super
-                       v.addn
-                       return
-               end
-
-               if is_licence then
-                       super
-                       v.addn
-                       if is_last_in_group then v.addn
-                       return
-               end
-
-               if is_orphan then
-                       v.addn
-                       v.addt
-                       super
-                       v.addn
-                       v.addn
-                       return
-               end
-
-               if is_inline then
-                       if next_token isa TComment and is_first_in_group then v.addn
-                       v.addt
-                       super
-                       v.addn
-                       var prev_token = self.prev_token
-                       if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
-                       return
-               end
-
-               super
-       end
-
-       # Is `self` part of an `ADoc`?
-       private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null
-
-       # Is `self` part of a licence?
-       private fun is_licence: Bool do
-               var prev_token = self.prev_token
-
-               if prev_token == null then
-                       return true
-               else if prev_token isa TComment then
-                       return prev_token.is_licence
-               else
-                       return false
-               end
-       end
-
-       # Is `self` starts and ends its line?
-       private fun is_inline: Bool do
-               return self == first_real_token_in_line and self == last_real_token_in_line
-       end
-
-       # Is `self` an orphan line (blank before and after)?
-       private fun is_orphan: Bool do
-               return prev_token isa TEol and
-                  (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
-                  next_token isa TEol
-       end
-
-       # Is `self` the first comment of a group of comment?
-       private fun is_first_in_group: Bool do return not prev_token isa TComment
-
-       # Is `self` the last comment of a group of comments?
-       private fun is_last_in_group: Bool do return not next_token isa TComment
-end
-
-redef class ADoc
-       redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
-       redef fun is_inlinable do return n_comment.length <= 1
-end
-
-# Annotations
-
-redef class AAnnotations
-       redef fun accept_pretty_printer(v) do
-               v.adds
-               v.consume "is"
-
-               if v.can_inline(self) then
-                       v.adds
-                       for n_item in n_items do
-                               v.visit n_item
-                               if n_item != n_items.last then
-                                       v.add ", "
-                               end
-                       end
-                       v.finish_line
-               else if n_items.length > 1 then
-                       v.addn
-                       v.indent += 1
-
-                       for n_item in n_items do
-                               v.addt
-                               v.visit n_item
-                               v.finish_line
-                               v.addn
-                       end
-
-                       v.indent -= 1
-               end
-               if not was_inline and v.current_token isa TKwend then v.skip
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               for annot in n_items do if not annot.is_inlinable then return false
-               return true
-       end
-end
-
-redef class AAnnotation
-       redef fun accept_pretty_printer(v) do
-               v.visit n_atid
-               if not n_args.is_empty then
-                       if n_opar == null then
-                               v.adds
-                       else
-                               v.visit n_opar
-                       end
-                       v.visit_list n_args
-                       v.visit n_cpar
-               end
-       end
-end
-
-redef class ATypeExpr
-       redef fun accept_pretty_printer(v) do v.visit n_type
-end
-
-# Modules
-
-redef class AModule
-       redef fun accept_pretty_printer(v) do
-               v.catch_up start_token
-               v.visit n_moduledecl
-
-               if not n_imports.is_empty then
-                       v.addn
-
-                       for n_import in n_imports do
-                               v.catch_up n_import
-                               v.visit n_import
-                       end
-               end
-
-               if not n_extern_code_blocks.is_empty then
-                       v.addn
-
-                       for n_extern_block in n_extern_code_blocks do
-                               v.catch_up n_extern_block
-                               v.visit n_extern_block
-                               v.addn
-                               if n_extern_block != n_extern_code_blocks.last then v.addn
-                       end
-
-                       if not n_classdefs.is_empty then v.addn
-               end
-
-               if not n_classdefs.is_empty then
-                       v.addn
-
-                       for n_classdef in n_classdefs do
-                               v.catch_up n_classdef
-                               v.visit n_classdef
-                               if n_classdef != n_classdefs.last then v.addn
-                       end
-               end
-
-               assert v.indent == 0
-       end
-
-       # Skip doc if any.
-       redef fun start_token do
-               if n_moduledecl != null then return n_moduledecl.first_token
-               if not n_imports.is_empty then return n_imports.first.first_token
-               if not n_classdefs.is_empty then return n_classdefs.first.first_token
-               return first_token
-       end
-
-       redef fun is_inlinable do return false
-end
-
-redef class AModuledecl
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               v.visit n_kwmodule
-               v.adds
-               v.visit n_name
-
-               if n_annotations != null then
-                       var annot_inline = v.can_inline(n_annotations)
-                       v.visit n_annotations
-
-                       if not annot_inline then
-                               if v.current_token isa TKwend then
-                                       v.consume "end"
-                                       v.finish_line
-                               else
-                                       v.add "end"
-                               end
-                       end
-               end
-
-               v.finish_line
-               v.addn
-       end
-end
-
-redef class AModuleName
-       redef fun accept_pretty_printer(v) do
-               for path in n_path do
-                       v.visit path
-                       v.add "::"
-               end
-
-               v.visit n_id
-       end
-end
-
-redef class ANoImport
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwimport
-               v.adds
-               v.visit n_kwend
-               v.finish_line
-               v.addn
-       end
-end
-
-redef class AStdImport
-       redef fun accept_pretty_printer(v) do
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               v.visit n_kwimport
-               v.adds
-               v.visit n_name
-               v.finish_line
-               v.addn
-       end
-end
-
-# Classes
-
-redef class AClassdef
-       redef fun accept_pretty_printer(v) do
-               for n_propdef in n_propdefs do
-                       v.catch_up n_propdef
-
-                       if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
-                               if n_propdef != n_propdefs.first then v.addn
-                               v.visit n_propdef
-                               if n_propdef != n_propdefs.last then v.addn
-                       else
-                               v.visit n_propdef
-                       end
-               end
-       end
-end
-
-redef class AStdClassdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               var can_inline = v.can_inline(self)
-
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               if n_kwredef != null then
-                       v.visit n_kwredef
-                       v.adds
-               end
-
-               v.visit n_classkind
-               v.adds
-               v.visit n_id
-
-               if not n_formaldefs.is_empty then
-                       v.consume "["
-                       v.visit_list n_formaldefs
-                       v.consume "]"
-               end
-
-               if n_extern_code_block != null then
-                       v.adds
-                       v.visit n_extern_code_block
-               end
-
-               if can_inline then
-                       v.adds
-
-                       if not n_superclasses.is_empty then
-                               for n_superclass in n_superclasses do
-                                       v.visit n_superclass
-                                       v.adds
-                               end
-                       end
-               else
-                       v.finish_line
-                       v.addn
-                       v.indent += 1
-
-                       for n_superclass in n_superclasses do
-                               v.catch_up n_superclass
-                               v.addt
-                               v.visit n_superclass
-                               v.finish_line
-                               v.addn
-                       end
-
-                       if not n_superclasses.is_empty and not n_propdefs.is_empty then
-                               v.addn
-                       end
-
-                       super
-                       v.catch_up n_kwend
-                       v.indent -= 1
-               end
-
-               v.visit n_kwend
-               v.finish_line
-               v.addn
-               assert v.indent == 0
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if not n_propdefs.is_empty then return false
-               if n_superclasses.length > 1 then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-
-       redef fun start_token do
-               if not n_visibility isa APublicVisibility then return n_visibility.first_token
-               if n_kwredef != null then return n_kwredef
-               return n_classkind.first_token
-       end
-end
-
-redef class AAbstractClasskind
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwabstract
-               v.adds
-               v.visit n_kwclass
-       end
-end
-
-redef class AExternClasskind
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwextern
-               v.adds
-               v.visit n_kwclass
-       end
-end
-
-redef class AFormaldef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-       end
-end
-
-redef class AType
-       redef fun accept_pretty_printer(v) do
-               if n_kwnullable != null then
-                       v.visit n_kwnullable
-                       v.adds
-               end
-
-               v.visit n_id
-
-               if not n_types.is_empty then
-                       v.consume "["
-                       v.visit_list n_types
-                       v.consume "]"
-               end
-       end
-end
-
-redef class ASuperclass
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwsuper
-               v.adds
-               v.visit n_type
-       end
-end
-
-# Properties
-
-redef class APropdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               v.addt
-
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               if n_kwredef != null then
-                       v.visit n_kwredef
-                       v.adds
-               end
-       end
-
-       redef fun start_token do
-               if n_doc == null then return super
-               return n_doc.last_token.next_token
-       end
-end
-
-redef class AAttrPropdef
-       redef fun accept_pretty_printer(v) do
-               super
-               v.visit n_kwvar
-               v.adds
-               v.visit n_id2
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_expr != null then
-                       v.adds
-                       v.consume "="
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_annotations != null then v.visit n_annotations
-               v.finish_line
-               v.addn
-       end
-
-       redef fun first_token do
-               if n_doc != null then return n_doc.first_token
-               if not n_visibility isa APublicVisibility then return n_visibility.first_token
-               if n_kwredef != null then return n_kwredef
-               return n_kwvar
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class ATypePropdef
-       redef fun accept_pretty_printer(v) do
-               super
-               v.visit n_kwtype
-               v.adds
-               v.visit n_id
-               v.consume ":"
-               v.adds
-               v.visit n_type
-               v.finish_line
-               v.addn
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AMethPropdef
-       redef fun accept_pretty_printer(v) do
-               #  TODO: Handle extern annotations
-
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-               super
-               if n_kwinit != null then v.visit n_kwinit
-               if n_kwmeth != null then v.visit n_kwmeth
-               if n_kwnew != null then v.visit n_kwnew
-
-               if not n_methid == null then
-                       v.adds
-                       v.visit n_methid
-               end
-
-               v.visit n_signature
-
-               if n_annotations != null then
-                       v.visit n_annotations
-               else
-                       v.adds
-               end
-
-               if n_extern_calls != null or n_extern_code_block != null then
-                       if n_annotations != null then v.adds
-                       if n_extern_calls != null then v.visit n_extern_calls
-                       if n_extern_code_block != null then v.visit n_extern_code_block
-               end
-
-               var n_block = self.n_block
-
-               if n_block != null then
-                       while not v.current_token isa TKwdo do v.skip
-                       if n_annotations != null then
-                               if v.can_inline(n_annotations) then
-                                       v.adds
-                               else
-                                       v.addt
-                               end
-                       end
-                       v.consume "do"
-
-                       if can_inline then
-                               v.adds
-
-                               if n_block isa ABlockExpr then
-                                       if n_block.n_expr.is_empty then
-                                               v.visit n_block.n_kwend
-                                       else
-                                               v.visit n_block.n_expr.first
-                                               v.current_token = n_block.n_kwend
-                                               v.skip
-                                       end
-                               else
-                                       v.visit n_block
-                                       if v.current_token isa TKwend then v.skip
-                               end
-                       else
-                               v.finish_line
-                               v.addn
-                               v.indent += 1
-
-                               if n_block isa ABlockExpr then
-                                       n_block.force_block = true
-                                       v.visit n_block
-                                       v.catch_up n_block.n_kwend
-                               else
-                                       v.addt
-                                       v.visit n_block
-                                       v.addn
-                               end
-
-                               v.indent -= 1
-                               v.addt
-                               if n_block isa ABlockExpr then
-                                       v.visit n_block.n_kwend
-                               else
-                                       v.add "end"
-                               end
-                       end
-               end
-
-               v.finish_line
-               v.addn
-               assert v.indent == before
-       end
-
-       # Can be inlined if:
-       # * block is empty or can be inlined
-       # * contains no comments
-       redef fun is_inlinable do
-               if not super then return false
-               if n_annotations != null and not n_annotations.is_inlinable then return false
-               if n_block != null and not n_block.is_inlinable then return false
-               if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
-               if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-end
-
-redef class AMainMethPropdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_block
-               v.addn
-       end
-end
-
-redef class ASignature
-       redef fun accept_pretty_printer(v) do
-               if not n_params.is_empty then
-                       v.consume "("
-                       v.visit_list n_params
-                       v.consume ")"
-               end
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-       end
-end
-
-redef class AParam
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_dotdotdot != null then v.visit n_dotdotdot
-       end
-end
-
-# Extern
-
-redef class AExternCalls
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwimport
-
-               if can_inline then
-                       v.adds
-                       v.visit_list n_extern_calls
-               else
-                       v.addn
-                       v.addt
-                       v.addt
-                       v.visit_list n_extern_calls
-               end
-
-               v.adds
-       end
-end
-
-redef class AFullPropExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.visit n_dot
-               v.visit n_methid
-       end
-end
-
-redef class ALocalPropExternCall
-       redef fun accept_pretty_printer(v) do v.visit n_methid
-end
-
-redef class AInitPropExternCall
-       redef fun accept_pretty_printer(v) do v.visit n_type
-end
-
-redef class ACastAsExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_from_type
-               v.visit n_dot
-               v.visit n_kwas
-               v.consume "("
-               v.visit n_to_type
-               v.consume ")"
-       end
-end
-
-redef class AAsNullableExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.consume "."
-               v.visit n_kwas
-               v.adds
-               v.visit n_kwnullable
-       end
-end
-
-redef class AAsNotNullableExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.consume "."
-               v.visit n_kwas
-               v.adds
-               v.visit n_kwnot
-               v.adds
-               v.visit n_kwnullable
-       end
-end
-
-redef class AExternCodeBlock
-       redef fun accept_pretty_printer(v) do
-               if n_in_language != null then
-                       v.visit n_in_language
-                       v.adds
-               end
-
-               v.visit n_extern_code_segment
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               return n_extern_code_segment.is_inlinable
-       end
-end
-
-redef class AInLanguage
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwin
-               v.adds
-               v.visit n_string
-       end
-end
-
-redef class TExternCodeSegment
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if can_inline then
-                       super
-               else
-                       var text = text.substring(2, text.length - 4)
-                       var lines = text.r_trim.split("\n")
-
-                       if text.is_empty then
-                               v.add "`\{`\}"
-                       else
-                               v.add "`\{"
-
-                               if not lines.first.trim.is_empty then
-                                       v.addn
-                                       lines.first.l_trim
-                                       v.indent += 1
-                                       v.addt
-                                       v.indent -= 1
-                               end
-
-                               for line in lines do
-                                       v.add line.r_trim
-                                       v.addn
-                               end
-
-                               v.addt
-                               v.add "`\}"
-                       end
-
-                       v.current_token = next_token
-               end
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               return location.line_start == location.line_end
-       end
-end
-
-# Blocks
-
-redef class ABlockExpr
-       redef fun accept_pretty_printer(v) do
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-
-               if can_inline and not n_expr.is_empty then
-                       v.visit n_expr.first
-                       v.finish_line
-               else
-                       for nexpr in n_expr do
-                               var expr_inline = v.can_inline(nexpr)
-                               if not expr_inline and nexpr != n_expr.first then v.addn
-                               v.catch_up nexpr
-                               v.addt
-                               v.visit nexpr
-                               v.finish_line
-                               v.addn
-                               if not expr_inline and nexpr != n_expr.last then v.addn
-                       end
-               end
-
-               assert v.indent == before
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if not collect_comments.is_empty then return false
-
-               if not n_expr.is_empty then
-                       if n_expr.length > 1 then return false
-                       if not n_expr.first.is_inlinable then return false
-               end
-
-               return true
-       end
-end
-
-redef class AIfExpr
-       redef fun accept_pretty_printer(v) do
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-               v.visit n_kwif
-               v.adds
-
-               if v.can_inline(n_expr) then
-                       v.visit n_expr
-                       v.adds
-               else
-                       v.visit n_expr
-                       v.addn
-                       v.addt
-               end
-
-               # skip comments before `then` token
-               while not v.current_token isa TKwthen do v.skip
-               v.consume "then"
-               var n_else = self.n_else
-
-               if can_inline then
-                       v.adds
-                       if n_then != null then v.visit n_then
-
-                       if has_else(v) then
-                               n_else.force_inline = true
-                               v.adds
-                               v.consume "else"
-                               v.adds
-                               v.visit n_else
-                       else if n_then == null then
-                               v.add "end"
-                       end
-
-                       v.skip_to last_token.last_real_token_in_line
-               else
-                       v.finish_line
-                       v.addn
-                       v.indent += 1
-
-                       if n_then != null then
-                               if n_then isa ABlockExpr then
-                                       n_then.force_block = true
-                                       v.visit n_then
-                               else
-                                       v.addt
-                                       v.visit n_then
-                                       v.addn
-                               end
-                       end
-
-                       if has_else(v) then
-                               while not v.current_token isa TKwelse do
-                                       v.consume v.current_token.text
-                               end
-
-                               v.indent -= 1
-                               v.addt
-                               v.consume "else"
-
-                               if n_else isa AIfExpr then
-                                       n_else.force_block = true
-                                       v.adds
-                                       v.visit n_else
-                               else
-                                       v.finish_line
-                                       v.addn
-                                       v.indent += 1
-
-                                       if n_else isa ABlockExpr then
-                                               n_else.force_block = true
-                                               v.visit n_else
-                                       else
-                                               v.addt
-                                               v.visit n_else
-                                               v.addn
-                                       end
-
-                                       if last_token isa TKwend then
-                                               v.catch_up last_token
-                                               v.indent -= 1
-                                               v.addt
-                                               v.consume "end"
-                                       else
-                                               v.indent -= 1
-                                               v.addt
-                                               v.add "end"
-                                       end
-                               end
-                       else
-                               if last_token.location >= v.current_token.location then v.catch_up last_token
-                               v.indent -= 1
-                               v.addt
-                               v.add "end"
-                               if v.current_token isa TKwend then v.skip
-                       end
-               end
-
-               assert v.indent == before
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if n_then != null and not n_then.is_inlinable then return false
-               var n_else = self.n_else
-               if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
-                  (not n_else isa ABlockExpr and n_else != null) then
-                       return false
-               end
-               if not collect_comments.is_empty then return false
-               return true
-       end
-
-       # Does this `if` statement contains a `else` part?
-       private fun has_else(v: PrettyPrinterVisitor): Bool do
-               var n_else = n_else
-               if n_else == null then return false
-               var n_kwelse = collect_kwelse
-               if n_kwelse == null then return false
-
-               if n_else isa ABlockExpr then
-                       var comments: Array[TComment]
-
-                       if n_then == null then
-                               comments = v.collect_comments(n_expr.last_token, n_else.last_token)
-                       else
-                               comments = v.collect_comments(n_then.last_token, n_else.last_token)
-                       end
-
-                       if not comments.is_empty then return true
-                       return not n_else.n_expr.is_empty
-               end
-
-               return true
-       end
-
-       # Lookup for `else` token in `self`.
-       private fun collect_kwelse: nullable TKwelse do
-               var token = first_token
-
-               while token != last_token do
-                       if token isa TKwelse then return token
-                       token = token.next_token
-               end
-
-               return null
-       end
-end
-
-# Used to factorize work on loops.
-private class ALoopHelper
-       super AExpr
-
-       fun loop_block: nullable ANode is abstract
-       fun loop_label: nullable ANode is abstract
-
-       fun visit_loop_block(v: PrettyPrinterVisitor) do
-               var n_block = loop_block
-               v.finish_line
-               v.addn
-               v.indent += 1
-
-               if n_block isa ABlockExpr then
-                       n_block.force_block = true
-                       v.visit n_block
-                       v.catch_up n_block.n_kwend
-                       v.indent -= 1
-                       v.addt
-                       v.visit n_block.n_kwend
-               else
-                       v.addt
-                       v.visit n_block
-                       v.addn
-                       v.indent -= 1
-                       v.addt
-                       v.add "end"
-               end
-
-               if loop_label != null then
-                       v.adds
-                       v.visit loop_label
-               end
-       end
-
-       fun visit_loop_inline(v: PrettyPrinterVisitor) do
-               var n_block = loop_block
-               v.adds
-
-               if n_block isa ABlockExpr then
-                       if n_block.n_expr.is_empty then
-                               v.visit n_block.n_kwend
-                       else
-                               v.visit n_block.n_expr.first
-                               v.current_token = n_block.n_kwend
-                               v.skip
-                       end
-               else
-                       v.visit n_block
-                       if v.current_token isa TKwend then v.skip
-               end
-
-               if loop_label != null then
-                       v.adds
-                       v.visit loop_label
-               end
-       end
-
-       redef fun is_inlinable do
-               var n_block = loop_block
-               if not super then return false
-               if n_block isa ABlockExpr and not n_block.is_inlinable then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-end
-
-redef class ALoopExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwloop
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class AWhileExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwwhile
-               v.adds
-               v.visit n_expr
-               v.adds
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class ADoExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class AForExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwfor
-               v.adds
-
-               for n_id in n_ids do
-                       v.visit n_id
-                       if n_id != n_ids.last then v.add ", "
-               end
-
-               v.adds
-               v.consume "in"
-               v.adds
-               v.visit n_expr
-               v.adds
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class ABreakExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwbreak
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_label != null then
-                       v.adds
-                       v.visit n_label
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AContinueExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwcontinue
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_label != null then
-                       v.adds
-                       v.visit n_label
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Calls
-
-redef class ASendExpr
-       redef fun is_inlinable do return true
-end
-
-redef class ACallExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit_recv n_expr
-
-               if not n_expr isa AImplicitSelfExpr and not can_inline then
-                       v.addn
-                       v.addt
-                       v.addt
-               end
-
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       if is_stmt and n_args.n_exprs.length == 1 then
-                               v.adds
-                               if v.current_token isa TOpar then v.skip
-                               v.visit n_args.n_exprs.first
-                               if v.current_token isa TCpar then v.skip
-                       else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
-                       end
-               end
-       end
-
-       # Is the call alone on its line?
-       fun is_stmt: Bool do return parent isa ABlockExpr
-end
-
-redef class ACallAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-
-               v.adds
-               v.consume "="
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ACallReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ABraExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-       end
-end
-
-redef class ABraAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ABraReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAssignMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-               v.visit n_assign
-       end
-end
-
-redef class ABraMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_cbra
-       end
-end
-
-redef class ABraassignMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_cbra
-               v.visit n_assign
-       end
-end
-
-redef class AInitExpr
-       redef fun accept_pretty_printer(v) do
-               if not n_expr isa AImplicitSelfExpr then
-                       v.visit n_expr
-                       v.consume "."
-               end
-
-               v.visit n_kwinit
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-       end
-end
-
-redef class ANewExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwnew
-               v.adds
-               v.visit n_type
-
-               if n_id != null then
-                       v.consume "."
-
-                       if not can_inline then
-                               v.addn
-                               v.addt
-                               v.addt
-                       end
-
-                       v.visit n_id
-               end
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Attributes
-
-redef class AAttrExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AAttrAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAttrReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-# Exprs
-
-redef class AVardeclExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwvar
-               v.adds
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_expr != null then
-                       v.adds
-                       v.consume "="
-                       v.adds
-                       v.visit n_expr
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AVarAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAssertExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwassert
-
-               if n_id != null then
-                       v.adds
-                       v.visit n_id
-                       v.consume ":"
-               end
-
-               v.adds
-               v.visit n_expr
-               var n_else = self.n_else
-
-               if n_else != null then
-                       v.adds
-                       v.consume "else"
-
-                       if can_inline then
-                               v.adds
-                               v.visit n_else
-                       else
-                               v.addn
-
-                               if n_else isa ABlockExpr then
-                                       v.indent += 1
-                                       n_else.force_block = true
-                                       v.visit n_else
-                                       v.indent -= 1
-                                       v.addt
-                                       v.visit n_else.n_kwend
-                               else
-                                       v.indent += 1
-                                       v.addt
-                                       v.visit n_else
-                                       v.addn
-                                       v.indent -= 1
-                                       v.addt
-                                       v.add "end"
-                               end
-                       end
-               end
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if n_else != null and not n_else.is_inlinable then return false
-               return true
-       end
-end
-
-redef class AReturnExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwreturn
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-       end
-end
-
-redef class ASuperExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwsuper
-
-               if not n_args.n_exprs.is_empty then
-                       if is_stmt and n_args.n_exprs.length == 1 then
-                               v.adds
-                               if v.current_token isa TOpar then v.skip
-                               v.visit n_args.n_exprs.first
-                               if v.current_token isa TCpar then v.skip
-                       else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
-                       end
-               end
-       end
-
-       # Is the call alone on its line?
-       fun is_stmt: Bool do return self.first_token.is_starting_line
-
-       redef fun is_inlinable do return true
-end
-
-redef class AOnceExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwonce
-               v.adds
-               v.visit n_expr
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AAbortExpr
-       redef fun accept_pretty_printer(v) do v.visit n_kwabort
-       redef fun is_inlinable do return true
-end
-
-redef class ANotExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwnot
-               v.adds
-               v.visit n_expr
-       end
-end
-
-redef class AAsCastExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.consume "."
-               v.visit n_kwas
-               v.visit n_opar
-               v.visit n_type
-               v.visit n_cpar
-       end
-end
-
-redef class AAsNotnullExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.consume "."
-               v.visit n_kwas
-               v.visit n_opar
-               v.visit n_kwnot
-               v.adds
-               v.visit n_kwnull
-               v.visit n_cpar
-       end
-end
-
-# Binops
-
-# Used to factorize work on Or, And, Implies and Binop expressions.
-private class ABinOpHelper
-       super AExpr
-
-       fun bin_expr1: AExpr is abstract
-       fun bin_expr2: AExpr is abstract
-
-       # Operator string
-       fun bin_op: String is abstract
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if not can_inline then
-                       if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
-                          (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
-                          (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
-                       then
-                               bin_expr1.force_block = true
-                       end
-               end
-
-               v.visit bin_expr1
-               v.adds
-               v.consume bin_op
-
-               if can_inline then
-                       v.adds
-                       v.visit bin_expr2
-               else
-                       v.addn
-                       v.addt
-                       v.addt
-                       v.visit bin_expr2
-               end
-       end
-end
-
-redef class AAndExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "and"
-end
-
-redef class AOrExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "or"
-end
-
-redef class AImpliesExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "implies"
-end
-
-redef class ABinopExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-end
-
-redef class AEqExpr
-       redef fun bin_op do return "=="
-end
-
-redef class AGeExpr
-       redef fun bin_op do return ">="
-end
-
-redef class AGgExpr
-       redef fun bin_op do return ">>"
-end
-
-redef class AGtExpr
-       redef fun bin_op do return ">"
-end
-
-redef class ALeExpr
-       redef fun bin_op do return "<="
-end
-
-redef class ALlExpr
-       redef fun bin_op do return "<<"
-end
-
-redef class ALtExpr
-       redef fun bin_op do return "<"
-end
-
-redef class AMinusExpr
-       redef fun bin_op do return "-"
-end
-
-redef class ANeExpr
-       redef fun bin_op do return "!="
-end
-
-redef class APercentExpr
-       redef fun bin_op do return "%"
-end
-
-redef class APlusExpr
-       redef fun bin_op do return "+"
-end
-
-redef class ASlashExpr
-       redef fun bin_op do return "/"
-end
-
-redef class AStarExpr
-       redef fun bin_op do return "*"
-end
-
-redef class AStarstarExpr
-       redef fun bin_op do return "**"
-end
-
-redef class AStarshipExpr
-       redef fun bin_op do return "<=>"
-end
-
-redef class AIsaExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.adds
-               v.consume "isa"
-               v.adds
-               v.visit n_type
-       end
-end
-
-redef class AOrElseExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.adds
-               v.consume "or"
-               v.adds
-               v.consume "else"
-               v.adds
-               v.visit n_expr2
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Syntax
-
-redef class AUminusExpr
-       redef fun accept_pretty_printer(v) do
-               v.consume "-"
-               v.visit n_expr
-       end
-end
-
-redef class ANullExpr
-       redef fun accept_pretty_printer(v) do v.visit n_kwnull
-       redef fun is_inlinable do return true
-end
-
-redef class AParExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_opar
-               v.visit n_expr
-               v.visit n_cpar
-       end
-end
-
-redef class AArrayExpr
-       redef fun accept_pretty_printer(v) do
-               v.consume "["
-               v.visit_list n_exprs.n_exprs
-               v.consume "]"
-       end
-end
-
-redef class ACrangeExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_expr
-               v.consume ".."
-               v.visit n_expr2
-               v.visit n_cbra
-       end
-end
-
-redef class AOrangeExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_expr
-               v.consume ".."
-               v.visit n_expr2
-               v.visit n_cbra
-       end
-end
-
-# Strings
-
-redef class AStringFormExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if can_inline then
-                       v.visit n_string
-               else
-                       var text = n_string.text
-                       var i = 0
-
-                       while i < text.length do
-                               v.add text[i].to_s
-
-                               if v.current_length >= v.max_size and i <= text.length - 3 then
-                                       v.add "\" +"
-                                       v.addn
-                                       v.indent += 1
-                                       v.addt
-                                       v.indent -= 1
-                                       v.add "\""
-                               end
-
-                               i += 1
-                       end
-
-                       v.current_token = n_string.next_token
-               end
-       end
-end
-
-redef class ASuperstringExpr
-       redef fun accept_pretty_printer(v) do
-               for n_expr in n_exprs do v.visit n_expr
-       end
-
-       redef fun must_be_inline do
-               if super then return true
-
-               if not n_exprs.is_empty then
-                       var first = n_exprs.first
-                       return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
-               end
-
-               return false
-       end
-end
+# See `man nitpretty` for more infos.
+import pretty
 
 redef class ToolContext
+       # The working directory used to store temp files.
        var opt_dir = new OptionString("Working directory (default is '.nitpretty')", "--dir")
 
+       # Output pretty printed code with this filename.
        var opt_output = new OptionString("Output name (default is pretty.nit)", "-o",
           "--output")
 
+       # Show diff between source and pretty printed code.
        var opt_diff = new OptionBool("Show diff between source and output", "--diff")
 
+       # Show diff between source and pretty printed code using meld.
        var opt_meld = new OptionBool("Show diff between source and output using meld",
           "--meld")
 
+       # Check formatting instead of pretty printing.
+       #
+       # This option create a tempory pretty printed file then check if
+       # the output of the diff command on the source file and the pretty
+       # printed one is empty.
        var opt_check = new OptionBool("Check format of Nit source files", "--check")
 end
 
index 68d9a47..0a9c436 100644 (file)
@@ -310,7 +310,7 @@ private class PagerMatchesRenderer
                pager.render
        end
 
-       private fun props_fulldoc(pager: Pager, raw_mprops: List[MProperty]) do
+       fun props_fulldoc(pager: Pager, raw_mprops: List[MProperty]) do
                # group by module
                var cats = new HashMap[MModule, Array[MProperty]]
                for mprop in raw_mprops do
diff --git a/src/pretty.nit b/src/pretty.nit
new file mode 100644 (file)
index 0000000..75d9879
--- /dev/null
@@ -0,0 +1,2119 @@
+# 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.
+
+# Library used to pretty print Nit code.
+#
+# Usage:
+#
+#     import parser_util
+#     var tc = new ToolContext
+#     var nmodule = tc.parse_something("class A\nfun   toto :  Int  do   return   5\nend")
+#     var ppv = new PrettyPrinterVisitor
+#     var pmodule = ppv.pretty(nmodule)
+#     assert pmodule.write_to_string == """
+#     class A
+#     \tfun toto: Int do return 5
+#     end"""
+#
+# See `nitpretty` tool for more documentation.
+module pretty
+
+import template
+import toolcontext
+import modelbuilder
+import astutil
+
+# The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
+#
+# The main method here is `visit` that performs the pretty printing of a `ANode`.
+#
+# Because some tokens like `TComment` are not stored in the AST,
+# we visit the AST like traditionnal visitor and also maintain a
+# pointer to the `current_token` (actually the next one to be printed).
+#
+# Visited productions are in charges to move the token pointer using methods such as:
+#
+# * `consume`: print `current_token` and move to the next one
+# * `skip`: move to the next token without printing the current one
+# * `skip_to`: move to a specified token skipping all the tokens before
+# * `catch_up`: consume all the tokens between `current_token` and a specified token
+# * `finish_line`: consume all the tokens between `current_token` and the end of line
+class PrettyPrinterVisitor
+       # Pretty print `n`.
+       fun pretty(n: ANode): Template do
+               clear
+               n.parentize_tokens
+
+               if n isa Prod then
+                       current_token = n.first_token
+                       visit n
+               else if n isa Token then
+                       var p = n.parent
+
+                       while p != null and not p isa Prod do
+                               p = p.parent
+                       end
+
+                       current_token = p.first_token
+                       visit p
+               end
+
+               return tpl.as(not null)
+       end
+
+       # Pretty print the whole `nmodule` with comments before and after.
+       fun pretty_nmodule(nmodule: AModule): Template do
+               clear
+               nmodule.parentize_tokens
+               current_token = nmodule.location.file.first_token
+               visit nmodule
+               catch_up nmodule.location.file.last_token
+               tpl.add "\n"
+               return tpl.as(not null)
+       end
+
+       # Prepare `self` for a new visit.
+       private fun clear do
+               tpl = new Template
+               current_token = null
+               indent = 0
+               current_length = 0
+               previous_length = 0
+               wait_addn = 0
+       end
+
+       # Visit `n` if not null.
+       fun visit(n: nullable ANode) do
+               if n == null then return
+               n.accept_pretty_printer self
+       end
+
+       # Visit a list of `Anode`.
+       fun visit_list(n: nullable ANodes[ANode]) do
+               if n == null then return
+               n.accept_pretty_printer self
+       end
+
+       # Is the node inlinable and can fit on the line.
+       fun can_inline(n: nullable ANode): Bool do
+               if n == null then return true
+               if n.must_be_inline then return true
+               if n.must_be_block then return false
+               # check length
+               if n.collect_length + current_length > max_size then return false
+               # check block is inlinable
+               return n.is_inlinable
+       end
+
+       # Collect all `TComment` between `from` and `to`.
+       fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
+               var res = new Array[TComment]
+               if from isa Prod then from = from.first_token
+               if to isa Prod then to = to.first_token
+               if from == null or to == null then return res
+
+               while from != to do
+                       if from isa TComment then res.add from
+                       from = from.as(Token).next_token
+               end
+
+               return res
+       end
+
+       # Token under cursor.
+       #
+       # This is the next token to visit.
+       var current_token: nullable Token = null
+
+       # Skip the `current_token`.
+       fun skip do current_token = current_token.next_token
+
+       # Skip `current_token` until the end of line.
+       fun skip_line do current_token = current_token.last_real_token_in_line
+
+       # Skip `current_token` until `target` is reached.
+       fun skip_to(target: nullable Token) do
+               if target == null then return
+               while current_token != target do skip
+       end
+
+       # Visit `current_token`.
+       fun consume(token: String) do
+               assert current_token.text == token
+               visit current_token
+       end
+
+       # Is there token to visit between `current_token` and `target`?
+       fun need_catch_up(target: nullable Token): Bool do
+               if target == null then return false
+               return current_token != target
+       end
+
+       # Visit all tokens between `current_token` and `target`.
+       fun catch_up(target: nullable ANode) do
+               if target == null then return
+               if current_token == null then return
+               var token: Token
+               if target isa Token then
+                       token = target
+               else if target isa Prod then
+                       token = target.first_token.as(not null)
+               else
+                       abort
+               end
+               assert current_token.location <= token.location
+               while current_token != token do visit current_token
+       end
+
+       # Visit all tokens between `current_token` and the end of line.
+       fun finish_line do
+               if current_token isa TComment then
+                       adds
+                       visit current_token
+               end
+
+               while current_token isa TEol do skip
+       end
+
+       # The template under construction.
+       private var tpl: nullable Template = null
+
+       # Current indent level.
+       var indent = 0
+
+       # Size of a tabulation in spaces.
+       var tab_size = 8
+
+       # Max line size.
+       var max_size = 80
+
+       # Length of the current line.
+       var current_length = 0
+
+       # Length of the previous line.
+       var previous_length = 0
+
+       # Is the last line a blank line?
+       fun last_line_is_blank: Bool do return previous_length == 0
+
+       # Add `t` to current template.
+       fun add(t: String) do
+               if t.is_empty then return
+               while wait_addn > 0 do
+                       tpl.add "\n"
+                       wait_addn -= 1
+               end
+               tpl.add t
+               current_length += t.length
+       end
+
+       # Add a `'\n'`.
+       fun addn do
+               if current_length == 0 and last_line_is_blank then return
+               previous_length = current_length
+               current_length = 0
+               wait_addn += 1
+       end
+
+       # End of line chars are stored until another char is added.
+       # This avoid empty files with only a '`\n`'.
+       private var wait_addn = 0
+
+       # Add `'\t' * indent`.
+       fun addt do add "\t" * indent
+
+       # Add a space.
+       fun adds do add " "
+
+       # Visit explicit receiver, implicit self will be ignored.
+       fun visit_recv(n_expr: AExpr) do
+               if not n_expr isa AImplicitSelfExpr then
+                       visit n_expr
+                       consume "."
+               end
+       end
+end
+
+# Base framework redefs
+
+redef class ANodes[E]
+       private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
+               for e in self do
+                       var e_can_inline = v.can_inline(e)
+
+                       if e != first then
+                               if not e_can_inline then
+                                       v.add ","
+                                       v.addn
+                                       v.addt
+                                       v.addt
+                               else
+                                       v.add ", "
+                               end
+                       end
+
+                       v.visit e
+               end
+       end
+end
+
+redef class ANode
+       # Start visit of `self` using a `PrettyPrinterVisitor`
+       private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract
+
+       # Collect the length (in `Char`) of the node.
+       private fun collect_length: Int is abstract
+
+       # Is `self` printable in one line?
+       private fun is_inlinable: Bool do return true
+
+       # Force `self` to be rendered as a block.
+       private var force_block = false
+
+       # Does `self` have to be rendered as a block?
+       private fun must_be_block: Bool do return force_block
+
+       # Force `self` to be rendered as a line.
+       private var force_inline = false
+
+       # Does `self` have be rendered as a line?
+       private fun must_be_inline: Bool do
+               if parent != null and parent.must_be_inline then return true
+               return force_inline
+       end
+
+       # Does `self` was written in one line before transformation?
+       private fun was_inline: Bool is abstract
+end
+
+redef class Token
+       redef fun accept_pretty_printer(v) do
+               v.add text.trim
+               v.current_token = next_token
+       end
+
+       redef fun collect_length do return text.length
+       redef fun is_inlinable do return true
+       redef fun was_inline do return true
+end
+
+redef class Prod
+       redef fun accept_pretty_printer(v) do v.visit first_token
+
+       # The token where the production really start (skipping ADoc).
+       private fun start_token: nullable Token do return first_token
+
+       # Collect all `TComment` contained in the production
+       # (between `start_token` and `end_token`).
+       private fun collect_comments: Array[TComment] do
+               var res = new Array[TComment]
+               if start_token == null or last_token == null then return res
+               var token = start_token
+
+               while token != last_token do
+                       if token isa TComment then res.add token
+                       token = token.next_token
+               end
+
+               return res
+       end
+
+       redef fun collect_length do
+               var res = 0
+               if start_token == null or last_token == null then return res
+               var token = start_token
+
+               while token != last_token do
+                       res += token.text.length
+                       token = token.next_token
+               end
+
+               res += token.text.length
+               return res
+       end
+
+       redef fun was_inline do
+               return first_token.location.line_start == last_token.location.line_end
+       end
+end
+
+# Comments
+
+redef class TComment
+       redef fun accept_pretty_printer(v) do
+               if is_adoc then
+                       v.addt
+                       super
+                       v.addn
+                       return
+               end
+
+               if is_licence then
+                       super
+                       v.addn
+                       if is_last_in_group then v.addn
+                       return
+               end
+
+               if is_orphan then
+                       v.addn
+                       v.addt
+                       super
+                       v.addn
+                       v.addn
+                       return
+               end
+
+               if is_inline then
+                       if next_token isa TComment and is_first_in_group then v.addn
+                       v.addt
+                       super
+                       v.addn
+                       var prev_token = self.prev_token
+                       if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
+                       return
+               end
+
+               super
+       end
+
+       # Is `self` part of an `ADoc`?
+       private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null
+
+       # Is `self` part of a licence?
+       private fun is_licence: Bool do
+               var prev_token = self.prev_token
+
+               if prev_token == null then
+                       return true
+               else if prev_token isa TComment then
+                       return prev_token.is_licence
+               else
+                       return false
+               end
+       end
+
+       # Is `self` starts and ends its line?
+       private fun is_inline: Bool do
+               return self == first_real_token_in_line and self == last_real_token_in_line
+       end
+
+       # Is `self` an orphan line (blank before and after)?
+       private fun is_orphan: Bool do
+               return prev_token isa TEol and
+                  (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
+                  next_token isa TEol
+       end
+
+       # Is `self` the first comment of a group of comment?
+       private fun is_first_in_group: Bool do return not prev_token isa TComment
+
+       # Is `self` the last comment of a group of comments?
+       private fun is_last_in_group: Bool do return not next_token isa TComment
+end
+
+redef class ADoc
+       redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
+       redef fun is_inlinable do return n_comment.length <= 1
+end
+
+# Annotations
+
+redef class AAnnotations
+       redef fun accept_pretty_printer(v) do
+               v.adds
+               v.consume "is"
+
+               if v.can_inline(self) then
+                       v.adds
+                       for n_item in n_items do
+                               v.visit n_item
+                               if n_item != n_items.last then
+                                       v.add ", "
+                               end
+                       end
+                       v.finish_line
+               else if n_items.length > 1 then
+                       v.addn
+                       v.indent += 1
+
+                       for n_item in n_items do
+                               v.addt
+                               v.visit n_item
+                               v.finish_line
+                               v.addn
+                       end
+
+                       v.indent -= 1
+               end
+               if not was_inline and v.current_token isa TKwend then v.skip
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               for annot in n_items do if not annot.is_inlinable then return false
+               return true
+       end
+end
+
+redef class AAnnotation
+       redef fun accept_pretty_printer(v) do
+               v.visit n_atid
+               if not n_args.is_empty then
+                       if n_opar == null then
+                               v.adds
+                       else
+                               v.visit n_opar
+                       end
+                       v.visit_list n_args
+                       v.visit n_cpar
+               end
+       end
+end
+
+redef class ATypeExpr
+       redef fun accept_pretty_printer(v) do v.visit n_type
+end
+
+# Modules
+
+redef class AModule
+       redef fun accept_pretty_printer(v) do
+               v.catch_up start_token
+               v.visit n_moduledecl
+
+               if not n_imports.is_empty then
+                       v.addn
+
+                       for n_import in n_imports do
+                               v.catch_up n_import
+                               v.visit n_import
+                       end
+               end
+
+               if not n_extern_code_blocks.is_empty then
+                       v.addn
+
+                       for n_extern_block in n_extern_code_blocks do
+                               v.catch_up n_extern_block
+                               v.visit n_extern_block
+                               v.addn
+                               if n_extern_block != n_extern_code_blocks.last then v.addn
+                       end
+
+                       if not n_classdefs.is_empty then v.addn
+               end
+
+               if not n_classdefs.is_empty then
+                       v.addn
+
+                       for n_classdef in n_classdefs do
+                               v.catch_up n_classdef
+                               v.visit n_classdef
+                               if n_classdef != n_classdefs.last then v.addn
+                       end
+               end
+
+               assert v.indent == 0
+       end
+
+       # Skip doc if any.
+       redef fun start_token do
+               if n_moduledecl != null then return n_moduledecl.first_token
+               if not n_imports.is_empty then return n_imports.first.first_token
+               if not n_classdefs.is_empty then return n_classdefs.first.first_token
+               return first_token
+       end
+
+       redef fun is_inlinable do return false
+end
+
+redef class AModuledecl
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               v.visit n_kwmodule
+               v.adds
+               v.visit n_name
+
+               if n_annotations != null then
+                       var annot_inline = v.can_inline(n_annotations)
+                       v.visit n_annotations
+
+                       if not annot_inline then
+                               if v.current_token isa TKwend then
+                                       v.consume "end"
+                                       v.finish_line
+                               else
+                                       v.add "end"
+                               end
+                       end
+               end
+
+               v.finish_line
+               v.addn
+       end
+end
+
+redef class AModuleName
+       redef fun accept_pretty_printer(v) do
+               for path in n_path do
+                       v.visit path
+                       v.add "::"
+               end
+
+               v.visit n_id
+       end
+end
+
+redef class ANoImport
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwimport
+               v.adds
+               v.visit n_kwend
+               v.finish_line
+               v.addn
+       end
+end
+
+redef class AStdImport
+       redef fun accept_pretty_printer(v) do
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               v.visit n_kwimport
+               v.adds
+               v.visit n_name
+               v.finish_line
+               v.addn
+       end
+end
+
+# Classes
+
+redef class AClassdef
+       redef fun accept_pretty_printer(v) do
+               for n_propdef in n_propdefs do
+                       v.catch_up n_propdef
+
+                       if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
+                               if n_propdef != n_propdefs.first then v.addn
+                               v.visit n_propdef
+                               if n_propdef != n_propdefs.last then v.addn
+                       else
+                               v.visit n_propdef
+                       end
+               end
+       end
+end
+
+redef class AStdClassdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               var can_inline = v.can_inline(self)
+
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               if n_kwredef != null then
+                       v.visit n_kwredef
+                       v.adds
+               end
+
+               v.visit n_classkind
+               v.adds
+               v.visit n_id
+
+               if not n_formaldefs.is_empty then
+                       v.consume "["
+                       v.visit_list n_formaldefs
+                       v.consume "]"
+               end
+
+               if n_extern_code_block != null then
+                       v.adds
+                       v.visit n_extern_code_block
+               end
+
+               if can_inline then
+                       v.adds
+
+                       if not n_superclasses.is_empty then
+                               for n_superclass in n_superclasses do
+                                       v.visit n_superclass
+                                       v.adds
+                               end
+                       end
+               else
+                       v.finish_line
+                       v.addn
+                       v.indent += 1
+
+                       for n_superclass in n_superclasses do
+                               v.catch_up n_superclass
+                               v.addt
+                               v.visit n_superclass
+                               v.finish_line
+                               v.addn
+                       end
+
+                       if not n_superclasses.is_empty and not n_propdefs.is_empty then
+                               v.addn
+                       end
+
+                       super
+                       v.catch_up n_kwend
+                       v.indent -= 1
+               end
+
+               v.visit n_kwend
+               v.finish_line
+               v.addn
+               assert v.indent == 0
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if not n_propdefs.is_empty then return false
+               if n_superclasses.length > 1 then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+
+       redef fun start_token do
+               if not n_visibility isa APublicVisibility then return n_visibility.first_token
+               if n_kwredef != null then return n_kwredef
+               return n_classkind.first_token
+       end
+end
+
+redef class AAbstractClasskind
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwabstract
+               v.adds
+               v.visit n_kwclass
+       end
+end
+
+redef class AExternClasskind
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwextern
+               v.adds
+               v.visit n_kwclass
+       end
+end
+
+redef class AFormaldef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+       end
+end
+
+redef class AType
+       redef fun accept_pretty_printer(v) do
+               if n_kwnullable != null then
+                       v.visit n_kwnullable
+                       v.adds
+               end
+
+               v.visit n_id
+
+               if not n_types.is_empty then
+                       v.consume "["
+                       v.visit_list n_types
+                       v.consume "]"
+               end
+       end
+end
+
+redef class ASuperclass
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwsuper
+               v.adds
+               v.visit n_type
+       end
+end
+
+# Properties
+
+redef class APropdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               v.addt
+
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               if n_kwredef != null then
+                       v.visit n_kwredef
+                       v.adds
+               end
+       end
+
+       redef fun start_token do
+               if n_doc == null then return super
+               return n_doc.last_token.next_token
+       end
+end
+
+redef class AAttrPropdef
+       redef fun accept_pretty_printer(v) do
+               super
+               v.visit n_kwvar
+               v.adds
+               v.visit n_id2
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_expr != null then
+                       v.adds
+                       v.consume "="
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_annotations != null then v.visit n_annotations
+               v.finish_line
+               v.addn
+       end
+
+       redef fun first_token do
+               if n_doc != null then return n_doc.first_token
+               if not n_visibility isa APublicVisibility then return n_visibility.first_token
+               if n_kwredef != null then return n_kwredef
+               return n_kwvar
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class ATypePropdef
+       redef fun accept_pretty_printer(v) do
+               super
+               v.visit n_kwtype
+               v.adds
+               v.visit n_id
+               v.consume ":"
+               v.adds
+               v.visit n_type
+               v.finish_line
+               v.addn
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AMethPropdef
+       redef fun accept_pretty_printer(v) do
+               #  TODO: Handle extern annotations
+
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+               super
+               if n_kwinit != null then v.visit n_kwinit
+               if n_kwmeth != null then v.visit n_kwmeth
+               if n_kwnew != null then v.visit n_kwnew
+
+               if not n_methid == null then
+                       v.adds
+                       v.visit n_methid
+               end
+
+               v.visit n_signature
+
+               if n_annotations != null then
+                       v.visit n_annotations
+               else
+                       v.adds
+               end
+
+               if n_extern_calls != null or n_extern_code_block != null then
+                       if n_annotations != null then v.adds
+                       if n_extern_calls != null then v.visit n_extern_calls
+                       if n_extern_code_block != null then v.visit n_extern_code_block
+               end
+
+               var n_block = self.n_block
+
+               if n_block != null then
+                       while not v.current_token isa TKwdo do v.skip
+                       if n_annotations != null then
+                               if v.can_inline(n_annotations) then
+                                       v.adds
+                               else
+                                       v.addt
+                               end
+                       end
+                       v.consume "do"
+
+                       if can_inline then
+                               v.adds
+
+                               if n_block isa ABlockExpr then
+                                       if n_block.n_expr.is_empty then
+                                               v.visit n_block.n_kwend
+                                       else
+                                               v.visit n_block.n_expr.first
+                                               v.current_token = n_block.n_kwend
+                                               v.skip
+                                       end
+                               else
+                                       v.visit n_block
+                                       if v.current_token isa TKwend then v.skip
+                               end
+                       else
+                               v.finish_line
+                               v.addn
+                               v.indent += 1
+
+                               if n_block isa ABlockExpr then
+                                       n_block.force_block = true
+                                       v.visit n_block
+                                       v.catch_up n_block.n_kwend
+                               else
+                                       v.addt
+                                       v.visit n_block
+                                       v.addn
+                               end
+
+                               v.indent -= 1
+                               v.addt
+                               if n_block isa ABlockExpr then
+                                       v.visit n_block.n_kwend
+                               else
+                                       v.add "end"
+                               end
+                       end
+               end
+
+               v.finish_line
+               v.addn
+               assert v.indent == before
+       end
+
+       # Can be inlined if:
+       # * block is empty or can be inlined
+       # * contains no comments
+       redef fun is_inlinable do
+               if not super then return false
+               if n_annotations != null and not n_annotations.is_inlinable then return false
+               if n_block != null and not n_block.is_inlinable then return false
+               if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
+               if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+end
+
+redef class AMainMethPropdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_block
+               v.addn
+       end
+end
+
+redef class ASignature
+       redef fun accept_pretty_printer(v) do
+               if not n_params.is_empty then
+                       v.consume "("
+                       v.visit_list n_params
+                       v.consume ")"
+               end
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+       end
+end
+
+redef class AParam
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_dotdotdot != null then v.visit n_dotdotdot
+       end
+end
+
+# Extern
+
+redef class AExternCalls
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwimport
+
+               if can_inline then
+                       v.adds
+                       v.visit_list n_extern_calls
+               else
+                       v.addn
+                       v.addt
+                       v.addt
+                       v.visit_list n_extern_calls
+               end
+
+               v.adds
+       end
+end
+
+redef class AFullPropExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.visit n_dot
+               v.visit n_methid
+       end
+end
+
+redef class ALocalPropExternCall
+       redef fun accept_pretty_printer(v) do v.visit n_methid
+end
+
+redef class AInitPropExternCall
+       redef fun accept_pretty_printer(v) do v.visit n_type
+end
+
+redef class ACastAsExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_from_type
+               v.visit n_dot
+               v.visit n_kwas
+               v.consume "("
+               v.visit n_to_type
+               v.consume ")"
+       end
+end
+
+redef class AAsNullableExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.consume "."
+               v.visit n_kwas
+               v.adds
+               v.visit n_kwnullable
+       end
+end
+
+redef class AAsNotNullableExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.consume "."
+               v.visit n_kwas
+               v.adds
+               v.visit n_kwnot
+               v.adds
+               v.visit n_kwnullable
+       end
+end
+
+redef class AExternCodeBlock
+       redef fun accept_pretty_printer(v) do
+               if n_in_language != null then
+                       v.visit n_in_language
+                       v.adds
+               end
+
+               v.visit n_extern_code_segment
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               return n_extern_code_segment.is_inlinable
+       end
+end
+
+redef class AInLanguage
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwin
+               v.adds
+               v.visit n_string
+       end
+end
+
+redef class TExternCodeSegment
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if can_inline then
+                       super
+               else
+                       var text = text.substring(2, text.length - 4)
+                       var lines = text.r_trim.split("\n")
+
+                       if text.is_empty then
+                               v.add "`\{`\}"
+                       else
+                               v.add "`\{"
+
+                               if not lines.first.trim.is_empty then
+                                       v.addn
+                                       lines.first.l_trim
+                                       v.indent += 1
+                                       v.addt
+                                       v.indent -= 1
+                               end
+
+                               for line in lines do
+                                       v.add line.r_trim
+                                       v.addn
+                               end
+
+                               v.addt
+                               v.add "`\}"
+                       end
+
+                       v.current_token = next_token
+               end
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               return location.line_start == location.line_end
+       end
+end
+
+# Blocks
+
+redef class ABlockExpr
+       redef fun accept_pretty_printer(v) do
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+
+               if can_inline and not n_expr.is_empty then
+                       v.visit n_expr.first
+                       v.finish_line
+               else
+                       for nexpr in n_expr do
+                               var expr_inline = v.can_inline(nexpr)
+                               if not expr_inline and nexpr != n_expr.first then v.addn
+                               v.catch_up nexpr
+                               v.addt
+                               v.visit nexpr
+                               v.finish_line
+                               v.addn
+                               if not expr_inline and nexpr != n_expr.last then v.addn
+                       end
+               end
+
+               assert v.indent == before
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if not collect_comments.is_empty then return false
+
+               if not n_expr.is_empty then
+                       if n_expr.length > 1 then return false
+                       if not n_expr.first.is_inlinable then return false
+               end
+
+               return true
+       end
+end
+
+redef class AIfExpr
+       redef fun accept_pretty_printer(v) do
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+               v.visit n_kwif
+               v.adds
+
+               if v.can_inline(n_expr) then
+                       v.visit n_expr
+                       v.adds
+               else
+                       v.visit n_expr
+                       v.addn
+                       v.addt
+               end
+
+               # skip comments before `then` token
+               while not v.current_token isa TKwthen do v.skip
+               v.consume "then"
+               var n_else = self.n_else
+
+               if can_inline then
+                       v.adds
+                       if n_then != null then v.visit n_then
+
+                       if has_else(v) then
+                               n_else.force_inline = true
+                               v.adds
+                               v.consume "else"
+                               v.adds
+                               v.visit n_else
+                       else if n_then == null then
+                               v.add "end"
+                       end
+
+                       v.skip_to last_token.last_real_token_in_line
+               else
+                       v.finish_line
+                       v.addn
+                       v.indent += 1
+
+                       if n_then != null then
+                               if n_then isa ABlockExpr then
+                                       n_then.force_block = true
+                                       v.visit n_then
+                               else
+                                       v.addt
+                                       v.visit n_then
+                                       v.addn
+                               end
+                       end
+
+                       if has_else(v) then
+                               while not v.current_token isa TKwelse do
+                                       v.consume v.current_token.text
+                               end
+
+                               v.indent -= 1
+                               v.addt
+                               v.consume "else"
+
+                               if n_else isa AIfExpr then
+                                       n_else.force_block = true
+                                       v.adds
+                                       v.visit n_else
+                               else
+                                       v.finish_line
+                                       v.addn
+                                       v.indent += 1
+
+                                       if n_else isa ABlockExpr then
+                                               n_else.force_block = true
+                                               v.visit n_else
+                                       else
+                                               v.addt
+                                               v.visit n_else
+                                               v.addn
+                                       end
+
+                                       if last_token isa TKwend then
+                                               v.catch_up last_token
+                                               v.indent -= 1
+                                               v.addt
+                                               v.consume "end"
+                                       else
+                                               v.indent -= 1
+                                               v.addt
+                                               v.add "end"
+                                       end
+                               end
+                       else
+                               if last_token.location >= v.current_token.location then v.catch_up last_token
+                               v.indent -= 1
+                               v.addt
+                               v.add "end"
+                               if v.current_token isa TKwend then v.skip
+                       end
+               end
+
+               assert v.indent == before
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if n_then != null and not n_then.is_inlinable then return false
+               var n_else = self.n_else
+               if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
+                  (not n_else isa ABlockExpr and n_else != null) then
+                       return false
+               end
+               if not collect_comments.is_empty then return false
+               return true
+       end
+
+       # Does this `if` statement contains a `else` part?
+       private fun has_else(v: PrettyPrinterVisitor): Bool do
+               var n_else = n_else
+               if n_else == null then return false
+               var n_kwelse = collect_kwelse
+               if n_kwelse == null then return false
+
+               if n_else isa ABlockExpr then
+                       var comments: Array[TComment]
+
+                       if n_then == null then
+                               comments = v.collect_comments(n_expr.last_token, n_else.last_token)
+                       else
+                               comments = v.collect_comments(n_then.last_token, n_else.last_token)
+                       end
+
+                       if not comments.is_empty then return true
+                       return not n_else.n_expr.is_empty
+               end
+
+               return true
+       end
+
+       # Lookup for `else` token in `self`.
+       private fun collect_kwelse: nullable TKwelse do
+               var token = first_token
+
+               while token != last_token do
+                       if token isa TKwelse then return token
+                       token = token.next_token
+               end
+
+               return null
+       end
+end
+
+# Used to factorize work on loops.
+private class ALoopHelper
+       super AExpr
+
+       fun loop_block: nullable ANode is abstract
+       fun loop_label: nullable ANode is abstract
+
+       fun visit_loop_block(v: PrettyPrinterVisitor) do
+               var n_block = loop_block
+               v.finish_line
+               v.addn
+               v.indent += 1
+
+               if n_block isa ABlockExpr then
+                       n_block.force_block = true
+                       v.visit n_block
+                       v.catch_up n_block.n_kwend
+                       v.indent -= 1
+                       v.addt
+                       v.visit n_block.n_kwend
+               else
+                       v.addt
+                       v.visit n_block
+                       v.addn
+                       v.indent -= 1
+                       v.addt
+                       v.add "end"
+               end
+
+               if loop_label != null then
+                       v.adds
+                       v.visit loop_label
+               end
+       end
+
+       fun visit_loop_inline(v: PrettyPrinterVisitor) do
+               var n_block = loop_block
+               v.adds
+
+               if n_block isa ABlockExpr then
+                       if n_block.n_expr.is_empty then
+                               v.visit n_block.n_kwend
+                       else
+                               v.visit n_block.n_expr.first
+                               v.current_token = n_block.n_kwend
+                               v.skip
+                       end
+               else
+                       v.visit n_block
+                       if v.current_token isa TKwend then v.skip
+               end
+
+               if loop_label != null then
+                       v.adds
+                       v.visit loop_label
+               end
+       end
+
+       redef fun is_inlinable do
+               var n_block = loop_block
+               if not super then return false
+               if n_block isa ABlockExpr and not n_block.is_inlinable then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+end
+
+redef class ALoopExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwloop
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class AWhileExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwwhile
+               v.adds
+               v.visit n_expr
+               v.adds
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class ADoExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class AForExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwfor
+               v.adds
+
+               for n_id in n_ids do
+                       v.visit n_id
+                       if n_id != n_ids.last then v.add ", "
+               end
+
+               v.adds
+               v.consume "in"
+               v.adds
+               v.visit n_expr
+               v.adds
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class ABreakExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwbreak
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_label != null then
+                       v.adds
+                       v.visit n_label
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AContinueExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwcontinue
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_label != null then
+                       v.adds
+                       v.visit n_label
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Calls
+
+redef class ASendExpr
+       redef fun is_inlinable do return true
+end
+
+redef class ACallExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit_recv n_expr
+
+               if not n_expr isa AImplicitSelfExpr and not can_inline then
+                       v.addn
+                       v.addt
+                       v.addt
+               end
+
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       if is_stmt and n_args.n_exprs.length == 1 then
+                               v.adds
+                               if v.current_token isa TOpar then v.skip
+                               v.visit n_args.n_exprs.first
+                               if v.current_token isa TCpar then v.skip
+                       else
+                               if v.current_token isa TOpar then
+                                       v.consume "("
+                               else
+                                       v.adds
+                               end
+
+                               v.visit_list n_args.n_exprs
+                               if v.current_token isa TCpar then v.consume ")"
+                       end
+               end
+       end
+
+       # Is the call alone on its line?
+       fun is_stmt: Bool do return parent isa ABlockExpr
+end
+
+redef class ACallAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+
+               v.adds
+               v.consume "="
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ACallReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ABraExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+       end
+end
+
+redef class ABraAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ABraReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAssignMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+               v.visit n_assign
+       end
+end
+
+redef class ABraMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_cbra
+       end
+end
+
+redef class ABraassignMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_cbra
+               v.visit n_assign
+       end
+end
+
+redef class AInitExpr
+       redef fun accept_pretty_printer(v) do
+               if not n_expr isa AImplicitSelfExpr then
+                       v.visit n_expr
+                       v.consume "."
+               end
+
+               v.visit n_kwinit
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+       end
+end
+
+redef class ANewExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwnew
+               v.adds
+               v.visit n_type
+
+               if n_id != null then
+                       v.consume "."
+
+                       if not can_inline then
+                               v.addn
+                               v.addt
+                               v.addt
+                       end
+
+                       v.visit n_id
+               end
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Attributes
+
+redef class AAttrExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AAttrAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAttrReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+# Exprs
+
+redef class AVardeclExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwvar
+               v.adds
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_expr != null then
+                       v.adds
+                       v.consume "="
+                       v.adds
+                       v.visit n_expr
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AVarAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAssertExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwassert
+
+               if n_id != null then
+                       v.adds
+                       v.visit n_id
+                       v.consume ":"
+               end
+
+               v.adds
+               v.visit n_expr
+               var n_else = self.n_else
+
+               if n_else != null then
+                       v.adds
+                       v.consume "else"
+
+                       if can_inline then
+                               v.adds
+                               v.visit n_else
+                       else
+                               v.addn
+
+                               if n_else isa ABlockExpr then
+                                       v.indent += 1
+                                       n_else.force_block = true
+                                       v.visit n_else
+                                       v.indent -= 1
+                                       v.addt
+                                       v.visit n_else.n_kwend
+                               else
+                                       v.indent += 1
+                                       v.addt
+                                       v.visit n_else
+                                       v.addn
+                                       v.indent -= 1
+                                       v.addt
+                                       v.add "end"
+                               end
+                       end
+               end
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if n_else != null and not n_else.is_inlinable then return false
+               return true
+       end
+end
+
+redef class AReturnExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwreturn
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+       end
+end
+
+redef class ASuperExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwsuper
+
+               if not n_args.n_exprs.is_empty then
+                       if is_stmt and n_args.n_exprs.length == 1 then
+                               v.adds
+                               if v.current_token isa TOpar then v.skip
+                               v.visit n_args.n_exprs.first
+                               if v.current_token isa TCpar then v.skip
+                       else
+                               if v.current_token isa TOpar then
+                                       v.consume "("
+                               else
+                                       v.adds
+                               end
+
+                               v.visit_list n_args.n_exprs
+                               if v.current_token isa TCpar then v.consume ")"
+                       end
+               end
+       end
+
+       # Is the call alone on its line?
+       fun is_stmt: Bool do return self.first_token.is_starting_line
+
+       redef fun is_inlinable do return true
+end
+
+redef class AOnceExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwonce
+               v.adds
+               v.visit n_expr
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AAbortExpr
+       redef fun accept_pretty_printer(v) do v.visit n_kwabort
+       redef fun is_inlinable do return true
+end
+
+redef class ANotExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwnot
+               v.adds
+               v.visit n_expr
+       end
+end
+
+redef class AAsCastExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.consume "."
+               v.visit n_kwas
+               v.visit n_opar
+               v.visit n_type
+               v.visit n_cpar
+       end
+end
+
+redef class AAsNotnullExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.consume "."
+               v.visit n_kwas
+               v.visit n_opar
+               v.visit n_kwnot
+               v.adds
+               v.visit n_kwnull
+               v.visit n_cpar
+       end
+end
+
+# Binops
+
+# Used to factorize work on Or, And, Implies and Binop expressions.
+private class ABinOpHelper
+       super AExpr
+
+       fun bin_expr1: AExpr is abstract
+       fun bin_expr2: AExpr is abstract
+
+       # Operator string
+       fun bin_op: String is abstract
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if not can_inline then
+                       if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
+                          (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
+                          (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
+                       then
+                               bin_expr1.force_block = true
+                       end
+               end
+
+               v.visit bin_expr1
+               v.adds
+               v.consume bin_op
+
+               if can_inline then
+                       v.adds
+                       v.visit bin_expr2
+               else
+                       v.addn
+                       v.addt
+                       v.addt
+                       v.visit bin_expr2
+               end
+       end
+end
+
+redef class AAndExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "and"
+end
+
+redef class AOrExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "or"
+end
+
+redef class AImpliesExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "implies"
+end
+
+redef class ABinopExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+end
+
+redef class AEqExpr
+       redef fun bin_op do return "=="
+end
+
+redef class AGeExpr
+       redef fun bin_op do return ">="
+end
+
+redef class AGgExpr
+       redef fun bin_op do return ">>"
+end
+
+redef class AGtExpr
+       redef fun bin_op do return ">"
+end
+
+redef class ALeExpr
+       redef fun bin_op do return "<="
+end
+
+redef class ALlExpr
+       redef fun bin_op do return "<<"
+end
+
+redef class ALtExpr
+       redef fun bin_op do return "<"
+end
+
+redef class AMinusExpr
+       redef fun bin_op do return "-"
+end
+
+redef class ANeExpr
+       redef fun bin_op do return "!="
+end
+
+redef class APercentExpr
+       redef fun bin_op do return "%"
+end
+
+redef class APlusExpr
+       redef fun bin_op do return "+"
+end
+
+redef class ASlashExpr
+       redef fun bin_op do return "/"
+end
+
+redef class AStarExpr
+       redef fun bin_op do return "*"
+end
+
+redef class AStarstarExpr
+       redef fun bin_op do return "**"
+end
+
+redef class AStarshipExpr
+       redef fun bin_op do return "<=>"
+end
+
+redef class AIsaExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.adds
+               v.consume "isa"
+               v.adds
+               v.visit n_type
+       end
+end
+
+redef class AOrElseExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.adds
+               v.consume "or"
+               v.adds
+               v.consume "else"
+               v.adds
+               v.visit n_expr2
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Syntax
+
+redef class AUminusExpr
+       redef fun accept_pretty_printer(v) do
+               v.consume "-"
+               v.visit n_expr
+       end
+end
+
+redef class ANullExpr
+       redef fun accept_pretty_printer(v) do v.visit n_kwnull
+       redef fun is_inlinable do return true
+end
+
+redef class AParExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_opar
+               v.visit n_expr
+               v.visit n_cpar
+       end
+end
+
+redef class AArrayExpr
+       redef fun accept_pretty_printer(v) do
+               v.consume "["
+               v.visit_list n_exprs.n_exprs
+               v.consume "]"
+       end
+end
+
+redef class ACrangeExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_expr
+               v.consume ".."
+               v.visit n_expr2
+               v.visit n_cbra
+       end
+end
+
+redef class AOrangeExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_expr
+               v.consume ".."
+               v.visit n_expr2
+               v.visit n_cbra
+       end
+end
+
+# Strings
+
+redef class AStringFormExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if can_inline then
+                       v.visit n_string
+               else
+                       var text = n_string.text
+                       var i = 0
+
+                       while i < text.length do
+                               v.add text[i].to_s
+
+                               if v.current_length >= v.max_size and i <= text.length - 3 then
+                                       v.add "\" +"
+                                       v.addn
+                                       v.indent += 1
+                                       v.addt
+                                       v.indent -= 1
+                                       v.add "\""
+                               end
+
+                               i += 1
+                       end
+
+                       v.current_token = n_string.next_token
+               end
+       end
+end
+
+redef class ASuperstringExpr
+       redef fun accept_pretty_printer(v) do
+               for n_expr in n_exprs do v.visit n_expr
+       end
+
+       redef fun must_be_inline do
+               if super then return true
+
+               if not n_exprs.is_empty then
+                       var first = n_exprs.first
+                       return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
+               end
+
+               return false
+       end
+end
index 32b13e3..7881db0 100644 (file)
@@ -111,7 +111,7 @@ class RapidTypeAnalysis
        # Return a ready-to-save CSV document objet that agregates informations about live types.
        # Each discovered type is listed in a line, with its status: resolution, liveness, cast-liveness.
        # Note: types are listed in an alphanumeric order to improve human reading.
-       fun live_types_to_csv: CSVDocument
+       fun live_types_to_csv: CsvDocument
        do
                # Gather all kind of type
                var typeset = new HashSet[MType]
@@ -121,7 +121,8 @@ class RapidTypeAnalysis
                typeset.add_all(live_open_cast_types)
                var types = typeset.to_a
                (new CachedAlphaComparator).sort(types)
-               var res = new CSVDocument
+               var res = new CsvDocument
+               res.format = new CsvFormat('"', ';', "\n")
                res.header = ["Type", "Resolution", "Liveness", "Cast-liveness"]
                for t in types do
                        var reso
@@ -130,7 +131,7 @@ class RapidTypeAnalysis
                        if t isa MClassType and (live_types.has(t) or live_open_types.has(t)) then live = "LIVE" else live = "DEAD"
                        var cast
                        if live_cast_types.has(t) or live_open_cast_types.has(t) then cast = "CAST LIVE" else cast = "CAST DEAD"
-                       res.add_line(t, reso, live, cast)
+                       res.add_record(t, reso, live, cast)
                end
                return res
        end
index cdb8ae8..2c58f74 100644 (file)
@@ -178,7 +178,7 @@ private class TypeVisitor
        end
 
 
-       private fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
+       fun visit_expr_cast(node: ANode, nexpr: AExpr, ntype: AType): nullable MType
        do
                var sub = visit_expr(nexpr)
                if sub == null then return null # Forward error
index 0f1b117..37f5c74 100644 (file)
@@ -141,19 +141,26 @@ class TestSuite
                if not toolcontext.test_dir.file_exists then
                        toolcontext.test_dir.mkdir
                end
+               write_to_nit
+               compile
                toolcontext.info("Execute test-suite {mmodule.name}", 1)
                var before_module = self.before_module
-               if not before_module == null then run_case(before_module)
-               for case in test_cases do run_case(case)
+               if not before_module == null then before_module.run
+               for case in test_cases do case.run
                var after_module = self.after_module
-               if not after_module == null then run_case(after_module)
+               if not after_module == null then after_module.run
        end
 
-       # Execute a test case
-       fun run_case(test_case: TestCase) do
-               test_case.write_to_nit
-               test_case.compile
-               test_case.run
+       # Write the test unit for `self` in a nit compilable file.
+       fun write_to_nit do
+               var file = new Template
+               file.addn "intrude import test_suite"
+               file.addn "import {mmodule.name}\n"
+               file.addn "var name = args.first"
+               for case in test_cases do
+                       case.write_to_nit(file)
+               end
+               file.write_to_file("{test_file}.nit")
        end
 
        # Return the test suite in XML format compatible with Jenkins.
@@ -161,56 +168,22 @@ class TestSuite
        fun to_xml: HTMLTag do
                var n = new HTMLTag("testsuite")
                n.attr("package", mmodule.name)
-               for test in test_cases do n.add test.to_xml
+               if failure != null then
+                       var f = new HTMLTag("failure")
+                       f.attr("message", failure.to_s)
+                       n.add f
+               else
+                       for test in test_cases do n.add test.to_xml
+               end
                return n
        end
-end
-
-# A test case is a unit test considering only a `MMethodDef`.
-class TestCase
-
-       # Test suite wich `self` belongs to.
-       var test_suite: TestSuite
-
-       # Test method to be compiled and tested.
-       var test_method: MMethodDef
-
-       # `ToolContext` to use to display messages and find `nitg` bin.
-       var toolcontext: ToolContext
-
-       # `MMethodDef` to call before the test case.
-       var before_test: nullable MMethodDef = null
-
-       # `MMethodDef` to call after the test case.
-       var after_test: nullable MMethodDef = null
 
        # Generated test file name.
        fun test_file: String do
-               var dir = toolcontext.test_dir
-               var mod = test_method.mclassdef.mmodule.name
-               var cls = test_method.mclassdef.name
-               var name = test_method.name
-               return "{dir}/{mod}_{cls}_{name}"
-       end
-
-       # Generate the test unit in a nit file.
-       fun write_to_nit do
-               var name = test_method.name
-               var file = new Template
-               file.addn "intrude import test_suite"
-               file.addn "import {test_method.mclassdef.mmodule.name}\n"
-               if test_method.mproperty.is_toplevel then
-                       file.addn name
-               else
-                       file.addn "var subject = new {test_method.mclassdef.name}.nitunit"
-                       if before_test != null then file.addn "subject.{before_test.name}"
-                       file.addn "subject.{name}"
-                       if after_test != null then file.addn "subject.{after_test.name}"
-               end
-               file.write_to_file("{test_file}.nit")
+               return toolcontext.test_dir / "gen_{mmodule.name.escape_to_c}"
        end
 
-       # Compile all test cases in once.
+       # Compile all `test_cases` cases in one file.
        fun compile do
                # find nitg
                var nit_dir = toolcontext.nit_dir
@@ -221,49 +194,86 @@ class TestCase
                end
                # compile test suite
                var file = test_file
-               var include_dir = test_method.mclassdef.mmodule.location.file.filename.dirname
+               var include_dir = mmodule.location.file.filename.dirname
                var cmd = "{nitg} --no-color '{file}.nit' -I {include_dir} -o '{file}.bin' > '{file}.out' 2>&1 </dev/null"
                var res = sys.system(cmd)
                var f = new IFStream.open("{file}.out")
                var msg = f.read_all
                f.close
                # set test case result
-               var loc = test_method.location
+               var loc = mmodule.location
                if res != 0 then
                        failure = msg
-                       toolcontext.warning(loc, "failure", "FAILURE: {test_method.name} (in file {file}.nit): {msg}")
+                       toolcontext.warning(loc, "failure", "FAILURE: {mmodule.name} (in file {file}.nit): {msg}")
                        toolcontext.modelbuilder.failed_tests += 1
                end
                toolcontext.check_errors
        end
 
+       # Error occured during test-suite compilation.
+       var failure: nullable String = null
+end
+
+# A test case is a unit test considering only a `MMethodDef`.
+class TestCase
+
+       # Test suite wich `self` belongs to.
+       var test_suite: TestSuite
+
+       # Test method to be compiled and tested.
+       var test_method: MMethodDef
+
+       # `ToolContext` to use to display messages and find `nitg` bin.
+       var toolcontext: ToolContext
+
+       # `MMethodDef` to call before the test case.
+       var before_test: nullable MMethodDef = null
+
+       # `MMethodDef` to call after the test case.
+       var after_test: nullable MMethodDef = null
+
+       # Generate the test unit for `self` in `file`.
+       fun write_to_nit(file: Template) do
+               var name = test_method.name
+               file.addn "if name == \"{name}\" then"
+               if test_method.mproperty.is_toplevel then
+                       file.addn "\t{name}"
+               else
+                       file.addn "\tvar subject = new {test_method.mclassdef.name}.nitunit"
+                       if before_test != null then file.addn "\tsubject.{before_test.name}"
+                       file.addn "\tsubject.{name}"
+                       if after_test != null then file.addn "\tsubject.{after_test.name}"
+               end
+               file.addn "end"
+       end
+
        # Execute the test case.
        fun run do
                toolcontext.info("Execute test-case {test_method.name}", 1)
                was_exec = true
                if toolcontext.opt_noact.value then return
                # execute
-               var file = test_file
-               var res = sys.system("{file.to_program_name}.bin > '{file}.out1' 2>&1 </dev/null")
-               var f = new IFStream.open("{file}.out1")
+               var method_name = test_method.name
+               var test_file = test_suite.test_file
+               var res_name = "{test_file}_{method_name.escape_to_c}"
+               var res = sys.system("{test_file}.bin {method_name} > '{res_name}.out1' 2>&1 </dev/null")
+               var f = new IFStream.open("{res_name}.out1")
                var msg = f.read_all
                f.close
                # set test case result
                var loc = test_method.location
                if res != 0 then
                        error = msg
-                       toolcontext.warning(loc, "failure", "ERROR: {test_method.name} (in file {file}.nit): {msg}")
+                       toolcontext.warning(loc, "failure",
+                          "ERROR: {method_name} (in file {test_file}.nit): {msg}")
                        toolcontext.modelbuilder.failed_tests += 1
                end
                toolcontext.check_errors
        end
 
-       # Error occured during execution.
+       # Error occured during test-case execution.
        var error: nullable String = null
 
-       # Error occured during compilation.
-       var failure: nullable String = null
-
        # Was the test case executed at least one?
        var was_exec = false
 
@@ -284,11 +294,6 @@ class TestCase
                                n.attr("message", error.to_s)
                                tc.add n
                        end
-                       if failure != null then
-                               n = new HTMLTag("failure")
-                               n.attr("message", failure.to_s)
-                               tc.add n
-                       end
                end
                return tc
        end
@@ -366,8 +371,13 @@ redef class MModule
 end
 
 redef class ModelBuilder
+       # Number of test classes generated.
        var total_classes = 0
+
+       # Number of tests generated.
        var total_tests = 0
+
+       # Number of failed tests.
        var failed_tests = 0
 
        # Run NitUnit test file for mmodule (if exists).
index 3494602..53a99fa 100644 (file)
@@ -45,19 +45,30 @@ class VirtualMachine super NaiveInterpreter
        var memory_manager: MemoryManager = new MemoryManager
 
        # The unique instance of the `MInit` value
-       var initialization_value: Instance
+       var initialization_value: Instance is noinit
 
-       init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String])
+       init
        do
                var init_type = new MInitType(mainmodule.model)
                initialization_value = new MutableInstance(init_type)
                super
        end
 
-       # Subtyping test for the virtual machine
+       # Runtime subtyping test
        redef fun is_subtype(sub, sup: MType): Bool
        do
+               if sub == sup then return true
+
                var anchor = self.frame.arguments.first.mtype.as(MClassType)
+
+               # `sub` or `sup` are formal or virtual types, resolve them to concrete types
+               if sub isa MParameterType or sub isa MVirtualType then
+                       sub = sub.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false)
+               end
+               if sup isa MParameterType or sup isa MVirtualType then
+                       sup = sup.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false)
+               end
+
                var sup_accept_null = false
                if sup isa MNullableType then
                        sup_accept_null = true
@@ -77,11 +88,6 @@ class VirtualMachine super NaiveInterpreter
                end
                # Now the case of direct null and nullable is over
 
-               # An unfixed formal type can only accept itself
-               if sup isa MParameterType or sup isa MVirtualType then
-                       return sub == sup
-               end
-
                if sub isa MParameterType or sub isa MVirtualType then
                        sub = sub.anchor_to(mainmodule, anchor)
                        # Manage the second layer of null/nullable
@@ -100,34 +106,33 @@ class VirtualMachine super NaiveInterpreter
 
                assert sup isa MClassType
 
-               # Create the sup vtable if not create
+               # `sub` and `sup` can be discovered inside a Generic type during the subtyping test
                if not sup.mclass.loaded then create_class(sup.mclass)
-
-               # Sub can be discovered inside a Generic type during the subtyping test
                if not sub.mclass.loaded then create_class(sub.mclass)
 
-               if sup isa MGenericType then
-                       var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass)
-                       assert sub2.mclass == sup.mclass
-
-                       for i in [0..sup.mclass.arity[ do
-                               var sub_arg = sub2.arguments[i]
-                               var sup_arg = sup.arguments[i]
-                               var res = is_subtype(sub_arg, sup_arg)
-
-                               if res == false then return false
-                       end
-                       return true
-               end
-
+               # For now, always use perfect hashing for subtyping test
                var super_id = sup.mclass.vtable.id
                var mask = sub.mclass.vtable.mask
 
-               return inter_is_subtype(super_id, mask, sub.mclass.vtable.internal_vtable)
+               var res = inter_is_subtype_ph(super_id, mask, sub.mclass.vtable.internal_vtable)
+               if res == false then return false
+               # sub and sup can be generic types, each argument of generics has to be tested
+
+               if not sup isa MGenericType then return true
+               var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass)
+
+               # Test each argument of a generic by recursive calls
+               for i in [0..sup.mclass.arity[ do
+                       var sub_arg = sub2.arguments[i]
+                       var sup_arg = sup.arguments[i]
+                       var res2 = is_subtype(sub_arg, sup_arg)
+                       if res2 == false then return false
+               end
+               return true
        end
 
        # Subtyping test with perfect hashing
-       private fun inter_is_subtype(id: Int, mask:Int, vtable: Pointer): Bool `{
+       private fun inter_is_subtype_ph(id: Int, mask:Int, vtable: Pointer): Bool `{
                // hv is the position in hashtable
                int hv = id & mask;
 
@@ -138,6 +143,14 @@ class VirtualMachine super NaiveInterpreter
                return *offset == id;
        `}
 
+       # Subtyping test with Cohen test (direct access)
+       private fun inter_is_subtype_sst(id: Int, position: Int, vtable: Pointer): Bool `{
+               // Direct access to the position given in parameter
+               int tableid = (long unsigned int)((long int *)vtable)[position];
+
+               return id == tableid;
+       `}
+
        # Redef init_instance to simulate the loading of a class
        redef fun init_instance(recv: Instance)
        do
@@ -195,17 +208,21 @@ class VirtualMachine super NaiveInterpreter
                var ret = send_commons(mproperty, args, mtype)
                if ret != null then return ret
 
-               var propdef = method_dispatch(mproperty, recv.vtable.as(not null))
+               var propdef = method_dispatch(mproperty, recv.vtable.as(not null), recv)
 
                return self.call(propdef, args)
        end
 
        # Method dispatch, for a given global method `mproperty`
        # returns the most specific local method in the class corresponding to `vtable`
-       private fun method_dispatch(mproperty: MMethod, vtable: VTable): MMethodDef
+       private fun method_dispatch(mproperty: MMethod, vtable: VTable, recv: Instance): MMethodDef
        do
-               return method_dispatch_ph(vtable.internal_vtable, vtable.mask,
+               if mproperty.intro_mclassdef.mclass.positions_methods[recv.mtype.as(MClassType).mclass] != -1 then
+                       return method_dispatch_sst(vtable.internal_vtable, mproperty.absolute_offset)
+               else
+                       return method_dispatch_ph(vtable.internal_vtable, vtable.mask,
                                mproperty.intro_mclassdef.mclass.vtable.id, mproperty.offset)
+               end
        end
 
        # Execute a method dispatch with perfect hashing
@@ -221,6 +238,17 @@ class VirtualMachine super NaiveInterpreter
                return propdef;
        `}
 
+       # Execute a method dispatch with direct access and return the appropriate `MMethodDef`
+       # `vtable` : Pointer to the internal pointer of the class
+       # `absolute_offset` : Absolute offset from the beginning of the virtual table
+       private fun method_dispatch_sst(vtable: Pointer, absolute_offset: Int): MMethodDef `{
+               // pointer+2 is the position where methods are
+               // Add the offset of property and get the method implementation
+               MMethodDef propdef = (MMethodDef)((long int *)vtable)[absolute_offset];
+
+               return propdef;
+       `}
+
        # Return the value of the attribute `mproperty` for the object `recv`
        redef fun read_attribute(mproperty: MAttribute, recv: Instance): Instance
        do
@@ -349,6 +377,10 @@ redef class MClass
        # True when the class is effectively loaded by the vm, false otherwise
        var loaded: Bool = false
 
+       # Color for Cohen subtyping test : the absolute position of the id
+       # of this class in virtual tables
+       var color: Int
+
        # For each loaded subclass, keep the position of the group of attributes
        # introduced by self class in the object
        var positions_attributes: HashMap[MClass, Int] = new HashMap[MClass, Int]
@@ -373,8 +405,11 @@ redef class MClass
 
                # Absolute offset of attribute from the beginning of the attributes table
                var offset_attributes = 0
-               # Absolute offset of method from the beginning of the methods table
-               var offset_methods = 0
+
+               # Absolute offset of method from the beginning of the methods table,
+               # is initialize to 3 because the first position is empty in the virtual table
+               # and the second and third are respectively class id and delta
+               var offset_methods = 3
 
                # The previous element in `superclasses`
                var previous_parent: nullable MClass = null
@@ -408,12 +443,16 @@ redef class MClass
 
                        offset_attributes += attributes
                        offset_methods += methods
+                       offset_methods += 2 # Because each block starts with an id and the delta
                end
 
                # When all super-classes have their identifiers and vtables, allocate current one
                allocate_vtable(v, ids, nb_methods, nb_attributes, offset_attributes, offset_methods)
                loaded = true
 
+               # Set the absolute position of the identifier of this class in the virtual table
+               color = offset_methods - 2
+
                # The virtual table now needs to be filled with pointer to methods
                superclasses.add(self)
                for cl in superclasses do
@@ -520,7 +559,9 @@ redef class MClass
        private fun superclasses_ordering(v: VirtualMachine): Array[MClass]
        do
                var superclasses = new Array[MClass]
-               superclasses.add_all(ancestors)
+
+               # Add all superclasses of `self`
+               superclasses.add_all(self.in_hierarchy(v.mainmodule).greaters)
 
                var res = new Array[MClass]
                if superclasses.length > 1 then
@@ -627,6 +668,8 @@ end
 
 # Redef to associate an `Instance` to its `VTable`
 redef class Instance
+
+       # Associate a runtime instance to its virtual table which contains methods, types etc.
        var vtable: nullable VTable
 end
 
@@ -635,10 +678,6 @@ class MInitType
        super MType
 
        redef var model: Model
-       protected init(model: Model)
-       do
-               self.model = model
-       end
 
        redef fun to_s do return "InitType"
        redef fun as_nullable do return self
diff --git a/tests/Linux.skip b/tests/Linux.skip
new file mode 100644 (file)
index 0000000..609105e
--- /dev/null
@@ -0,0 +1,3 @@
+cocoa_extern_types
+cocoa_message_box
+hello_cocoa
index cdd5559..0b8c466 100644 (file)
@@ -1,5 +1,5 @@
 
-PROGS=*.nit ../examples/*.nit ../examples/leapfrog/leapfrog.nit ../examples/shoot/shoot_logic.nit ../contrib/pep8analysis/src/pep8analysis ../contrib/nitiwiki/src/nitiwiki ../lib/*.nit ../src/nitdoc.nit ../src/test_parser.nit ../src/nit.nit ../src/nitmetrics.nit ../src/nitg.nit
+PROGS=*.nit ../examples/*.nit ../examples/leapfrog/leapfrog.nit ../examples/shoot/shoot_logic.nit ../contrib/pep8analysis/src/pep8analysis ../contrib/nitiwiki/src/nitiwiki ../lib/*.nit ../src/nitdoc.nit ../src/test_parser.nit ../src/nit.nit ../src/nitmetrics.nit ../src/nitc.nit
 
 all: niti nitg-g nitg-s
 
index c1b4cff..578880a 100644 (file)
@@ -170,12 +170,18 @@ It is a success.
 The `$engine.skip` files (where `$engine` is an engine name, see below) describe tests that are skipped completely on a given engine.
 Usually it used with then engine `niti` because tests are too long.
 
-The `cc.skip` file describes tests that are analysed but no executable is generated.
-Usually it is because expected CC errors or missing C libraries.
+The `cc.skip` file describes tests that are analyzed but no executable is generated.
+Usually it is because of expected CC errors or missing C libraries.
 
 The `exec.skip` file describes tests that compiled but not executed.
 Usually it is because the programs are interactive or run some kind of server.
 
+The `$os.skip` file describes tests that are to be skipped completely on the given OS.
+Usually it is because of OS specific libraries.
+
+The `turing.skip` file describes tests that are to be skipped completely on the Turing cluster doing continuous testing over MPI.
+Usually it is because of an unavailable library or a large work which would not benefit from parallelization.
+
 These `*.skip` files contain a list of patterns that will be used against test names.
 A single substring can thus be used to skip a full family of tests.
 
diff --git a/tests/base_empty_module2.nit b/tests/base_empty_module2.nit
new file mode 100644 (file)
index 0000000..dc498f0
--- /dev/null
@@ -0,0 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import end
similarity index 100%
rename from tests/nitg.args
rename to tests/nitc.args
index f99ea03..8725c3e 100644 (file)
@@ -1,7 +1,11 @@
-nitg
+nitc
 nitdoc
 nitlight
 neo_doxygen_dump
 neo_doxygen_file_compound
 neo_doxygen_graph_empty_project
 neo_doxygen_member_resolve_introducer
+test_docdown
+nith
+nit_pretty
+nitmetrics
index bf8ea1b..cb551a2 100644 (file)
@@ -7,11 +7,10 @@ nit_args4
 nitvm_args1
 nitvm_args3
 nitc_args1
-nitg_args1
-nitg_args3
-nitg_args5
-nitg_args6
-nitg_args8
+nitc_args3
+nitc_args5
+nitc_args6
+nitc_args8
 nitunit_args
 test_docdown_args
 pep8analysis
@@ -19,3 +18,5 @@ emscripten
 nitserial_args
 nitunit_args
 nitpretty_args
+hamming_number
+hailstone
index da1d367..329751f 100644 (file)
@@ -6,10 +6,10 @@ nit_args3
 nitvm_args1
 nitvm_args3
 nitc_args1
-nitg_args1
-nitg_args3
-nitg_args5
-nitg_args6
+nitc_args3
+nitc_args5
+nitc_args6
+nitc_args8
 test_markdown_args1
 pep8analysis
 test_android_platform
index a64bbf0..b0818df 100644 (file)
@@ -1,5 +1,5 @@
-base_as_notnull2.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-base_as_notnull2.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+base_as_notnull2.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+base_as_notnull2.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 1
 1
 2
index a28e2e1..01ee624 100644 (file)
@@ -1,3 +1,3 @@
-alt/base_as_notnull2_alt1.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-alt/base_as_notnull2_alt1.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+alt/base_as_notnull2_alt1.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+alt/base_as_notnull2_alt1.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 alt/base_as_notnull2_alt1.nit:58,7--10: Type error: expected Object, got null
index ccaa594..e6335c5 100644 (file)
@@ -1,5 +1,5 @@
-alt/base_as_notnull2_alt2.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-alt/base_as_notnull2_alt2.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+alt/base_as_notnull2_alt2.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+alt/base_as_notnull2_alt2.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 Runtime error: Cast failed (alt/base_as_notnull2_alt2.nit:40)
 1
 1
index 7ce3a5b..4fb6e73 100644 (file)
@@ -1,3 +1,3 @@
-alt/base_as_notnull2_alt3.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-alt/base_as_notnull2_alt3.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+alt/base_as_notnull2_alt3.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+alt/base_as_notnull2_alt3.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 alt/base_as_notnull2_alt3.nit:64,7--10: Type error: expected Int, got null
index 4b49330..9ac131b 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `B` (alt/base_gen_variance2_alt1.nit:27)
+Runtime error: Cast failed. Expected `F`, got `B` (alt/base_gen_variance2_alt1.nit:27)
 3
index 10f4ba7..27315ba 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `D` (alt/base_gen_variance2_alt2.nit:27)
+Runtime error: Cast failed. Expected `F`, got `D` (alt/base_gen_variance2_alt2.nit:27)
 3
index f450e1f..c6eef59 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `D` (alt/base_gen_variance3_alt1.nit:27)
+Runtime error: Cast failed. Expected `B`, got `D` (alt/base_gen_variance3_alt1.nit:27)
 2
index f912514..3c8702a 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `Char` (alt/base_gen_variance_int_alt1.nit:27)
+Runtime error: Cast failed. Expected `Int`, got `Char` (alt/base_gen_variance_int_alt1.nit:27)
 2
diff --git a/tests/sav/cocoa_extern_types.res b/tests/sav/cocoa_extern_types.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
diff --git a/tests/sav/cocoa_message_box.res b/tests/sav/cocoa_message_box.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/hailstone.res b/tests/sav/hailstone.res
new file mode 100644 (file)
index 0000000..0fb3d98
--- /dev/null
@@ -0,0 +1,2 @@
+Sequence for 27 has 112 begin with: 27, 82, 41, 124 and end with: 8, 4, 2, 1
+The number with longest sequence is 77031 with length of 351
diff --git a/tests/sav/hamming_number.res b/tests/sav/hamming_number.res
new file mode 100644 (file)
index 0000000..72c05b7
--- /dev/null
@@ -0,0 +1,21 @@
+1: 1
+2: 2
+3: 3
+4: 4
+5: 5
+6: 6
+7: 8
+8: 9
+9: 10
+10: 12
+11: 15
+12: 16
+13: 18
+14: 20
+15: 24
+16: 25
+17: 27
+18: 30
+19: 32
+20: 36
+1691: 2125764000
diff --git a/tests/sav/hello_cocoa.res b/tests/sav/hello_cocoa.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
similarity index 54%
rename from tests/sav/nitg.res
rename to tests/sav/nitc.res
index 60ea2fa..daade81 100644 (file)
@@ -1,3 +1,3 @@
-Usage: nitg [OPTION]... file.nit...
+Usage: nitc [OPTION]... file.nit...
 Compiles Nit programs.
 Use --help for help
index 60ea2fa..daade81 100644 (file)
@@ -1,3 +1,3 @@
-Usage: nitg [OPTION]... file.nit...
+Usage: nitc [OPTION]... file.nit...
 Compiles Nit programs.
 Use --help for help
index 3fd326a..4ad3dc3 100644 (file)
@@ -1,3 +1 @@
-Usage: nitls [OPTION]... <file.nit|directory>...
-Lists the projects and/or paths of Nit sources files.
-Use --help for help
+UNDEFINED
index 435add1..accb0d6 100644 (file)
@@ -1,2 +1,5 @@
-base_simple3 (base_simple3.nit)
-project1 (project1)
+\e[1mbase_simple3\e[m (\e[33mbase_simple3.nit\e[m)
+project1 (\e[33mproject1\e[m)
+|--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
+|--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
+`--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
index 813821d..accb0d6 100644 (file)
@@ -1,4 +1,5 @@
-base_simple3 (base_simple3.nit)
-`--base_simple3 (base_simple3.nit)
-project1 (project1)
-`--project1 (project1/project1.nit)
+\e[1mbase_simple3\e[m (\e[33mbase_simple3.nit\e[m)
+project1 (\e[33mproject1\e[m)
+|--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
+|--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
+`--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
index d1705b7..accb0d6 100644 (file)
@@ -1,6 +1,5 @@
-base_simple3 (base_simple3.nit)
-`--base_simple3 (base_simple3.nit)
-project1 (project1)
-|--module1 (project1/module1.nit)
-|--module2 (project1/module2.nit)
-`--project1 (project1/project1.nit)
+\e[1mbase_simple3\e[m (\e[33mbase_simple3.nit\e[m)
+project1 (\e[33mproject1\e[m)
+|--\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
+|--\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
+`--\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
index eb37699..f54c342 100644 (file)
@@ -1,2 +1,4 @@
-base_simple3/base_simple3 (base_simple3.nit)
-project1/project1 (project1/project1.nit)
+base_simple3/\e[1mbase_simple3\e[m (\e[33mbase_simple3.nit\e[m)
+project1/\e[1mmodule1\e[m (\e[33mproject1/module1.nit\e[m)
+project1/\e[1mmodule2\e[m (\e[33mproject1/module2.nit\e[m)
+project1/\e[1mproject1\e[m (\e[33mproject1/project1.nit\e[m)
index e95605c..62be912 100644 (file)
@@ -8,10 +8,10 @@ import serialization
 redef class Deserializer
        redef fun deserialize_class(name)
        do
+               if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
                if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
                if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
                if name == "Array[String]" then return new Array[String].from_deserializer(self)
-               if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
                return super
        end
 end
index dc528b1..b00e342 100644 (file)
@@ -2,7 +2,7 @@ test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit.test_nitunit::X.<class>
 
 test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit-3.nit): .nitunit/test_nitunit-3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
 
-test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/test_test_nitunit_TestX_test_foo1.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
+test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/gen_test_test_nitunit.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
 
 DocUnits:
 Entities: 27; Documented ones: 3; With nitunits: 3; Failures: 2
index 87254bb..9ce343f 100644 (file)
@@ -1 +1 @@
-Runtime error: Assert failed (../lib/c.nit:44)
+Runtime error: Assert failed (../lib/c.nit:45)
index d222f9c..dda3187 100644 (file)
@@ -1,3 +1,3 @@
 World
 Hello
-Hello!
\ No newline at end of file
+Hello!
index 514b4d6..6803f77 100644 (file)
@@ -4,9 +4,9 @@ c
 d
 e
 f
-e
-f
 remove: a
 remove: b
 remove: c
 remove: d
+e
+f
index e11e7e7..fb6d6df 100644 (file)
@@ -1,4 +1,4 @@
 Created in Nit
-Also created in Nit
 Created in Java
+Also created in Nit
 Also created in Java
diff --git a/tests/sav/test_ffi_objc_types_and_callbacks.res b/tests/sav/test_ffi_objc_types_and_callbacks.res
new file mode 100644 (file)
index 0000000..9eb68aa
--- /dev/null
@@ -0,0 +1,2 @@
+From Objective-C: 2468 12.340000 1234
+From Nit: 2468 12.34 1234
diff --git a/tests/sav/test_file_open_fail.res b/tests/sav/test_file_open_fail.res
new file mode 100644 (file)
index 0000000..613542b
--- /dev/null
@@ -0,0 +1,2 @@
+Error: Opening file at 'donotcreate.bing' failed with 'No such file or directory'
+Error: Opening file at 'donotcreate.bing' failed with 'No such file or directory'
index 2972528..9fb5305 100644 (file)
@@ -1,14 +1,14 @@
+Compilation des classes Java ...
+Initialisation de la JVM ...
+---------------------Test 1----------------------
 From java, pushing premier
 From java, pushing deuxi?me
 From java, pushing troisi?me
 From java, popping premier
-From java, popping deuxi?me
-From java, popping troisi?me
-Compilation des classes Java ...
-Initialisation de la JVM ...
----------------------Test 1----------------------
 premier
+From java, popping deuxi?me
 deuxième
+From java, popping troisi?me
 troisième
 --------------------Test 2---------------------
 true
diff --git a/tests/sav/test_ropes_buffer_add_overflow.res b/tests/sav/test_ropes_buffer_add_overflow.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/test_ropes_buffer_reverse.res b/tests/sav/test_ropes_buffer_reverse.res
new file mode 100644 (file)
index 0000000..eb577ea
--- /dev/null
@@ -0,0 +1,2 @@
+//
+yxxxx
diff --git a/tests/sav/test_ropes_buffer_to_s.res b/tests/sav/test_ropes_buffer_to_s.res
new file mode 100644 (file)
index 0000000..5bff6e1
--- /dev/null
@@ -0,0 +1,7 @@
+`clear` and `append`: abcd
+`clear` and `add`: ab
+`add` at `maxlen + 1`: c
+`append` up to `maxlen + 1`: ab
+`reverse`: xyz
+`upper`: foo
+`lower`: BAR
index 4aa3a13..4ad3dc3 100644 (file)
@@ -1,2 +1 @@
-Runtime error: Assert failed (alt/test_sqlite3_nity_alt1.nit:27)
-unable to open database file
+UNDEFINED
index 8d75750..4ad3dc3 100644 (file)
@@ -1,2 +1 @@
-Runtime error: Assert failed (alt/test_sqlite3_nity_alt2.nit:39)
-SQL logic error or missing database
+UNDEFINED
index c1e3e84..6de686a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import stream
+import file
 
-var fd_in = new FDIStream(0)
-var fd_out = new FDOStream(1)
-var fd_err = new FDOStream(2)
+var fd_in = new IFStream.from_fd(0)
+var fd_out = new OFStream.from_fd(1)
+var fd_err = new OFStream.from_fd(2)
 
 fd_out.write("Hello\n")
 
 var s = fd_in.read_line
 fd_out.write(s)
+fd_out.write("\n")
 
 fd_err.write("World\n")
diff --git a/tests/test_ffi_objc_types_and_callbacks.nit b/tests/test_ffi_objc_types_and_callbacks.nit
new file mode 100644 (file)
index 0000000..9b0cd1c
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# 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.
+
+module test_ffi_objc_types_and_callbacks
+
+fun foo(i: Int, f: Float, s: String)
+import bar, String.to_cstring, Int.+ in "ObjC" `{
+       char *cstr = String_to_cstring(s);
+
+       long ii = Int__plus(i, i);
+
+       printf("From Objective-C: %ld %f %s\n", ii, f, cstr);
+
+       Object_bar(recv, ii, f, s);
+`}
+
+fun bar(i: Int, f: Float, s: String)
+do
+       print "From Nit: {i} {f} {s}"
+end
+
+foo(1234, 12.34, "1234")
diff --git a/tests/test_file_open_fail.nit b/tests/test_file_open_fail.nit
new file mode 100644 (file)
index 0000000..f71ed82
--- /dev/null
@@ -0,0 +1,29 @@
+# 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.
+
+var ifs = new IFStream.open("donotcreate.bing")
+
+var s = ifs.read_all
+
+ifs.close
+
+if ifs.last_error != null then print ifs.last_error.as(not null)
+
+ifs.reopen
+
+s = ifs.read_all
+
+ifs.close
+
+if ifs.last_error != null then print ifs.last_error.as(not null)
diff --git a/tests/test_ropes_buffer_add_overflow.nit b/tests/test_ropes_buffer_add_overflow.nit
new file mode 100644 (file)
index 0000000..6482b0e
--- /dev/null
@@ -0,0 +1,28 @@
+# 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.
+
+# Checks that `RopeBuffer.add` does not makes the internal buffer overflow.
+#
+# Note: In order to help repoducibility, this test read an private attribute of
+# the buffer.
+module test_ropes_buffer_add_overflow
+
+import standard
+intrude import ropes
+
+var buffer = new RopeBuffer
+
+buffer.append("x" * maxlen)
+buffer.add 'y'
+assert buffer.rpos <= maxlen else print "{buffer.rpos} > {maxlen}"
diff --git a/tests/test_ropes_buffer_reverse.nit b/tests/test_ropes_buffer_reverse.nit
new file mode 100644 (file)
index 0000000..e4a0170
--- /dev/null
@@ -0,0 +1,29 @@
+# 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.
+
+module test_ropes_buffer_reverse
+
+import standard
+
+redef fun maxlen do return 3
+
+var buffer = new RopeBuffer
+
+buffer.reverse
+print "/{buffer}/"
+
+buffer.append("x" * (maxlen + 1))
+buffer.add 'y'
+buffer.reverse
+print buffer.to_s
diff --git a/tests/test_ropes_buffer_to_s.nit b/tests/test_ropes_buffer_to_s.nit
new file mode 100644 (file)
index 0000000..cc8eb4f
--- /dev/null
@@ -0,0 +1,79 @@
+# 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.
+
+# Checks the immutability of the strings returned by `RopeBuffer.to_s`.
+module test_ropes_buffer_to_s
+
+import standard
+
+# Note: In this sort of test, never print the string more than once: the string
+# itself may cache an flatten representation of itself when `print` calls `to_s`
+# on it. For example, the original bug that motivated the writting of the
+# present test got unoticed until we tried to edit the buffer **before**
+# outputting `s`.
+
+var buffer = new RopeBuffer
+var s: String
+
+sys.stdout.write "`clear` and `append`: "
+buffer.append "abcd"
+s = buffer.to_s
+buffer.clear
+buffer.append "ef"
+print s
+
+sys.stdout.write "`clear` and `add`: "
+buffer.clear
+buffer.add 'a'
+buffer.add 'b'
+s = buffer.to_s
+buffer.clear
+buffer.add 'c'
+print s
+
+sys.stdout.write "`add` at `maxlen + 1`: "
+buffer.clear
+buffer.add 'c'
+s = buffer.to_s
+buffer.append("*" * (maxlen -1))
+buffer.add 'x'
+print s
+
+sys.stdout.write "`append` up to `maxlen + 1`: "
+buffer.clear
+buffer.append "ab"
+s = buffer.to_s
+buffer.append("*" * (maxlen -1))
+print s
+
+sys.stdout.write "`reverse`: "
+buffer.clear
+buffer.append "xyz"
+s = buffer.to_s
+buffer.reverse
+print s
+
+sys.stdout.write "`upper`: "
+buffer.clear
+buffer.append "foo"
+s = buffer.to_s
+buffer.upper
+print s
+
+sys.stdout.write "`lower`: "
+buffer.clear
+buffer.append "BAR"
+s = buffer.to_s
+buffer.lower
+print s
index a893976..7981d06 100644 (file)
@@ -25,7 +25,7 @@ p1 = new IProcess( "sleep", "0.2" )
 p2 = new IProcess( "sleep", "0.1" )
 p3 = new IProcess( "sleep", "0.4" )
 
-var order = new Array[FDStream]
+var order = new Array[FStream]
 var streams = [p1.stream_in, p2.stream_in, p3.stream_in]
 
 while not streams.is_empty do
@@ -33,7 +33,7 @@ while not streams.is_empty do
        if s == null then continue # may have been interrupted
 
        order.add( s )
-       streams.remove( s.as(FDIStream ) )
+       streams.remove( s.as(IFStream ) )
 end
 
 print order[0] == p2.stream_in
index d44d2fd..88487d4 100755 (executable)
@@ -29,9 +29,9 @@ unset NIT_DIR
 shopt -s nullglob
 JAVA_HOME=$(dirname $(dirname $(readlink -f $(which javac))))
 
-paths=`echo $JAVA_HOME/jre/lib/*/{client,server}/`
-paths=($paths) 
-JNI_LIB_PATH=${paths[0]}
+paths=`echo $JAVA_HOME/jre/lib/*/{client,server}/libjvm.so`
+paths=($paths)
+JNI_LIB_PATH=`dirname ${paths[0]}`
 shopt -u nullglob
 
 outdir="out"
@@ -45,7 +45,7 @@ Usage: $e [options] modulenames
 -o option   Pass option to the engine
 -v          Verbose (show tests steps)
 -h          This help
---engine    Use a specific engine (default=nitg)
+--engine    Use a specific engine (default=nitc)
 --noskip    Do not skip a test even if the .skip file matches
 --outdir    Use a specific output folder (default=out/)
 --compdir   Use a specific temporary compilation folder (default=.nit_compile)
@@ -316,6 +316,14 @@ need_skip()
                echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
                return 0
        fi
+
+       # Skip by OS
+       os_skip_file=`uname`.skip
+       if test -e $os_skip_file && echo "$1" | grep -f "$os_skip_file"; then
+               echo "=> $2: [skip os]"
+               echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
+               return 0
+       fi
        return 1
 }
 
@@ -355,7 +363,7 @@ find_nitc()
 verbose=false
 isnode=false
 stop=false
-engine=nitg
+engine=nitc
 noskip=
 savdirs=
 while [ $stop = false ]; do
@@ -374,29 +382,29 @@ done
 enginebinname=$engine
 isinterpret=
 case $engine in
-       nitg)
+       nitc|nitg)
                engine=nitg-s;
-               enginebinname=nitg;
+               enginebinname=nitc;
                OPT="--separate $OPT --compile-dir $compdir"
                savdirs="sav/nitg-common/"
                ;;
-       nitg-s)
-               enginebinname=nitg;
+       nitcs|nitg-s)
+               enginebinname=nitc;
                OPT="--separate $OPT --compile-dir $compdir"
                savdirs="sav/nitg-common/"
                ;;
-       nitg-e)
-               enginebinname=nitg;
+       nitce|nitg-e)
+               enginebinname=nitc;
                OPT="--erasure $OPT --compile-dir $compdir"
                savdirs="sav/nitg-common/"
                ;;
-       nitg-sg)
-               enginebinname=nitg;
+       nitcsg|nitg-sg)
+               enginebinname=nitc;
                OPT="--semi-global $OPT --compile-dir $compdir"
                savdirs="sav/nitg-common/"
                ;;
-       nitg-g)
-               enginebinname=nitg;
+       nitcg|nitg-g)
+               enginebinname=nitc;
                OPT="--global $OPT --compile-dir $compdir"
                savdirs="sav/nitg-common/"
                ;;
@@ -413,7 +421,7 @@ case $engine in
                savdirs="sav/niti/"
                ;;
        emscripten)
-               enginebinname=nitg
+               enginebinname=nitc
                OPT="-m emscripten_nodejs.nit --semi-global $OPT --compile-dir $compdir"
                savdirs="sav/nitg-sg/"
                ;;
diff --git a/tests/turing.skip b/tests/turing.skip
new file mode 100644 (file)
index 0000000..7cac9bd
--- /dev/null
@@ -0,0 +1,20 @@
+nitg
+nitx
+_linux
+android
+gles
+shoot
+curl
+neo
+gtk
+nitcorn
+ffi_objc
+mpi
+pnacl
+emscipten
+nodejs
+nitunit
+test_annot_c_compiler
+base_very
+test_jvm
+test_glsl_validation