Merge: Some gammar improvements
authorJean Privat <jean@pryen.org>
Wed, 22 Oct 2014 22:45:27 +0000 (18:45 -0400)
committerJean Privat <jean@pryen.org>
Wed, 22 Oct 2014 22:45:27 +0000 (18:45 -0400)
A bunch of unrelated grammar work: bugfixes, a feature and stuff for future work.

The only implemented feature is the use of extended method identifiers in annotation (setters and operators)

~~~nit
class A
   var foo: String is writable(real_foo=)
   fun foo=(f: String) do
      print "Hello!"
      real_foo = f
   end
end
~~~

Some additional syntax for future works are now accepted but are ignored (or will crash at compile-time).

* user defined factories in class (eg. define `new` in interface)

~~~
interface Buffer
   new do return new FlatBuffer
end
var x = new Buffer
~~~

* code-block in attributes. when the default/lazy value is more complex than a single expression

~~~
class A
   var foo: Int do
      print "Hello!"
      return 5
   end
end
~~~

* generalized tuples. eg. for multiple returns, but more word is needed #839

~~~
fun foo(a,b: Int)
do
   return (a/b, a%b)
end
~~~

In fact, generalized tuples accepts anything that annotations accepts... (eventually they will be rejected just after parsing)

~~~
var x = (1, Array[Int], for x in a do print x, toto=, -)
# in order: a literal expression, a type, a statement, a method identifier (setter),
# an other method identifier (the minus operator)
~~~

Pull-Request: #832
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

136 files changed:
NOTICE
contrib/brainfuck/README.md [new file with mode: 0644]
contrib/brainfuck/brainfuck.nit [new file with mode: 0644]
contrib/brainfuck/examples/hello.bf [new file with mode: 0644]
contrib/brainfuck/examples/hello2.bf [new file with mode: 0644]
contrib/brainfuck/examples/rot13.bf [new file with mode: 0644]
contrib/nitcc/src/autom.nit
contrib/nitester/Makefile [new file with mode: 0644]
contrib/nitester/src/nitester.nit [new file with mode: 0644]
contrib/opportunity/Makefile [new file with mode: 0644]
contrib/opportunity/README.md [new file with mode: 0644]
contrib/opportunity/src/opportunity_controller.nit [new file with mode: 0644]
contrib/opportunity/src/opportunity_model.nit [new file with mode: 0644]
contrib/opportunity/src/opportunity_web.nit [new file with mode: 0644]
contrib/opportunity/src/templates/boilerplate.nit [new file with mode: 0644]
contrib/opportunity/src/templates/meetup.nit [new file with mode: 0644]
contrib/opportunity/src/templates/meetup_confirmation.nit [new file with mode: 0644]
contrib/opportunity/src/templates/meetup_creation.nit [new file with mode: 0644]
contrib/opportunity/src/templates/templates.nit [new file with mode: 0644]
contrib/opportunity/src/templates/welcome.nit [new file with mode: 0644]
contrib/opportunity/tests/db_tests.nit [new file with mode: 0644]
contrib/opportunity/tests/sav/db_tests.res [new file with mode: 0644]
contrib/pep8analysis/src/pep8analysis_web.nit
examples/mnit_dino/src/game_logic.nit
lib/ai/search.nit
lib/console.nit
lib/counter.nit
lib/io/io.nit [new file with mode: 0644]
lib/io/push_back_reader.nit [new file with mode: 0644]
lib/io/test_push_back_reader.nit [new file with mode: 0644]
lib/mnit/mnit_injected_input.nit
lib/nitcorn/http_request.nit
lib/opts.nit
lib/ordered_tree.nit
lib/pipeline.nit
lib/poset.nit
lib/signals.nit
lib/sqlite3/sqlite3.nit
lib/standard/collection/abstract_collection.nit
lib/standard/collection/sorter.nit
lib/standard/file.nit
lib/standard/math.nit
lib/standard/math_nit.h
lib/standard/queue.nit
lib/standard/stream.nit
lib/standard/string.nit
misc/jenkins/listnit.sh
share/man/Makefile [new file with mode: 0644]
share/man/README.md [new file with mode: 0644]
share/man/nit.md [new file with mode: 0644]
share/man/nitdoc.md [new file with mode: 0644]
share/man/nitg.md [new file with mode: 0644]
share/man/nitlight.md [new file with mode: 0644]
share/man/nitls.md [new file with mode: 0644]
share/man/nitmetrics.md [new file with mode: 0644]
share/man/nitpick.md [new file with mode: 0644]
share/man/nitpretty.md [new file with mode: 0644]
share/man/nitserial.md [new file with mode: 0644]
share/man/nitunit.md [new file with mode: 0644]
share/man/nitx.md [new file with mode: 0644]
src/astbuilder.nit
src/compiler/abstract_compiler.nit
src/compiler/android_annotations.nit
src/compiler/coloring.nit
src/compiler/compiler_ffi.nit
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/compiler/separate_erasure_compiler.nit
src/doc/doc_model.nit
src/doc/doc_templates.nit
src/ffi/java.nit
src/frontend/simple_misc_analysis.nit
src/highlight.nit
src/interpreter/breakpoint.nit [deleted file]
src/interpreter/debugger.nit
src/interpreter/naive_interpreter.nit
src/metrics/tables_metrics.nit
src/mixin.nit [new file with mode: 0644]
src/model/model.nit
src/model/model_viz.nit
src/model_utils.nit
src/modelbuilder.nit
src/modelize/modelize_class.nit
src/modelize/modelize_property.nit
src/neo.nit
src/nit.nit
src/nitpretty.nit
src/nitserial.nit
src/parser/parser_nodes.nit
src/parser/parser_work.nit
src/rapid_type_analysis.nit
src/semantize/auto_super_init.nit
src/semantize/flow.nit
src/semantize/scope.nit
src/semantize/typing.nit
src/testing/testing_doc.nit
src/testing/testing_suite.nit
src/toolcontext.nit
src/transform.nit
src/vm.nit
tests/base_for_finish.nit [new file with mode: 0644]
tests/base_init_auto_refine.nit [new file with mode: 0644]
tests/base_vararg3.nit [new file with mode: 0644]
tests/bench_complex_sort.nit
tests/example_sorter.nit
tests/nit.args
tests/nitg.args
tests/niti.skip
tests/sav/base_eq_null_notnull.res
tests/sav/base_for_finish.res [new file with mode: 0644]
tests/sav/base_init_auto_refine.res [new file with mode: 0644]
tests/sav/base_var_type_evolution_null3.res
tests/sav/base_var_type_evolution_null3_alt1.res
tests/sav/base_vararg3.res [new file with mode: 0644]
tests/sav/base_vararg3_alt1.res [new file with mode: 0644]
tests/sav/base_vararg3_alt2.res [new file with mode: 0644]
tests/sav/base_vararg3_alt3.res [new file with mode: 0644]
tests/sav/base_vararg3_alt4.res [new file with mode: 0644]
tests/sav/nit_args4.res [new file with mode: 0644]
tests/sav/nitg-e/fixme/base_for_finish.res [new file with mode: 0644]
tests/sav/nitg-g/fixme/base_for_finish.res [new file with mode: 0644]
tests/sav/nitg-s/fixme/base_for_finish.res [new file with mode: 0644]
tests/sav/nitg-sg/fixme/base_for_finish.res [new file with mode: 0644]
tests/sav/nitg_args8.res [new file with mode: 0644]
tests/sav/niti/error_needed_method_alt3.res [deleted file]
tests/sav/niti/error_needed_method_alt4.res
tests/sav/nitmetrics_args1.res
tests/sav/test_define.res [new file with mode: 0644]
tests/sav/test_for_abuse.res
tests/sav/test_hash_debug.res
tests/sav/test_toolcontext_args1.res
tests/sav/test_toolcontext_args2.res
tests/test_define.nit [new file with mode: 0644]
tests/test_for_abuse.nit
tests/test_jvm.nit
tests/tests.sh

diff --git a/NOTICE b/NOTICE
index 14107e5..d0fe8c4 100644 (file)
--- a/NOTICE
+++ b/NOTICE
@@ -2,24 +2,51 @@ This product includes software developed as part of the Nit Language
 project ( http://nitlanguage.org ).
 
 Files: *
-Copyright: 2004-2012 Jean Privat <jean@pryen.org>
+Copyright: 2004-2014 Jean Privat <jean@pryen.org>
            2006-2008 Floréal Morandat <morandat@lirmm.fr>
+           2009      Julien Chevalier <chevjulien@gmail.com>
            2009-2011 Jean-Sebastien Gelinas <calestar@gmail.com>
-           2009-2012 Alexis Laferrière <alexis.laf@xymus.net>
-           2009 Julien Chevalier <chevjulien@gmail.com>
-           2011-2012 Alexandre Terrasa <alexandre@moz-concept.com>
+           2009-2014 Alexis Laferrière <alexis.laf@xymus.net>
+           2011      Matthieu Auger <matthieu.auger@gmail.com>
+           2011-2014 Alexandre Terrasa <alexandre@moz-code.org>
+           2012      Alexandre Pennetier <alexandre.pennetier@me.com>
+           2013-2014 Lucas Bajolet <r4pass@hotmail.com>
+           2013      Stefan Lage <lagestfan@gmail.com>
+           2013      Nathan Heu <heu.nathan@courrier.uqam.ca>
+           2013      Matthieu Lucas <lucasmatthieu@gmail.com>
+           2014      Romain Chanoir <romain.chanoir@viacesi.fr>
+           2014      Frédéric Vachon <fredvac@gmail.com>
+           2014      Johan Kayser <johan.kayser@viacesi.fr>
+           2014      Julien Pagès <julien.projet@gmail.com>
+           2014      Geoffrey Hecht <geoffrey.hecht@gmail.com>
+           2014      Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
 License: Apache 2.0 (see LICENSE)
-Comment: The main license of the work, exept for the following exceptions
+Comment: The main license of the work, except for the following exceptions
 
 Files: lib/*
        clib/*
        share/nitdoc/*
-Copyright: 2004-2012 Jean Privat <jean@pryen.org>
+Copyright: 2004-2014 Jean Privat <jean@pryen.org>
            2006-2008 Floréal Morandat <morandat@lirmm.fr>
            2009-2011 Jean-Sebastien Gelinas <calestar@gmail.com>
-           2009-2012 Alexis Laferrière <alexis.laf@xymus.net>
-           2009 Julien Chevalier <chevjulien@gmail.com>
-           2011-2012 Alexandre Terrasa <alexandre@moz-concept.com>
+           2009-2014 Alexis Laferrière <alexis.laf@xymus.net>
+           2009      Julien Chevalier <chevjulien@gmail.com>
+           2011-2014 Alexandre Terrasa <alexandre@moz-concept.com>
+           2012      Alexandre Pennetier <alexandre.pennetier@me.com>
+           2013-2014 Lucas Bajolet <r4pass@hotmail.com>
+           2013      Nathan Heu <heu.nathan@courrier.uqam.ca>
+           2013      Matthieu Lucas <lucasmatthieu@gmail.com>
+           2013      Stefan Lage <lagestfan@gmail.com>
+           2014      Romain Chanoir <romain.chanoir@viacesi.fr>
+           2014      Frédéric Vachon <fredvac@gmail.com>
+           2014      Johan Kayser <johan.kayser@viacesi.fr>
+           2014      Geoffrey Hecht <geoffrey.hecht@gmail.com>
+           2014      Julien Pagès <julien.projet@gmail.com>
+           2014      Christophe Gigax <christophe.gigax@viacesi.fr>
+           2014      Clement de Figueiredo <clement.defigueiredo@gmail.com>
+           2014      Maxime Leroy <maxime.leroy76@gmail.com>
+           2014      Johann Dubois <johann.dubois@outlook.com>
+           2014      Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
 Licence: BSD 2 (see LICENSE-BSD)
 Comment: Use of libraries and resources is basically unrestricted. We hold the copyright
          on the compiler and the tools but not on the programs made by the users.
@@ -29,11 +56,12 @@ Copyright: 2011 John Resig, http://jquery.com/
 Licence: BSD 2 (see LICENSE-BSD)
 
 Files: /misc/gtksourceview/nit.lang
-Copyright: 2009-2012 Alexis Laferrière <alexis.laf@xymus.net>
+Copyright: 2009-2011 Alexis Laferrière <alexis.laf@xymus.net>
+           2011      Jean Privat <jean@pryen.org>
            Based on ruby.lang from
-           2004 Archit Baweja <bighead@users.sourceforge.net>
-           2005 Michael Witrant <mike@lepton.fr>
-           2006 Gabriel Bauman <gbauman@gmail.com>
+           2004      Archit Baweja <bighead@users.sourceforge.net>
+           2005      Michael Witrant <mike@lepton.fr>
+           2006      Gabriel Bauman <gbauman@gmail.com>
 License: GPL 2.0 (see LICENSE-GPL-2)
 Comment: GPL because the original work is GPL
 
diff --git a/contrib/brainfuck/README.md b/contrib/brainfuck/README.md
new file mode 100644 (file)
index 0000000..136b674
--- /dev/null
@@ -0,0 +1,34 @@
+# Brainfuck
+
+Brainfuck is as its name implies a simple Brainfuck interpreter written in Nit.
+
+It has almost as much purposes as the language itself, except it provides a good example for Nit programs that work while being concise.
+
+[Specification](http://www.muppetlabs.com/~breadbox/bf/)
+
+The language is designed to need only a few things :
+
+* One instruction pointer to the current instruction
+* One array of Bytes for all manipulations of data
+* One data pointer to select where to write/read data
+
+Brainfuck a small instruction set, only eight instructions :
+
+* `>`: Increments the data pointer
+* `<`: Decrements the data pointer
+* `+`: Increments the byte in the current cell
+* `-`: Decrements the byte in the current cell
+* `[`: If the current cell's value is 0, jumps to the matching `]`
+* `]`: If the current cell's value is non-zero, jumps to the matching `[`
+* `.`: Writes the current cell's value to stdout
+* `,`: Reads a char from stdin and stores it in the current cell
+
+## How to use
+
+First, compile the interpreter with the Nit compiler/interpreter, and launch the program on a brainfuck source file for interpretation.
+
+Example:
+~~~
+nitg ./brainfuck.nit
+./brainfuck ./examples/hello.bf
+~~~
diff --git a/contrib/brainfuck/brainfuck.nit b/contrib/brainfuck/brainfuck.nit
new file mode 100644 (file)
index 0000000..b614776
--- /dev/null
@@ -0,0 +1,134 @@
+# 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.
+
+# Simple brainfuck interpreter
+module brainfuck
+
+# Interpreter for Brainfuck source code.
+class BFInterpret
+       # Data cells
+       var dr = new Array[Char]
+       # Data pointer
+       var dp = 0
+       # Instruction pointer
+       var ip = 0
+
+       # The program being interpreted
+       var program: String
+
+       # Contains the set of valid instructions, used in next
+       var valid_instr: Set[Char]
+
+       # Starts interpretation of file `filename`
+       init(filename: String) do
+               var ifs = new IFStream.open(filename.simplify_path)
+               valid_instr = new HashSet[Char]
+               valid_instr.add_all "><[].,+-".chars
+               dr.add 0.ascii
+               program = ifs.read_all
+               start
+       end
+
+       # Starts the interpretation of the loaded program
+       fun start do
+               loop
+                       if ip >= program.length then break
+                       eval
+                       next
+               end
+       end
+
+       # Go to the next executable instruction
+       fun next do
+               ip += 1
+               while ip < program.length and not valid_instr.has(program[ip]) do
+                       ip += 1
+               end
+       end
+
+       # Evaluates the current instruction
+       fun eval do
+               var instr = program[ip]
+               if instr == '.' then printn dr[dp]
+               if instr == '[' then
+                       if dr[dp] == 0.ascii then
+                               ip = find_matching_rbra
+                               return
+                       end
+               end
+               if instr == ']' then
+                       if dr[dp] != 0.ascii then
+                               ip = find_matching_lbra
+                               return
+                       end
+               end
+               if instr == '>' then
+                       dp += 1
+                       if dp >= dr.length then dr.add(0.ascii)
+               end
+               if instr == '<' then
+                       dp -= 1
+                       if dp < 0 then abort
+               end
+               if instr == '+' then
+                       dr[dp] = (dr[dp].ascii + 1).ascii
+               end
+               if instr == '-' then
+                       dr[dp] = (dr[dp].ascii - 1).ascii
+               end
+               if instr == ',' then
+                       dr[dp] = getc
+               end
+       end
+
+       # Seeks for the position of the matching `]` for the `[` located at `ip`
+       fun find_matching_rbra: Int do
+               var pos = ip + 1
+               var lbracnt = 0
+               loop
+                       if pos > program.length then abort
+                       if program[pos] == ']' then
+                               if lbracnt > 0 then
+                                       lbracnt -= 1
+                               else
+                                       break
+                               end
+                       end
+                       if program[pos] == '[' then lbracnt += 1
+                       pos += 1
+               end
+               return pos
+       end
+
+       # Seeks for the position of the matching `[` for the `]` located at `ip`
+       fun find_matching_lbra: Int do
+               var pos = ip - 1
+               var rbracnt = 0
+               loop
+                       if pos < 0 then abort
+                       if program[pos] == '[' then
+                               if rbracnt > 0 then
+                                       rbracnt -= 1
+                               else
+                                       break
+                               end
+                       end
+                       if program[pos] == ']' then rbracnt += 1
+                       pos -= 1
+               end
+               return pos
+       end
+end
+
+var i = new BFInterpret(args[0])
diff --git a/contrib/brainfuck/examples/hello.bf b/contrib/brainfuck/examples/hello.bf
new file mode 100644 (file)
index 0000000..8fa0f72
--- /dev/null
@@ -0,0 +1 @@
+++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
diff --git a/contrib/brainfuck/examples/hello2.bf b/contrib/brainfuck/examples/hello2.bf
new file mode 100644 (file)
index 0000000..8fc90f6
--- /dev/null
@@ -0,0 +1,38 @@
+[ This program prints "Hello World!" and a newline to the screen, its
+  length is 106 active command characters [it is not the shortest.]
+  This loop is a "comment loop", it's a simple way of adding a comment
+  to a BF program such that you don't have to worry about any command
+  characters. Any ".", ",", "+", "-", "<" and ">" characters are simply
+  ignored, the "[" and "]" characters just have to be balanced.
+]
++++++ +++               Set Cell #0 to 8
+[
+    >++++               Add 4 to Cell #1; this will always set Cell #1 to 4
+    [                   as the cell will be cleared by the loop
+        >++             Add 2 to Cell #2
+        >+++            Add 3 to Cell #3
+        >+++            Add 3 to Cell #4
+        >+              Add 1 to Cell #5
+        <<<<-           Decrement the loop counter in Cell #1
+    ]                   Loop till Cell #1 is zero; number of iterations is 4
+    >+                  Add 1 to Cell #2
+    >+                  Add 1 to Cell #3
+    >-                  Subtract 1 from Cell #4
+    >>+                 Add 1 to Cell #6
+    [<]                 Move back to the first zero cell you find; this will
+                        be Cell #1 which was cleared by the previous loop
+    <-                  Decrement the loop Counter in Cell #0
+]                       Loop till Cell #0 is zero; number of iterations is 8
+The result of this is:
+Cell No :   0   1   2   3   4   5   6
+Contents:   0   0  72 104  88  32   8
+Pointer :   ^
+>>.                     Cell #2 has value 72 which is 'H'
+>---.                   Subtract 3 from Cell #3 to get 101 which is 'e'
++++++++..+++.          Likewise for 'llo' from Cell #3
+>>.                     Cell #5 is 32 for the space
+<-.                     Subtract 1 from Cell #4 for 87 to give a 'W'
+<.                      Cell #3 was set to 'o' from the end of 'Hello'
++++.------.--------.    Cell #3 for 'rl' and 'd'
+>>+.                    Add 1 to Cell #5 gives us an exclamation point
+>++.                    And finally a newline from Cell #6
diff --git a/contrib/brainfuck/examples/rot13.bf b/contrib/brainfuck/examples/rot13.bf
new file mode 100644 (file)
index 0000000..85a97fa
--- /dev/null
@@ -0,0 +1,28 @@
+-,+[                         Read first character and start outer character reading loop
+    -[                       Skip forward if character is 0
+        >>++++[>++++++++<-]  Set up divisor (32) for division loop
+                               (MEMORY LAYOUT: dividend copy remainder divisor quotient zero zero)
+        <+<-[                Set up dividend (x minus 1) and enter division loop
+            >+>+>-[>>>]      Increase copy and remainder / reduce divisor / Normal case: skip forward
+            <[[>+<-]>>+>]    Special case: move remainder back to divisor and increase quotient
+            <<<<<-           Decrement dividend
+        ]                    End division loop
+    ]>>>[-]+                 End skip loop; zero former divisor and reuse space for a flag
+    >--[-[<->+++[-]]]<[         Zero that flag unless quotient was 2 or 3; zero quotient; check flag
+        ++++++++++++<[       If flag then set up divisor (13) for second division loop
+                               (MEMORY LAYOUT: zero copy dividend divisor remainder quotient zero zero)
+            >-[>+>>]         Reduce divisor; Normal case: increase remainder
+            >[+[<+>-]>+>>]   Special case: increase remainder / move it back to divisor / increase quotient
+            <<<<<-           Decrease dividend
+        ]                    End division loop
+        >>[<+>-]             Add remainder back to divisor to get a useful 13
+        >[                   Skip forward if quotient was 0
+            -[               Decrement quotient and skip forward if quotient was 1
+                -<<[-]>>     Zero quotient and divisor if quotient was 2
+            ]<<[<<->>-]>>    Zero divisor and subtract 13 from copy if quotient was 1
+        ]<<[<<+>>-]          Zero divisor and add 13 to copy if quotient was 0
+    ]                        End outer skip loop (jump to here if ((character minus 1)/32) was not 2 or 3)
+    <[-]                     Clear remainder from first division if second division was skipped
+    <.[-]                    Output ROT13ed character from copy and clear it
+    <-,+                     Read next character
+]                            End character reading loop
index 86f25b3..8825b01 100644 (file)
@@ -536,7 +536,7 @@ class Automaton
 
                        # From the important values, build a sequence of TSymbols
                        var a = alphabet.to_a
-                       (new ComparableSorter[Int]).sort(a)
+                       default_comparator.sort(a)
                        var tsyms = new Array[TSymbol]
                        var last = 0
                        for i in a do
diff --git a/contrib/nitester/Makefile b/contrib/nitester/Makefile
new file mode 100644 (file)
index 0000000..ffe3530
--- /dev/null
@@ -0,0 +1,3 @@
+all:
+       mkdir -p bin/
+       ../../bin/nitg src/nitester.nit -o bin/nitester
diff --git a/contrib/nitester/src/nitester.nit b/contrib/nitester/src/nitester.nit
new file mode 100644 (file)
index 0000000..5edfe22
--- /dev/null
@@ -0,0 +1,657 @@
+# 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.
+
+# Tester of Nit engines on an MPI cluster
+module nitester
+
+import mpi
+import signals
+import opts
+
+# Any processor, worker or controller
+#
+# All data and methods within this class are shared by the controller and the
+# workers.
+abstract class Processor
+       super SignalHandler
+
+       # Controller rank is always 0
+       var controller_rank: Rank = 0.rank
+
+       # Where to store data for transfer between nodes
+       #
+       # Require: `buffer.length % 4 == 0`
+       var buffer = new CIntArray(1024)
+
+       # Run in verbose mode, display more text
+       var verbose = 0
+
+       init
+       do
+               # OpenMPI sends a SIGTERM to all nodes upon receiving a SIGTERM or SIGINT
+               # on the first process.
+               handle_signal(sigterm, true)
+       end
+
+       # 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`
+       var result_tag: Tag = 1.tag
+
+       # Tag to notify `Worker` when to quit
+       var quit_tag: Tag = 2.tag
+
+       # Tag to request more work from the `Controller` by a `Worker`
+       var need_work_tag: Tag = 4.tag
+
+       # Tag to notify `Controller` that the sender `Worker` is done
+       var done_tag: Tag = 5.tag
+
+       # Number of tasks within each task assignation with `task_tag`
+       var tasks_per_packet = 4
+
+       # Run the main logic of this node
+       fun run is abstract
+
+       # Engines targetted by this execution
+       var engines: Array[String] is noinit
+
+       # All known engines, used to detect errors in `engines`
+       var all_engines: Array[String] = ["nitg-s", "nitg-sg", "nitg-g", "nitg-e", "niti", "emscripten"]
+
+       # Programs to test in this execution
+       var test_programs: Array[String] is noinit
+
+       # Root of the temporary directory
+       var tmp_dir = "/dev/shm/"
+
+       # `ccache` directory
+       var ccache_dir = "/dev/shm/nit_ccache"
+
+       # Read command line options
+       fun read_cli_options
+       do
+               var opt_ctx = new OptionContext
+               var opt_engines = new OptionString(
+                       "Engines to test, separated with commas ({all_engines.join(", ")} or all)",
+                       "--engine", "-e")
+               var opt_help = new OptionBool("Print this help message", "--help", "-h")
+               var opt_verbose = new OptionCount(
+                       "Be verbose, repeat to increase verbose level (max with -vvv)",
+                       "--verbose", "-v")
+               var opt_cleanup = new OptionBool(
+                       "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.parse args
+
+               # --help?
+               if opt_help.value then opt_ctx.usage_error null
+
+               # --verbose?
+               verbose = opt_verbose.value
+
+               # --cleanup?
+               if opt_cleanup.value then
+                       assert tmp_dir.file_exists
+                       for file in tmp_dir.files do if file.has_prefix("nit") then
+                               var full_path = tmp_dir / file
+                               if full_path == ccache_dir then continue
+
+                               assert full_path.file_exists
+
+                               var stat = full_path.file_lstat
+                               if stat.is_dir then
+                                       full_path.rmdir
+                               else
+                                       full_path.file_delete
+                               end
+                               stat.free
+                       end
+                       mpi.finalize
+                       exit 0
+               end
+
+               # any files?
+               var rest = opt_ctx.rest
+               if rest.is_empty then opt_ctx.usage_error "This tool needs at least one test_program.nit"
+               test_programs = rest
+
+               # gather and check engines
+               var engines_str = opt_engines.value
+               var engines
+               if engines_str == null then
+                       # default
+                       engines = ["nitg-s"]
+               else
+                       engines = engines_str.split(',')
+
+                       if engines.has("all") then
+                               # all engines
+                               engines = all_engines
+                       end
+               end
+
+               # check validity of targetted engines
+               var unknown_engines = new Array[String]
+               for engine in engines do if not all_engines.has(engine) then unknown_engines.add engine
+
+               if not unknown_engines.is_empty then
+                       opt_ctx.usage_error "Unknown engines: {unknown_engines.join(", ")} (expected one or most of {all_engines.join(", ")})"
+               end
+               self.engines = engines
+       end
+
+       # All tasks to be performed
+       var tasks = new Array[Task]
+
+       # Gather and registar all tasks
+       fun create_tasks
+       do
+               var c = 0
+               for engine in engines do for prog in test_programs do
+                       tasks.add new Task(engine, prog)
+                       c += 1
+               end
+       end
+end
+
+# Single controller to dispatch tasks, gather results and produce stats
+class Controller
+       super Processor
+
+       # Id as `Int` of the next task to distribute
+       var next_task_id = 0
+
+       redef fun receive_signal(signal)
+       do
+               shutdown
+               print_results
+
+               mpi.finalize
+               exit 0
+       end
+
+       redef fun run
+       do
+               read_cli_options
+               create_tasks
+               distribute_tasks
+               print_results
+       end
+
+       # Cumulated results from workers
+       var results = new ResultSet
+
+       # Maintain communication with workers to distribute tasks and receiver results
+       fun distribute_tasks
+       do
+               var at_work = new Array[Rank]
+
+               # send initial tasks
+               for r in [1..comm_world.size[ do
+                       var sent = send_task_to(r.rank)
+                       if sent then
+                               at_work.add r.rank
+                       else
+                               mpi.send_empty(r.rank, quit_tag, comm_world)
+                       end
+               end
+
+               var status = new Status
+               # await results and send new tasks
+               while not at_work.is_empty do
+                       check_signals
+
+                       # Double probe to avoid bug with some implementation of MPI
+                       mpi.probe(new Rank.any, new Tag.any, comm_world, status)
+                       mpi.probe(new Rank.any, new Tag.any, comm_world, status)
+
+                       if status.tag == result_tag then
+                               # Receive results fron a worker
+                               var count = status.count(new DataType.int)
+                               mpi.recv_into(buffer, 0, count, status.source, status.tag, comm_world)
+
+                               # Parse results from C array to `Result` instances
+                               #
+                               # Each result is on 4 ints: task id, arg, alt and result.
+                               #
+                               # See the comments where the data is produced in `Worker::work_on_tasks` for more informationé
+                               assert count % 4 == 0
+                               for t in (count/4).times do
+                                       var tt = t*4
+
+                                       var task_id = buffer[tt]
+                                       var arg = buffer[tt+1]
+                                       var alt = buffer[tt+2]
+                                       var res = buffer[tt+3]
+
+                                       var result = new Result(tasks[task_id], arg, alt)
+
+                                       if res == 1 then result.ok = true
+                                       if res == 2 then result.ok_empty = true
+                                       if res == 3 then result.no_sav = true
+                                       if res == 4 then result.fixme = true
+                                       if res == 5 then result.fail = true
+                                       if res == 6 then result.soso = true
+                                       if res == 7 then result.skip = true
+                                       if res == 0 then result.unknown = true
+
+                                       results.add result
+
+                                       if verbose > 0 and results.length % 25 == 0 then print_short_results
+                               end
+
+                       else if status.tag == need_work_tag then
+                               # A worker needs more work
+                               mpi.recv_empty(status.source, status.tag, comm_world)
+                               var sent = send_task_to(status.source)
+                               if not sent then
+                                       # no more work, quit
+                                       mpi.send_empty(status.source, quit_tag, comm_world)
+                               end
+                       else if status.tag == done_tag then
+                               # A worker is done and will quit
+                               mpi.recv_empty(status.source, status.tag, comm_world)
+                               at_work.remove(status.source)
+
+                               if verbose > 1 then print "worker {status.source} is done ({at_work.length} still at work)"
+                       else
+                               print "Unexpected tag {status.tag}"
+                               shutdown
+                               break
+                       end
+               end
+               status.free
+       end
+
+       # Send a packet of tasks to worker at `rank`
+       fun send_task_to(rank: Rank): Bool
+       do
+               if next_task_id >= tasks.length then return false
+
+               buffer[0] = next_task_id
+               next_task_id += tasks_per_packet
+
+               mpi.send_from(buffer, 0, 1, rank, task_tag, comm_world)
+
+               if verbose > 1 then print "sent tasks [{buffer[0]}..{next_task_id}[ to worker {rank}"
+               return true
+       end
+
+       # Display the accumulated results received from workers
+       fun print_results
+       do
+               print "# results #"
+               print "* {results.length} total"
+               print "* {results.oks.length + results.ok_empties.length} oks & 0ks"
+               print "* {results.fails.length} fails"
+               print "* {results.no_savs.length} no savs"
+               print "* {results.fixmes.length} fixmes"
+               print "* {results.sosos.length} sosos"
+               print "* {results.skips.length} skips"
+               print "* {results.unknowns.length} unknowns (bug in tests.sh or nitester)"
+       end
+
+       fun print_short_results do print "oks & fails / total: {results.oks.length + results.ok_empties.length} " +
+               "& {results.fails.length} / {results.length}"
+
+       # Shutdown anormaly the running tests
+       fun shutdown
+       do
+               print "Shutting down"
+               mpi.send_empty(new Rank.any, quit_tag, comm_world)
+       end
+end
+
+# A worker node which actually execute the tests
+class Worker
+       super Processor
+
+       # The `Rank` of `self`
+       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
+
+       # Output file of the `tests.sh` script
+       var tests_sh_out = "/dev/shm/nit_local_out{rank}" is lazy
+
+       # Path to the local copy of the Nit repository
+       var nit_copy_dir = "/dev/shm/nit{rank}/" is lazy
+
+       # Source Nit repository, must be already updated and `make` before execution
+       var nit_source_dir = "~/nit"
+
+       # Compiled `Regex` to detect the argument of an execution
+       var re_arg: Regex = "arg [0-9]+".to_re
+
+       # Compiled `Regex` to detect the alternative of an execution
+       var re_alt: Regex = "_alt[0-9]+".to_re
+
+       redef fun run
+       do
+               read_cli_options
+               setup
+               create_tasks
+               work_on_tasks
+               cleanup
+       end
+
+       # Setup the testing environment
+       #
+       # Clone the nit repository.
+       fun setup
+       do
+               if verbose > 0 then sys.system "hostname"
+               sys.system "git clone {nit_source_dir} {nit_copy_dir}"
+       end
+
+       # Clean up the testing environment
+       #
+       # Delete all temporary files, except `ccache_dir`.
+       fun cleanup
+       do
+               if comp_dir.file_exists then comp_dir.rmdir
+               if out_dir.file_exists then out_dir.rmdir
+               if nit_copy_dir.file_exists then nit_copy_dir.rmdir
+               if tests_sh_out.file_exists then tests_sh_out.file_delete
+       end
+
+       # Single C `int` to hold the next task id received from the `Controller`
+       var task_buffer = new CIntArray(1)
+
+       # Manage communication with the `Controller` and execute dispatched `Task`s
+       fun work_on_tasks
+       do
+               var status = new Status
+               loop
+                       check_signals
+
+                       # We double probe to prevent bug where a single probes does not receive the
+                       # real next read.
+                       mpi.probe(controller_rank, new Tag.any, comm_world, status)
+                       mpi.probe(controller_rank, new Tag.any, comm_world, status)
+
+                       if status.tag == task_tag then
+                               # Receive tasks to execute
+                               mpi.recv_into(task_buffer, 0, 1, status.source, status.tag, comm_world)
+                               var first_id = task_buffer[0]
+                               for task_id in [first_id .. first_id + tasks_per_packet] do
+
+                                       # If id is over all known tasks, stop right here
+                                       if task_id >= tasks.length then break
+                                       var task = tasks[task_id]
+
+                                       # Command line to execute test
+                                       var cmd = "XMLDIR={out_dir} ERRLIST={out_dir}/errlist TMPDIR={out_dir} " +
+                                               "CCACHE_DIR={ccache_dir} CCACHE_TEMPDIR={ccache_dir} CCACHE_BASEDIR={comp_dir} " +
+                                               "./tests.sh --compdir {comp_dir} --outdir {out_dir} -o \"--make-flags '-j1'\"" +
+                                               " --node --engine {task.engine} {nit_copy_dir / "tests" / task.test_program} > {tests_sh_out}"
+
+                                       # Execute test
+                                       sys.system cmd
+
+                                       # Test results were written to file, read them
+                                       var fstream = new IFStream.open(tests_sh_out)
+                                       var content = fstream.read_all
+                                       fstream.close
+
+                                       # Parse result and prepare them for sending
+                                       #
+                                       # The structure is composed of 4 ints for each result.
+                                       # 1. task id
+                                       # 2. arg number
+                                       # 3. alt number
+                                       # 4. test result as int
+                                       var c = results_count
+                                       for line in content.split('\n') do if not line.is_empty then
+                                               var cc = c*4
+
+                                               buffer[cc] = task_id
+
+                                               var arg_match = line.search(re_arg)
+                                               var arg = 0
+                                               if arg_match != null then arg = arg_match.to_s.substring_from(4).to_i
+                                               buffer[cc+1] = arg
+
+                                               var alt_match = line.search(re_alt)
+                                               var alt = 0
+                                               if alt_match != null then alt = alt_match.to_s.substring_from(4).to_i
+                                               buffer[cc+2] = alt
+
+                                               var res = null
+                                               if line.has("[ok]") then res = 1
+                                               if line.has("[0k]") then res = 2
+                                               if line.has("[=== no sav ===]") then res = 3
+                                               if line.has("[fixme]") then res = 4
+                                               if line.has("[======= fail") then res = 5
+                                               if line.has("[======= soso") then res = 6
+                                               if line.has("[skip]") then res = 7
+
+                                               if res == null then
+                                                       res = 0
+                                                       if verbose > 1 then print "Unknown result: '{line}'"
+                                               end
+                                               buffer[cc+3] = res
+
+                                               c += 1
+
+                                               if verbose > 2 then print "tests.sh output line: {line}"
+
+                                               # If result buffer is full, send to `Controller`
+                                               if c*4 == buffer.length then
+                                                       send_results
+                                                       c = 0
+                                               end
+                                       end
+
+                                       self.results_count = c
+                               end
+
+                               mpi.send_empty(controller_rank, need_work_tag, comm_world)
+                       else if status.tag == quit_tag then
+                               # Notification from the `Controller` to quit
+                               mpi.recv_empty(status.source, status.tag, comm_world)
+
+                               # Send remaining results
+                               send_results
+
+                               # Notify `Controller` that `self` is done and will quit
+                               mpi.send_empty(controller_rank, done_tag, comm_world)
+                               break
+                       else
+                               print "Unexpected tag {status.tag}"
+                               break
+                       end
+               end
+               status.free
+       end
+
+       # Total results listed in `buffer` and ready to send
+       var results_count = 0
+
+       # Send all results in `buffer` to the `Controller`
+       fun send_results
+       do
+               if results_count > 0 then
+                       if verbose > 1 then print "sending {results_count} results"
+                       mpi.send_from(buffer, 0, results_count*4, controller_rank, result_tag, comm_world)
+                       results_count = 0
+               end
+       end
+
+       redef fun receive_signal(signal)
+       do
+               cleanup
+               mpi.finalize
+               exit 0
+       end
+end
+
+# A single test task, on a `test_program` with an `engine`
+#
+# Note that a task may involve more than one program to test considering the
+# alts and args for the `test_program`.
+class Task
+       # Engine to test executing `test_program`
+       var engine: String
+
+       # Program to execute with `engine`
+       var test_program: String
+
+       redef fun to_s do return "{engine} {test_program}"
+end
+
+# Result of a `Task`
+#
+# There may be more than one result per `Task`.
+class Result
+       # `Task` associated to `self`
+       var task: Task
+
+       # Argument index of the execution resulting in `self`
+       var arg: Int
+
+       # Alternative index of the execution resulting in `self`
+       var alt: Int
+
+       # Is `self` result an _ok_?
+       var ok = false
+
+       # Is `self` result an _0k_?
+       var ok_empty = false
+
+       # Is `self` result a _no sav_?
+       var no_sav = false
+
+       # Is `self` result a _fixme_?
+       var fixme = false
+
+       # Is `self` result a _fail_?
+       var fail = false
+
+       # Is `self` result a _soso_?
+       var soso = false
+
+       # Is `self` skipped test?
+       var skip = false
+
+       # Is `self` an unknown result, probably an error
+       var unknown = false
+
+       redef fun to_s
+       do
+               var err = "Unknown"
+               if no_sav then err = "no sav"
+               if ok then err = "ok"
+               if ok_empty then err = "0k"
+               if fixme then err = "fixme"
+               if fail then err = "fail"
+
+               return "{task} arg{arg} alt{alt} => {err}"
+       end
+end
+
+# A global and sorted collection of `Result`
+class ResultSet
+       super HashSet[Result]
+
+       var no_savs = new HashSet[Result]
+       var oks = new HashSet[Result]
+       var ok_empties = new HashSet[Result]
+       var fixmes = new HashSet[Result]
+       var fails = new HashSet[Result]
+       var sosos = new HashSet[Result]
+       var skips = new HashSet[Result]
+       var unknowns = new HashSet[Result]
+
+       # TODO remove
+       var per_engines = new HashMap[String, Result]
+
+       redef fun add(result)
+       do
+               if result.no_sav then no_savs.add result
+               if result.ok then oks.add result
+               if result.ok_empty then ok_empties.add result
+               if result.fixme then fixmes.add result
+               if result.fail then fails.add result
+               if result.soso then sosos.add result
+               if result.skip then skips.add result
+               if result.unknown then unknowns.add result
+
+               super
+       end
+
+       redef fun remove(r) do abort
+
+       redef fun clear do abort
+end
+
+redef class OptionContext
+
+       # Print usage with a possible error `message`
+       private fun usage_error(message: nullable String)
+       do
+               var ret = 0
+               if message != null then
+                       print "Error: {message}"
+                       ret = 1
+               end
+
+               if comm_world.rank == 0 then
+                       print "Usage: mpirun nitester [Options] test_program.nit [other_test.nit [...]]"
+                       usage
+               end
+
+               mpi.finalize
+               exit ret
+       end
+end
+
+# On `Worker` nodes, prefix all prints with `rank/comm_world.size`
+redef fun print(msg: Object)
+do
+       if comm_world.rank != 0.rank then
+               super "{comm_world.rank}/{comm_world.size}: {msg}"
+       else super msg
+end
+
+# Running MPI instance
+fun mpi: MPI do return once new MPI
+
+# Launch mpi
+mpi
+
+# Local rank
+var rank = comm_world.rank
+
+var processor: Processor
+if rank == 0.rank then
+       # If rank == 0, this is the `Controller`
+       processor = new Controller
+else
+       # This is a worker
+       processor = new Worker(rank)
+end
+processor.run
+
+mpi.finalize
diff --git a/contrib/opportunity/Makefile b/contrib/opportunity/Makefile
new file mode 100644 (file)
index 0000000..bbcf10b
--- /dev/null
@@ -0,0 +1,3 @@
+all:
+       mkdir -p bin/
+       ../../bin/nitg --dir bin/ src/opportunity_web.nit
diff --git a/contrib/opportunity/README.md b/contrib/opportunity/README.md
new file mode 100644 (file)
index 0000000..32eb786
--- /dev/null
@@ -0,0 +1,30 @@
+Opportunity is a web-application written in Nit to plan meetups with people in real-life (or on the internet, why not !).
+
+It runs on Nit's official web server: Nitcorn, available along with the compiler for the language on Github <https://github.com/privat/nit>.
+
+# Compile and execute
+
+To compile the software, make sure you have all the dependencies for the Nit compiler at hand, make the compiler.
+
+In addition to those, you will also need libevent-dev and libsqlite3-dev.
+
+Under Debian or any Debian-based distribution of Linux, you can use apt-get install for those.
+If you run an RPM based distribution, yum install should work as well.
+
+For those who run an OSX-based system, macports or brew might have the packages you need to run Opportunity.
+
+There is no support for Windows yet.
+
+Then change directory to Opportunity, and use the command make here as well.
+
+This will make an executable for opportunity in the bin folder that you can run from a terminal as usual.
+
+# Features/TODO
+
+- [x] Creation and visualization of Meetups
+- [x] Adding people to meetups
+- [x] Change answers
+- [ ] Edit an Existing Meetup
+- [ ] More security and stability for the software
+- [ ] Full support of a REST API for eventual extern clients
+- [ ] Maybe support another DB
diff --git a/contrib/opportunity/src/opportunity_controller.nit b/contrib/opportunity/src/opportunity_controller.nit
new file mode 100644 (file)
index 0000000..032b7d3
--- /dev/null
@@ -0,0 +1,205 @@
+# 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
+
+# Actions for Opportunity web application
+module opportunity_controller
+
+import nitcorn
+import sha1
+import templates
+import opportunity_model
+
+# Any kind of opportunity `Action` (serves a request)
+abstract class OpportunityAction
+       super Action
+
+       # Path to db
+       var db_path = "opportunity"
+
+       # Returns a bad request with an error code 400
+       #
+       # TODO: Add a specific body to the bad request page.
+       fun bad_req: HttpResponse do
+               var rsp = new HttpResponse(400)
+               rsp.body = (new OpportunityHomePage).write_to_string
+               return rsp
+       end
+end
+
+# Welcome page for Opportunity
+class OpportunityWelcome
+       super OpportunityAction
+
+       redef fun answer(request, url) do
+               print "Received request for {url}"
+               var get = request.get_args
+               var rq = url.split("/")
+               if rq.has("meetup_create") then
+                       var mname = request.string_arg("meetup_name")
+                       var mdate = request.string_arg("meetup_date")
+                       var mplace = request.string_arg("meetup_place")
+                       if mname == null or mdate == null or mplace == null then return bad_req
+                       var db = new OpportunityDB.open(db_path)
+                       var meet = new Meetup(mname, mdate, mplace)
+                       if meet == null then
+                               db.close
+                               return bad_req
+                       end
+                       meet.commit(db)
+                       var ans_tmp = "answer_"
+                       var cnt = 1
+                       loop
+                               var anss = request.string_arg(ans_tmp + cnt.to_s)
+                               if anss == null then break
+                               var ans = new Answer(anss)
+                               ans.meetup = meet
+                               ans.commit(db)
+                               cnt += 1
+                       end
+                       db.close
+                       var rsp = new HttpResponse(200)
+                       if meet.id == "" then
+                               rsp.body = (new MeetupCreationPage).write_to_string
+                       else
+                               rsp.body = (new MeetupConfirmation(meet)).write_to_string
+                       end
+                       return rsp
+               end
+               if rq.has("new_meetup") then
+                       var rsp = new HttpResponse(200)
+                       var page = new MeetupCreationPage
+                       rsp.body = page.write_to_string
+                       return rsp
+               end
+               if get.has_key("meetup_id") then
+                       var rsp = new HttpResponse(200)
+                       rsp.body = (new OpportunityMeetupPage.from_id(get["meetup_id"])).write_to_string
+                       return rsp
+               end
+               var rsp = new HttpResponse(200)
+               rsp.body = (new OpportunityHomePage).write_to_string
+               return rsp
+       end
+
+end
+
+# Any kind of REST request to Opportunity
+class OpportunityRESTAction
+       super OpportunityAction
+
+       redef fun answer(request, uri) do
+               print "Received REST request from {uri}"
+               var get = request.get_args
+               var req = uri.split("/")
+               if req.has("people") then
+                       return (new OpportunityPeopleREST).answer(request, uri)
+               else if req.has("answer") then
+                       return (new OpportunityAnswerREST).answer(request, uri)
+               else if req.has("meetup") then
+                       return (new OpportunityMeetupREST).answer(request, uri)
+               else
+                       return new HttpResponse(400)
+               end
+       end
+
+end
+
+# REST Actions working on People
+class OpportunityPeopleREST
+       super OpportunityAction
+
+       redef fun answer(request, uri) do
+               # Should be DELETE for true REST API
+               # TODO : change method to DELETE once supported by Nitcorn
+               if request.method == "POST" then
+                       var meth = request.string_arg("method")
+                       if meth == null then return bad_req
+                       if meth != "DELETE" then return bad_req
+                       var pid = request.int_arg("p_id")
+                       if pid == null then return bad_req
+                       var db = new OpportunityDB.open(db_path)
+                       db.remove_people_by_id(pid)
+                       db.close
+                       return new HttpResponse(200)
+               end
+               return new HttpResponse(400)
+       end
+
+end
+
+# REST Actions working on Answers
+class OpportunityAnswerREST
+       super OpportunityAction
+
+       redef fun answer(request, uri) do
+               var persid = request.int_arg("pers_id")
+               var ansid = request.int_arg("answer_id")
+               var ans = request.bool_arg("answer")
+               if persid == null or ansid == null or ans == null then return bad_req
+               var db = new OpportunityDB.open(db_path)
+               db.change_answer(ansid, persid, ans)
+               db.close
+               return new HttpResponse(200)
+       end
+end
+
+# REST Actions working on Meetups
+class OpportunityMeetupREST
+       super OpportunityAction
+
+       redef fun answer(request, uri) do
+               var args = uri.split("/")
+               if args.has("new_pers") then
+                       var name = request.string_arg("persname")
+                       var m_id = request.string_arg("meetup_id")
+                       var ans = request.string_arg("answers").split("&")
+                       if name == null or m_id == null then return bad_req
+                       print ans
+                       var ansmap = new HashMap[Int, Bool]
+                       for i in ans do
+                               var mp = i.split("=")
+                               var b = false
+                               if mp.last == "true" then b = true
+                               var id = mp.first.split("_").last
+                               if not id.is_numeric then continue
+                               ansmap[id.to_i] = b
+                       end
+                       var db = new OpportunityDB.open(db_path)
+                       var m = db.find_meetup_by_id(m_id)
+                       var sublen = name.index_of(' ')
+                       var rname = ""
+                       var rsurname = ""
+                       if sublen == -1 then
+                               rsurname = name
+                       else
+                               rsurname = name.substring(0, sublen)
+                               rname = name.substring_from(sublen + 1)
+                       end
+                       var p = new People(rname, rsurname)
+                       for i in m.answers(db) do
+                               if not ansmap.has_key(i.id) then
+                                       p.answers[i] = false
+                               else
+                                       p.answers[i] = ansmap[i.id]
+                               end
+                       end
+                       p.commit(db)
+                       db.close
+                       var rsp = new HttpResponse(200)
+                       rsp.body = p.id.to_s
+                       return rsp
+               end
+               return new HttpResponse(400)
+       end
+end
diff --git a/contrib/opportunity/src/opportunity_model.nit b/contrib/opportunity/src/opportunity_model.nit
new file mode 100644 (file)
index 0000000..fa9e674
--- /dev/null
@@ -0,0 +1,301 @@
+# 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
+
+# Model for the data of Opportunity
+module opportunity_model
+
+import sqlite3
+import sha1
+import serialization
+
+# A SQLiteDB object for `Opportunity`
+class OpportunityDB
+       super Sqlite3DB
+
+       init open(x) do
+               super
+
+               create_db
+       end
+
+       # Creates the tables and triggers for Opportunity (SQLite3 DB)
+       fun create_db do
+               assert create_table("IF NOT EXISTS meetups (id CHAR(40) PRIMARY KEY, name TEXT, date TEXT, place TEXT);") else
+                       print error or else "?"
+               end
+               assert create_table("IF NOT EXISTS people(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT);") else
+                       print error or else "?"
+               end
+               assert create_table("IF NOT EXISTS answers(id INTEGER PRIMARY KEY AUTOINCREMENT, meetup_id CHAR(40), name TEXT, FOREIGN KEY(meetup_id) REFERENCES meetups(id));") else
+                       print error or else "?"
+               end
+               assert create_table("IF NOT EXISTS part_answers(id_part INTEGER, id_ans INTEGER, value INTEGER, FOREIGN KEY(id_part) REFERENCES people(id), FOREIGN KEY(id_ans) REFERENCES answers(id));") else
+                       print error or else "?"
+               end
+               #NOTE: The following triggers could be replaced by ON DELETE CASCADE clauses
+               # Thing is, SQLite does not seem to support those operations (well, not by default, it seems
+               # we must re-compile the lib to support it. So, well, let's just create triggers heh.
+               assert execute("CREATE TRIGGER IF NOT EXISTS answers_clean AFTER DELETE ON meetups BEGIN DELETE FROM answers WHERE answers.meetup_id=OLD.id;END;") else
+                       print error or else "?"
+               end
+               assert execute("CREATE TRIGGER IF NOT EXISTS ans_clean AFTER DELETE ON answers BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_ans;END;") else
+                       print error or else "?"
+               end
+               assert execute("CREATE TRIGGER IF NOT EXISTS ppl_clean AFTER DELETE ON people BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_part;END;")
+       end
+
+       # Find a `People` by its id, or `null` if it could not be found
+       fun find_people_by_id(id: Int): nullable People do
+               var req = select("* from people where id={id};")
+               for i in req do
+                       return new People.from_db(i[0].to_i, i[1].to_s, i[2].to_s)
+               end
+               return null
+       end
+
+       # Find a `Meetup` by its id or `null` if it could not be found
+       fun find_meetup_by_id(id: String): nullable Meetup do
+               var req = select("* FROM meetups where id={id.to_sql_string};")
+               for i in req do
+                       return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s)
+               end
+               return null
+       end
+
+       # Change an Answer `ansid` for someone with an id `pid` to `resp`
+       #
+       # Returns `true` if the request was sucessful, false otherwise
+       fun change_answer(pid: Int, ansid: Int, resp: Bool): Bool do
+               var rsp = 0
+               if resp then rsp = 1
+               var rq = execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES({pid},{ansid},{rsp});")
+               if not rq then
+                       print "Error while updating answer {ansid}:{pid}"
+                       print error or else "Unknown error"
+                       return false
+               end
+               return true
+       end
+
+       # Removes a person in the Database by its `id`
+       #
+       # Returns true if sucessful, false otherwise
+       fun remove_people_by_id(id: Int): Bool do
+               var rq = execute("DELETE FROM people WHERE id = {id};")
+               if not rq then
+                       print "Cannot delete people {id}"
+                       print error or else "Unknown error"
+                       return false
+               end
+               return true
+       end
+end
+
+# Any kind of Database Object that can be persisted to the database
+abstract class DBObject
+
+       # Commits the modifications done to the Object in the database
+       fun commit(db: OpportunityDB): Bool is abstract
+end
+
+# A Meetup participant, linked to the DB
+class People
+       super DBObject
+
+       # ID in the Database, -1 if not set
+       var id: Int = -1
+       # Name of the participant
+       var name: String
+       # Surname of the participant
+       var surname: String
+       # Map of the answers of a Meetup and the answers of the participant
+       var answers: Map[Answer, Bool] = new HashMap[Answer, Bool]
+
+       # To be used internally when fetching the `People` in Database
+       private init from_db(id: Int, name, surname: String) do
+               init(name, surname)
+               self.id = id
+       end
+
+       # Changes an answer `ans` (or adds it)
+       fun answer=(ans: Answer, resp: Bool) do
+               answers[ans] = resp
+       end
+
+       # Loads the answers for a Meetup
+       #
+       # NOTE: If `self` does not exist in the Database, no answers will be fetched
+       fun load_answers(db: OpportunityDB, meetup: Meetup) do
+               self.answers = new HashMap[Answer, Bool]
+               var req = db.select("answers.id, answers.name, part_answers.value FROM part_answers, answers WHERE part_answers.id_part={id} AND answers.id=part_answers.id_ans AND answers.meetup_id={meetup.id.to_sql_string} GROUP BY answers.id;")
+               for i in req do
+                       var ans = new Answer.from_db(i[0].to_i, i[1].to_s)
+                       answers[ans] = false
+                       if i[2].to_i == 1 then answers[ans] = true
+               end
+       end
+
+       redef fun to_s do return "{surname.capitalized} {name.capitalized}"
+
+       redef fun commit(db) do
+               if id == -1 then
+                       if not db.execute("INSERT INTO people (name,surname) VALUES ({name.to_sql_string}, {surname.to_sql_string});") then
+                               print "Error while adding people {self}"
+                               print db.error or else "Unknown error"
+                               return false
+                       end
+                       id = db.last_insert_rowid
+               else
+                       if not db.execute("UPDATE people SET name={name.to_sql_string}, surname={surname.to_sql_string} WHERE ID={id};") then
+                               print "Error while updating people {self}"
+                               print db.error or else "Unknown error"
+                               return false
+                       end
+               end
+               for i,j in answers do
+                       if i.id == -1 then i.commit(db)
+                       var val = 0
+                       if j then val = 1
+                       if not db.execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES ({id},{i.id},{val});") then
+                               print("Error while adding/replacing part_answers {id}|{i.id}|{j}")
+                               print db.error or else "Unknown error"
+                               return false
+                       end
+               end
+               return true
+       end
+end
+
+# A `Meetup` is an opportunity of meeting, linked to the database
+class Meetup
+       super DBObject
+
+       # ID of the meetup, SHA-1 of the informations that are contained
+       var id: String = ""
+       # Name for the meetup
+       var name: String
+       # SQLite-formatted date : YYYY:DD:MM HH:MM:SS
+       var date: String
+       # Place of the meetup
+       var place: String
+
+       # Builds the object with all the informations found in the database
+       private init from_db(id, name, date, place: String) do
+               self.id = id
+               self.name = name
+               self.date = date
+               self.place = place
+       end
+
+       # Gets the answers bound to the current `Meetup`
+       fun answers(db: OpportunityDB): Array[Answer] do
+               if id == "" then
+                       return new Array[Answer]
+               end
+               var res = db.select("id, name FROM answers WHERE meetup_id={id.to_sql_string}")
+               var ans = new Array[Answer]
+               for i in res do
+                       ans.add new Answer.from_db(i[0].to_i, i[1].to_s)
+               end
+               return ans
+       end
+
+       # Gets the list of the participants of a `Meetup`
+       fun participants(db: OpportunityDB): Array[People] do
+               var resp = db.select("people.* FROM people, meetups, answers, part_answers WHERE meetups.id={id.to_sql_string} AND answers.meetup_id={id.to_sql_string} AND part_answers.id_ans=answers.id AND people.id=part_answers.id_part GROUP BY people.id;")
+               var arr = new Array[People]
+               for i in resp do
+                       arr.add (new People.from_db(i[0].to_i, i[1].to_s, i[2].to_s))
+               end
+               return arr
+       end
+
+       redef fun commit(db) do
+               if id == "" then
+                       var tmpid = (name + date + place).sha1_to_s
+                       if not db.execute("INSERT INTO meetups (id, name, date, place) VALUES({tmpid.to_sql_string}, {name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string});") then
+                               print "Error recording entry Meetup {self}"
+                               print db.error or else "Null error"
+                               return false
+                       end
+                       id = tmpid
+                       return true
+               else
+                       return db.execute("UPDATE meetups (name, date, place) VALUES({name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string}) WHERE ID={id.to_sql_string};")
+               end
+       end
+
+       redef fun to_s do
+               return "Event : {name}\nWhen : {date}\nWhere : {place}"
+       end
+end
+
+# An answer linked to a Meetup in the database
+class Answer
+       super DBObject
+
+       # Name of the answer (title)
+       var name: String
+       # Id in the database, -1 if not set
+       var id: Int = -1
+       # Meetup the answer is linked to (null while it is not added in the database or set via API)
+       var meetup: nullable Meetup = null is writable
+
+       # To be used internally when fetching the object from Database
+       private init from_db(id: Int, name: String) do
+               init name
+               self.id = id
+       end
+
+       # Loads the Meetup associated to `self`
+       #
+       # REQUIRE: is loaded in database
+       fun load_meetup(db: OpportunityDB): Meetup do
+               assert id != null
+               var res = db.select("meetups.* FROM meetups, answers WHERE answers.id={id} AND answers.meetup_id=meetups.id;")
+               for i in res do
+                       return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s)
+               end
+               # If no Meetup could be loaded, the contract was not respected
+               abort
+       end
+
+       redef fun commit(db) do
+               var m = meetup
+               if m == null then return false
+               if m.id == "" then
+                       if not m.commit(db) then
+                               print "Error when creating meetup {m}"
+                               return false
+                       end
+               end
+               if id == -1 then
+                       if not db.execute("INSERT INTO answers (name, meetup_id) VALUES({name.to_sql_string}, {m.id.to_sql_string});") then
+                               print "Cannot create {self} in database"
+                               print db.error or else "Unknown error"
+                               return false
+                       end
+                       id = db.last_insert_rowid
+               else
+                       if not db.execute("UPDATE answers (name) VALUES ({name.to_sql_string}) WHERE meetup_id={m.id.to_sql_string};") then
+                               print "Error updating {self} in database"
+                               print db.error or else "Unknown error"
+                               return false
+                       end
+               end
+               return true
+       end
+
+       redef fun to_s do return name
+end
diff --git a/contrib/opportunity/src/opportunity_web.nit b/contrib/opportunity/src/opportunity_web.nit
new file mode 100644 (file)
index 0000000..73a3b39
--- /dev/null
@@ -0,0 +1,35 @@
+# 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
+
+# Web server for Opportunity : A meetup scheduler for real-world encounters !
+module opportunity_web
+
+import opportunity_controller
+import opts
+import privileges
+
+# Avoids a crash when running automatic tests
+if "NIT_TESTING".environ == "true" then exit 0
+
+var iface = "localhost:80"
+
+var vh = new VirtualHost(iface)
+vh.routes.add new Route("/rest/", new OpportunityRESTAction)
+vh.routes.add new Route(null, new OpportunityWelcome)
+
+var fac = new HttpFactory.and_libevent
+fac.config.virtual_hosts.add vh
+
+print "Launching server on http://{iface}/"
+fac.run
diff --git a/contrib/opportunity/src/templates/boilerplate.nit b/contrib/opportunity/src/templates/boilerplate.nit
new file mode 100644 (file)
index 0000000..cb2b11c
--- /dev/null
@@ -0,0 +1,124 @@
+# 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
+
+# Contains the main components of a webpage for Opportunity
+module boilerplate
+
+import template
+
+# Header for a Opportunity page
+class OpportunityHeader
+       super Template
+
+       # Javascript code that is included in the `OpportunityPage`
+       var page_js = "" is writable
+
+       redef fun rendering do
+               add """
+<!DOCTYPE html>
+<html>
+<head>
+       <title>Opportunity - The meetup planner</title>
+       <meta charset="utf-8">
+       <meta name="viewport" content="width=device-width, initial-scale=1">
+       <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
+       <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
+       <script>
+               {{{page_js}}}
+       </script>
+       <style>
+               .menu {
+                       background-color: #0d8921;
+                       background-repeat: repeat-x;
+                       color: white;
+               }
+               .navbar-brand{
+                       color: white;
+               }
+               .navbar-brand:hover{
+                       color: #EEEEEE;
+               }
+               .navbar-nav{
+                       color: white;
+               }
+               .navbar-nav: hover{
+                       background-color: #0d8921;
+                       color: white;
+               }
+               .answer:hover {
+                       cursor:pointer;
+                       background-color:#0d8921;
+                       color:white;
+               }
+               .opportunity-action:hover {
+                       cursor:pointer;
+               }
+       </style>
+</head>
+<body>
+<nav class="menu" role="navigation">
+       <div class="container">
+               <div class="navbar-header">
+                       <a class="navbar-brand" href="/" >Opportunity</a>
+               </div>
+       </div>
+</nav>
+<div class="container-fluid">
+"""
+       end
+
+end
+
+# Footer for a Opportunity page
+class OpportunityFooter
+       super Template
+
+       redef fun rendering do
+               add """
+</div>
+</body>
+<div class="footer">
+       <div class="well well-sm">
+               <p class="text-muted text-center">
+                       Opportunity, the meetup planner.
+               </p>
+               <p class="text-muted text-center">
+                       Proudly powered by <a href="http://nitlanguage.org/">Nit</a>!
+               </p>
+       </div>
+</div>
+</html>
+"""
+       end
+
+end
+
+# Any Opportunity page that contains the header, body and footer.
+class OpportunityPage
+       super Template
+
+       var header = new OpportunityHeader
+
+       var body: Streamable = "" is writable
+
+       var footer = new OpportunityFooter
+
+       redef fun rendering do
+               add header
+               add body
+               add footer
+       end
+
+end
diff --git a/contrib/opportunity/src/templates/meetup.nit b/contrib/opportunity/src/templates/meetup.nit
new file mode 100644 (file)
index 0000000..d9af51d
--- /dev/null
@@ -0,0 +1,193 @@
+# 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
+
+# Shows a meetup and allows to modify its participants
+module meetup
+
+import opportunity_model
+import boilerplate
+import welcome
+import template
+
+# Shows a meetup and allows to modify its participants
+class OpportunityMeetupPage
+       super OpportunityPage
+
+       # Meetup the page is supposed to show
+       var meetup: nullable Meetup = null
+
+       init from_id(id: String) do
+               var db = new OpportunityDB.open("opportunity")
+               meetup = db.find_meetup_by_id(id)
+               db.close
+       end
+
+       init do
+               header.page_js = """
+               function change_answer(ele){
+                       var e = document.getElementById(ele.id);
+                       var i = e.innerHTML;
+                       var ans = true;
+                       if(i === "<center>✔</center>"){
+                               ans = false;
+                               e.innerHTML = "<center>✘</center>"
+                               e.style.color = "red";
+                       }else{
+                               e.innerHTML = "<center>✔</center>";
+                               e.style.color = "green";
+                       }
+                       var a = ele.id.split('_')
+                       var pid = a[1]
+                       var aid = a[2]
+                       $.ajax({
+                               type: "POST",
+                               url: "/rest/answer",
+                               data: {
+                                       answer_id: aid,
+                                       pers_id: pid,
+                                       answer: ans
+                               }
+                       });
+               }
+               function change_temp_answer(ele){
+                       var e = document.getElementById(ele.id);
+                       var i = e.innerHTML;
+                       var ans = true;
+                       if(i === "<center>✔</center>"){
+                               ans = false;
+                               e.innerHTML = "<center>✘</center>";
+                               e.style.color = "red";
+                       }else{
+                               e.innerHTML = "<center>✔</center>";
+                               e.style.color = "green";
+                       }
+               }
+               function add_part(ele){
+                       var e = document.getElementById(ele.id);
+                       var pname = document.getElementById("new_name").value;
+                       var arr = e.id.split("_");
+                       var mid = arr[1];
+                       var ans = $('#' + ele.id).parent().parent().parent().children(".answer");
+                       ansmap = {};
+                       for(i=0;i<ans.length;i++){
+                               var curr = ans.eq(i)
+                               if(curr[0].innerHTML === "✔"){
+                                       ansmap[curr.attr('id')] = true
+                               }else{
+                                       ansmap[curr.attr('id')] = false
+                               }
+                       }
+                       $.ajax({
+                               type: "POST",
+                               url: "/rest/meetup/new_pers",
+                               data: {
+                                       meetup_id: mid,
+                                       persname: pname,
+                                       answers: $.param(ansmap)
+                               }
+                               })
+                               .done(function(data){
+                                       location.reload();
+                               })
+                               .fail(function(data){
+                                       //TODO: Notify of failure
+                               });
+               }
+               function remove_people(ele){
+                       var arr = ele.id.split("_")
+                       var pid = arr[1]
+                       $('#' + ele.id).parent().remove();
+                       $.ajax({
+                               type: "POST",
+                               url: "/rest/people",
+                               data: {
+                                       method: "DELETE",
+                                       p_id: pid
+                               }
+                       });
+               }
+               """
+       end
+
+
+       redef fun rendering do
+               if meetup == null then
+                       add((new OpportunityHomePage).write_to_string)
+                       return
+               end
+               add header
+               var db = new OpportunityDB.open("opportunity")
+               add meetup.to_html(db)
+               db.close
+               add footer
+       end
+end
+
+redef class Meetup
+       # Build the HTML for `self`
+       fun to_html(db: OpportunityDB): Streamable do
+               var t = new Template
+               t.add """
+<div class="page-header">
+       <center><h1>{{{name}}}</h1></center>
+       <center><h4>When : {{{date}}}</h4></center>
+       <center><h4>Where : {{{place}}}</h4></center>
+</div>
+<table class="table">
+"""
+               t.add "<th></th>"
+               t.add "<th>Participating</th>"
+               for i in answers(db) do
+                       t.add "<th>"
+                       t.add i.to_s
+                       t.add "</th>"
+               end
+               t.add "</tr>"
+               for i in participants(db) do
+                       t.add "<tr>"
+                       t.add """<td class="opportunity-action" style="color: red;" onclick="remove_people(this)" id="remove_{{{i.id}}}"><center>❌</center></td>"""
+                       i.load_answers(db, self)
+                       t.add "<td>"
+                       t.add i.to_s
+                       t.add "</td>"
+                       for j,k in i.answers do
+                               t.add """<td class="answer" onclick="change_answer(this)" id="answer_{{{j.id}}}_{{{i.id}}}""""
+                               if k then
+                                       t.add " style=\"color:green;\""
+                               else
+                                       t.add " style=\"color:red;\""
+                               end
+                               t.add"><center>"
+                               if k then
+                                       t.add "✔"
+                               else
+                                       t.add "✘"
+                               end
+                               t.add "</center></td>"
+                       end
+                       t.add "</tr>"
+               end
+               t.add """
+<tr id="newrow">
+<td><center><span id="add_{{{id}}}" onclick="add_part(this)" style="color:green;" class="action"><strong>+</strong></span></center></td>
+       <td><input id="new_name" type="text" placeholder="Your name" class="input-large"></td>
+               """
+               for i in answers(db) do
+                       t.add "<td class=\"answer\" id=\"newans_{i.id}\" onclick=\"change_temp_answer(this)\" style=\"color:red;\"><center>✘</center></td>"
+               end
+               t.add "</tr>"
+               t.add "</table>"
+               return t
+       end
+end
diff --git a/contrib/opportunity/src/templates/meetup_confirmation.nit b/contrib/opportunity/src/templates/meetup_confirmation.nit
new file mode 100644 (file)
index 0000000..0752a37
--- /dev/null
@@ -0,0 +1,44 @@
+# 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
+
+# Page to show when the creation of a Meetup is successful
+module meetup_confirmation
+
+import boilerplate
+import opportunity_model
+
+# Show this page when a `Meetup` is sucessfully created.
+class MeetupConfirmation
+       super OpportunityPage
+
+       var meetup: Meetup
+
+       init do
+               body = """
+               <div class="page-header">
+                       <center><h1>Congratulations !</h1></center>
+               </div>
+               <p class="text-center">
+                       <h2> Your meetup was successfully created. </h2>
+                       <p>
+                       You can invite people to participate to your event by sharing them this link : <a href="/?meetup_id={{{meetup.id}}}">{{{meetup.name}}}</a>
+                       </p>
+                       <p>
+                       See you soon for more Opportunities !
+                       </p>
+               </p>
+               """
+       end
+
+end
diff --git a/contrib/opportunity/src/templates/meetup_creation.nit b/contrib/opportunity/src/templates/meetup_creation.nit
new file mode 100644 (file)
index 0000000..8cd364a
--- /dev/null
@@ -0,0 +1,66 @@
+# 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 meetup_creation
+
+import boilerplate
+
+class MeetupCreationPage
+       super OpportunityPage
+
+       init do
+               header.page_js = """
+                       function new_answer(sender){
+                               var ansdiv = $('#answers');
+                               var nb = ansdiv.children()
+                               var s = nb.last();
+                               var ss = s.attr("id").split('_');
+                               var l = ss[ss.length - 1];
+                               nb = parseInt(l) + 1;
+                               var ch = ansdiv.children();
+                               ch.last().after('<input name="answer_' + nb + '" id="answer_' + nb + '" class="form-control" type="text" placeholder="Answer">')
+                               ch.last().after('<label for="answer_' + nb + '">' + nb + '</label>');
+                       }
+               """
+               body = """
+               <div class="page-header">
+                       <center><h1>New meetup</h1></center>
+               </div>
+               <center>
+               <form action="meetup_create" method="POST" role="form">
+                       <div class = "form-group">
+                               <label for="meetup_name">Meetup Name : </label>
+                               <input name="meetup_name" id="meetup_name" type="text" class="form-control" placeholder="Meetup Name"/>
+                               <label for="meetup_date">When ? </label>
+                               <input name="meetup_date" id="meetup_date" type="text" class="form-control" placeholder="Time of the event">
+                               <label for="meetup=place">Where ? </label>
+                               <input name="meetup_place" id="meetup_place" type="text" class="form-control" placeholder="Place of the event">
+                       </div>
+                       <div id="answers" class="form-group">
+                               <h2>Answers</h2>
+                               <label for="answer_1">1</label>
+                               <input name="answer_1" id="answer_1" type="text" class="form-control" placeholder="Answer">
+                       </div>
+                       <div class="form-group">
+                               <button type="button" class="btn btn-lg" onclick="new_answer(this)">Add answer</button>
+                       </div>
+                       <div class="form-group">
+                               <button type="submit" class="btn btn-lg btn-success">Finish</button>
+                       </div>
+               </form>
+               </center>
+"""
+       end
+
+end
diff --git a/contrib/opportunity/src/templates/templates.nit b/contrib/opportunity/src/templates/templates.nit
new file mode 100644 (file)
index 0000000..d2dfd9c
--- /dev/null
@@ -0,0 +1,22 @@
+# 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
+
+# All the templates used for the view are to be declared here
+module templates
+
+import boilerplate
+import welcome
+import meetup_creation
+import meetup
+import meetup_confirmation
diff --git a/contrib/opportunity/src/templates/welcome.nit b/contrib/opportunity/src/templates/welcome.nit
new file mode 100644 (file)
index 0000000..eaae527
--- /dev/null
@@ -0,0 +1,41 @@
+# 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
+
+# Welcome page for Opportunity
+module welcome
+
+import boilerplate
+
+# Welcome page for Opportunity
+class OpportunityHomePage
+       super OpportunityPage
+
+       init do
+               body = """
+               <div class="page-header">
+               <h1 class="text-center">Welcome to opportunity !</h1>
+                       </div>
+                       <p class="text-center">
+                               <p class="text-center">Opportunity is a free (as in free software), easy-to-use, meetup planifier.</p>
+                               <p class="text-center">You can start using it right now by creating a new Meetup and sharing it with your friends!</p>
+                               <p class="text-center">
+                               <form action="new_meetup">
+                               <button type="submit" class="btn btn-lg center-block btn-success">Create a Meetup</button>
+                               </form>
+                               </p>
+                       </p>
+"""
+       end
+
+end
diff --git a/contrib/opportunity/tests/db_tests.nit b/contrib/opportunity/tests/db_tests.nit
new file mode 100644 (file)
index 0000000..cbec6f7
--- /dev/null
@@ -0,0 +1,99 @@
+# 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
+
+# Tests for the model of Opportunity
+module db_tests
+
+import opportunity_model
+
+redef class OpportunityDB
+
+       fun wipe do
+               execute("DROP TABLE people;")
+               execute("DROP TABLE meetups;")
+               execute("DROP TABLE answers;")
+               execute("DROP TABLE part_answers;")
+               execute("DROP INDEX answers_clean;")
+               execute("DROP INDEX ans_clean;")
+               execute("DROP INDEX ppl_clean;")
+       end
+
+end
+
+print "Opening DB"
+
+var db = new OpportunityDB.open("opportunity")
+
+print "DB opened"
+
+db.wipe
+
+print "Wiped"
+
+db.close
+
+db = new OpportunityDB.open("opportunity")
+
+var hj = new People("Jack", "Handsome")
+
+var m = new Meetup("Awaken the warrior", "2024/05/28", "Vault of the Warrior")
+assert m.commit(db)
+
+var vh = new People("Hunter", "Vault")
+
+var ll = new People("", "Lilith")
+
+var y = new Answer("Yes")
+y.meetup = m
+y.commit(db)
+
+var n = new Answer("No")
+n.meetup = m
+n.commit(db)
+
+var h = new Answer("I have no choice, I'm a hostage")
+h.meetup = m
+h.commit(db)
+
+hj.answer(y) = true
+hj.answer(n) = false
+hj.answer(h) = false
+
+vh.answer(y) = true
+vh.answer(n) = false
+vh.answer(h) = false
+
+ll.answer(y) = true
+ll.answer(n) = false
+ll.answer(h) = true
+
+hj.commit db
+vh.commit db
+ll.commit db
+
+assert hj.commit(db)
+assert vh.commit(db)
+assert ll.commit(db)
+
+print db.find_meetup_by_id(m.id) or else "null"
+
+for i in m.participants(db) do
+       print "Answers for {i.to_s.trim}"
+       i.load_answers(db, m)
+       for k,v in i.answers do
+               print "{k.to_s.trim} => {v.to_s.trim}"
+       end
+end
+
+db.close
diff --git a/contrib/opportunity/tests/sav/db_tests.res b/contrib/opportunity/tests/sav/db_tests.res
new file mode 100644 (file)
index 0000000..1667896
--- /dev/null
@@ -0,0 +1,18 @@
+Opening DB
+DB opened
+Wiped
+Event : Awaken the warrior
+When : 2024/05/28
+Where : Vault of the Warrior
+Answers for Handsome Jack
+Yes => true
+No => false
+I have no choice, I'm a hostage => false
+Answers for Vault Hunter
+Yes => true
+No => false
+I have no choice, I'm a hostage => false
+Answers for Lilith
+Yes => true
+No => false
+I have no choice, I'm a hostage => true
index 259fd09..18ee374 100644 (file)
@@ -108,15 +108,6 @@ redef class AnalysisManager
        fun show_graph(content: String) do "show_graph('{content.escape_to_c}');".run_js
 end
 
-class StringIStream
-       super BufferedIStream
-
-       init(str: String) do _buffer = new FlatBuffer.from(str)
-
-       redef fun fill_buffer do end_reached = true
-       redef var end_reached: Bool = false
-end
-
 redef class NativeString
        fun run_analysis do manager.run to_s
 end
index 1df0119..8d1a849 100644 (file)
@@ -386,7 +386,8 @@ class Bush super Entity end
 
 # Sort entities on screen in order of Y, entities in the back are drawn first
 class EntitiesSorter
-       super AbstractSorter[Entity]
+       super Comparator
+       redef type COMPARED: Entity
 
        redef fun compare(a, b) do return b.pos.y <=> a.pos.y
 end
index f279d4f..21a349d 100644 (file)
@@ -608,7 +608,8 @@ end
 # Used to compare nodes with their score.
 # Smaller is score, smaller is the node.
 private class NodeComparator[S: Object, A]
-       super Comparator[SearchNode[S, A]]
+       super Comparator
+       redef type COMPARED: SearchNode[S, A]
        redef fun compare(a,b) do return a.score <=> b.score
 end
 
index e6a129f..04dcb6e 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Simple numerical statistical analysis and presentation
+# Defines some ANSI Terminal Control Escape Sequences.
 module console
 
-# Redef String class to add a function to color the string
-redef class String
-       private fun add_escape_char(escapechar: String): String do
-               return "{escapechar}{self}{esc}[0m"
+# A ANSI/VT100 escape sequence.
+abstract class TermEscape
+       # The US-ASCII ESC character.
+       protected fun esc: Char do return 27.ascii
+end
+
+# ANSI/VT100 code to switch character attributes (SGR).
+#
+# By default, resets everything to the terminal’s defaults.
+#
+# Note:
+#
+# The escape sequence inserted at the end of the string by terminal-related
+# methods of `String` resets all character attributes to the terminal’s
+# defaults. So, when combining format `a` and `b`, something like
+# `("foo".a + " bar").b` will not work as expected, but `"foo".a.b + " bar".b`
+# will. You may also use `TermCharFormat` (this class).
+#
+# Usage example:
+#
+#     print "{(new TermCharFormat).yellow_fg.bold}a{(new TermCharFormat).yellow_fg}b{new TermCharFormat}"
+class TermCharFormat
+       super TermEscape
+
+       private var attributes: Array[String] = new Array[String]
+
+       # Copies the attributes from the specified format.
+       init from(format: TermCharFormat) do
+               attributes.add_all(format.attributes)
        end
 
-       private fun esc: Char do return 27.ascii
-       fun gray: String do return add_escape_char("{esc}[30m")
-       fun red: String do return add_escape_char("{esc}[31m")
-       fun green: String do return add_escape_char("{esc}[32m")
-       fun yellow: String do return add_escape_char("{esc}[33m")
-       fun blue: String do return add_escape_char("{esc}[34m")
-       fun purple: String do return add_escape_char("{esc}[35m")
-       fun cyan: String do return add_escape_char("{esc}[36m")
-       fun light_gray: String do return add_escape_char("{esc}[37m")
-       fun bold: String do return add_escape_char("{esc}[1m")
-       fun underline: String do return add_escape_char("{esc}[4m")
+       redef fun to_s: String do return "{esc}[{attributes.join(";")}m"
+
+       # Apply the specified SGR and return `self`.
+       private fun apply(sgr: String): TermCharFormat do
+               attributes.add(sgr)
+               return self
+       end
+
+       # Apply normal (default) format and return `self`.
+       fun default: TermCharFormat do return apply("0")
+
+       # Apply bold weight and return `self`.
+       fun bold: TermCharFormat do return apply("1")
+
+       # Apply underlining and return `self`.
+       fun underline: TermCharFormat do return apply("4")
+
+       # Apply blinking or bold weight and return `self`.
+       fun blink: TermCharFormat do return apply("5")
+
+       # Apply reverse video and return `self`.
+       fun inverse: TermCharFormat do return apply("7")
+
+       # Apply normal weight and return `self`.
+       fun normal_weight: TermCharFormat do return apply("22")
+
+       # Add the attribute that disable inderlining and return `self`.
+       fun not_underlined: TermCharFormat do return apply("24")
+
+       # Add the attribute that disable blinking and return `self`.
+       fun steady: TermCharFormat do return apply("25")
+
+       # Add the attribute that disable reverse video and return `self`.
+       fun positive: TermCharFormat do return apply("27")
+
+       # Apply a black foreground and return `self`.
+       fun black_fg: TermCharFormat do return apply("30")
+
+       # Apply a red foreground and return `self`.
+       fun red_fg: TermCharFormat do return apply("31")
+
+       # Apply a green foreground and return `self`.
+       fun green_fg: TermCharFormat do return apply("32")
+
+       # Apply a yellow foreground and return `self`.
+       fun yellow_fg: TermCharFormat do return apply("33")
+
+       # Apply a blue foreground and return `self`.
+       fun blue_fg: TermCharFormat do return apply("34")
+
+       # Apply a mangenta foreground and return `self`.
+       fun magenta_fg: TermCharFormat do return apply("35")
+
+       # Apply a cyan foreground and return `self`.
+       fun cyan_fg: TermCharFormat do return apply("36")
+
+       # Apply a white foreground and return `self`.
+       fun white_fg: TermCharFormat do return apply("37")
+
+       # Apply the default foreground and return `self`.
+       fun default_fg: TermCharFormat do return apply("39")
+
+       # Apply a black backgroud and return `self`.
+       fun black_bg: TermCharFormat do return apply("40")
+
+       # Apply a red backgroud and return `self`.
+       fun red_bg: TermCharFormat do return apply("41")
+
+       # Apply a green backgroud and return `self`.
+       fun green_bg: TermCharFormat do return apply("42")
+
+       # Apply a yellow backgroud and return `self`.
+       fun yellow_bg: TermCharFormat do return apply("43")
+
+       # Apply a blue backgroud and return `self`.
+       fun blue_bg: TermCharFormat do return apply("44")
+
+       # Apply a mangenta backgroud and return `self`.
+       fun magenta_bg: TermCharFormat do return apply("45")
+
+       # Apply a cyan backgroud and return `self`.
+       fun cyan_bg: TermCharFormat do return apply("46")
+
+       # Apply a white backgroud and return `self`.
+       fun white_bg: TermCharFormat do return apply("47")
+
+       # Apply the default backgroud and return `self`.
+       fun default_bg: TermCharFormat do return apply("49")
 end
 
+# Redefine the `String` class to add functions to color the string.
+redef class String
+       private fun apply_format(f: TermCharFormat): String do
+               return "{f}{self}{normal}"
+       end
+
+       private fun normal: TermCharFormat do return new TermCharFormat
+
+       # Make the text appear in dark gray (or black) in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun gray: String do return apply_format(normal.black_fg)
+
+       # Make the text appear in red in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun red: String do return apply_format(normal.red_fg)
+
+       # Make the text appear in green in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun green: String do return apply_format(normal.green_fg)
+
+       # Make the text appear in yellow in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun yellow: String do return apply_format(normal.yellow_fg)
+
+       # Make the text appear in blue in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun blue: String do return apply_format(normal.blue_fg)
+
+       # Make the text appear in mangenta in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun purple: String do return apply_format(normal.magenta_fg)
+
+       # Make the text appear in cyan in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun cyan: String do return apply_format(normal.cyan_fg)
+
+       # Make the text appear in light gray (or white) in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun light_gray: String do return apply_format(normal.white_fg)
+
+       # Make the text appear in bold in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun bold: String do return apply_format(normal.bold)
+
+       # Make the text underlined in a ANSI/VT100 terminal.
+       #
+       # WARNING: SEE: `TermCharFormat`
+       fun underline: String do return apply_format(normal.underline)
+end
index 75278d2..670fc59 100644 (file)
@@ -174,7 +174,8 @@ class Counter[E: Object]
 end
 
 private class CounterComparator[E: Object]
-       super Comparator[E]
+       super Comparator
+       redef type COMPARED: E
        var counter: Counter[E]
        redef fun compare(a,b) do return self.counter.map[a] <=> self.counter.map[b]
 end
diff --git a/lib/io/io.nit b/lib/io/io.nit
new file mode 100644 (file)
index 0000000..c48b171
--- /dev/null
@@ -0,0 +1,14 @@
+# 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.
+
+# Additional services for streams.
+module io
+
+import push_back_reader
diff --git a/lib/io/push_back_reader.nit b/lib/io/push_back_reader.nit
new file mode 100644 (file)
index 0000000..257c662
--- /dev/null
@@ -0,0 +1,110 @@
+# 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.
+
+# Input stream that permits to push bytes back to the stream.
+module io::push_back_reader
+
+# Input stream that permits to push bytes back to the stream.
+interface PushBackReader
+       super IStream
+
+       # Push the specified byte back to the stream.
+       #
+       # The specified byte is usually the last read byte that is not
+       # “unread” already.
+       fun unread_char(c: Char) is abstract
+
+       # Push the specified string back to the stream.
+       #
+       # The specified string is usually the last read string that is not
+       # “unread” already.
+       fun unread(s: String) do
+               for c in s.chars.reverse_iterator do unread_char(c)
+       end
+end
+
+# Decorates an input stream to permit to push bytes back to the input stream.
+class PushBackDecorator
+       super PushBackReader
+
+       # The parent stream.
+       var parent: IStream
+
+       # The stack of the pushed-back bytes.
+       #
+       # Bytes are in the reverse order they will reappear in the stream.
+       # `unread` pushes bytes after already pushed-back bytes.
+       #
+       # TODO: With optimized bulk array copy operations, a reversed stack (like in
+       # OpenJDK) would be more efficient.
+       private var buf: Sequence[Char] = new Array[Char]
+
+       redef fun read_char: Int do
+               if buf.length <= 0 then return parent.read_char
+               return buf.pop.ascii
+       end
+
+       redef fun read(i: Int): String do
+               if i <= 0 then return ""
+               if buf.length <= 0 then return parent.read(i)
+               var s = new FlatBuffer.with_capacity(i)
+
+               loop
+                       s.chars.push(buf.pop)
+                       i -= 1
+                       if i <= 0 then
+                               return s.to_s
+                       else if buf.length <= 0 then
+                               s.append(parent.read(i))
+                               return s.to_s
+                       end
+               end
+       end
+
+       redef fun read_all: String do
+               if buf.length <= 0 then return parent.read_all
+               var s = new FlatBuffer
+
+               loop
+                       s.chars.push(buf.pop)
+                       if buf.length <= 0 then
+                               s.append(parent.read_all)
+                               return s.to_s
+                       end
+               end
+       end
+
+       redef fun append_line_to(s: Buffer) do
+               if buf.length > 0 then
+                       var c: Char
+
+                       loop
+                               c = buf.pop
+                               s.chars.push(c)
+                               if c == '\n' then return
+                               if buf.length <= 0 then break
+                       end
+               end
+               parent.append_line_to(s)
+       end
+
+       redef fun eof: Bool do return buf.length <= 0 and parent.eof
+
+       redef fun close do
+               buf.clear
+               parent.close
+       end
+
+       redef fun unread_char(c: Char) do buf.push(c)
+
+       redef fun unread(s: String) do
+               for c in s.chars.reverse_iterator do buf.push(c)
+       end
+end
diff --git a/lib/io/test_push_back_reader.nit b/lib/io/test_push_back_reader.nit
new file mode 100644 (file)
index 0000000..5bace8f
--- /dev/null
@@ -0,0 +1,158 @@
+# 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.
+
+# Test suites for module `push_back_reader`
+module test_push_back_reader is test_suite
+
+import test_suite
+import io::push_back_reader
+
+class TestPushBackDecorator
+       super TestSuite
+
+       private fun sample: PushBackDecorator do
+               return new PushBackDecorator(new StringIStream("""
+abcd
+
+efg
+"""))
+       end
+
+       fun test_read_char do
+               var subject = sample
+
+               assert 'a' == subject.read_char.ascii
+       end
+
+       fun test_read_char_eof do
+               var subject = new PushBackDecorator(new StringIStream(""))
+
+               assert -1 == subject.read_char
+       end
+
+       fun test_unread_read_char do
+               var subject = sample
+
+               subject.unread_char('z')
+               assert 'z' == subject.read_char.ascii
+               assert 'a' == subject.read_char.ascii
+       end
+
+       fun test_read_partial do
+               var subject = sample
+
+               assert "abcd" == subject.read(4)
+       end
+
+       fun test_read_too_much do
+               var subject = sample
+               var exp = """
+abcd
+
+efg
+"""
+               assert exp == subject.read(100)
+       end
+
+       fun test_unread_read do
+               var subject = sample
+
+               subject.unread("a")
+               assert "aab" == subject.read(3)
+       end
+
+       fun test_unread_read_mixed do
+               var subject = sample
+
+               subject.unread("a")
+               assert "aab" == subject.read(3)
+       end
+
+       fun test_read_all do
+               var subject = sample
+               var exp = """
+abcd
+
+efg
+"""
+               assert exp == subject.read_all
+       end
+
+       fun test_unread_read_all do
+               var subject = sample
+               var exp = """
+fooabcd
+
+efg
+"""
+               subject.unread("foo")
+               assert exp == subject.read_all
+       end
+
+       fun test_read_line do
+               var subject = sample
+
+               assert "abcd\n" == subject.read_line
+               assert "\n" == subject.read_line
+       end
+
+       fun test_unread_read_line do
+               var subject = sample
+
+               subject.unread("a\nb")
+               assert "a\n" == subject.read_line
+               assert "babcd\n" == subject.read_line
+       end
+
+       fun test_eof do
+               var subject = sample
+
+               assert not subject.eof
+               subject.read_all
+               assert subject.eof
+       end
+
+       fun test_eof_empty do
+               var subject = new PushBackDecorator(new StringIStream(""))
+
+               assert subject.eof
+       end
+
+       fun test_close do
+               var subject = sample
+
+               subject.close
+               assert subject.eof
+       end
+
+       fun test_unread_close do
+               var subject = sample
+
+               subject.unread("foo")
+               subject.close
+               assert subject.eof
+       end
+
+       fun test_unread_char_order do
+               var subject = sample
+
+               subject.unread_char('z')
+               subject.unread_char('y')
+               assert "yzab" == subject.read(4)
+       end
+
+       fun test_unread_order do
+               var subject = sample
+
+               subject.unread("bar")
+               subject.unread("foo")
+               assert "foobarab" == subject.read(8)
+       end
+end
index c590a85..4ed9664 100644 (file)
@@ -76,12 +76,12 @@ redef class App
        redef fun setup
        do
                var env = "MNIT_SRAND".environ
-               if env != null and env != "" then
+               if env != "" then
                        srand_from(env.to_i)
                end
 
                var input = "MNIT_READ_INPUT".environ
-               if input != null and input != "" then
+               if input != "" then
                        injected_input_stream = new IFStream.open(input)
                        print "GET injected_input_stream {input}"
                end
index 07b91ad..47e6823 100644 (file)
@@ -57,6 +57,41 @@ class HttpRequest
 
        # The arguments passed with the POST or GET method (with a priority on POST)
        var all_args = new HashMap[String, String]
+
+       # Returns argument `arg_name` in the request as a String
+       # or null if it was not found.
+       # Also cleans the String by trimming it.
+       # If the Strings happens to be empty after trimming,
+       # the method will return `null`
+       #
+       # NOTE: Prioritizes POST before GET
+       fun string_arg(arg_name: String): nullable String do
+               if not all_args.has_key(arg_name) then return null
+               var s = all_args[arg_name].trim
+               if s.is_empty then return null
+               return s
+       end
+
+       # Returns argument `arg_name` as an Int or `null` if not found or not a number.
+       #
+       # NOTE: Prioritizes POST before GET
+       fun int_arg(arg_name: String): nullable Int do
+               if not all_args.has_key(arg_name) then return null
+               var i = all_args[arg_name]
+               if not i.is_numeric then return null
+               return i.to_i
+       end
+
+       # Returns argument `arg_name` as a Bool or `null` if not found or not a boolean.
+       #
+       # NOTE: Prioritizes POST before GET
+       fun bool_arg(arg_name: String): nullable Bool do
+               if not all_args.has_key(arg_name) then return null
+               var i = all_args[arg_name]
+               if i == "true" then return true
+               if i == "false" then return false
+               return null
+       end
 end
 
 # Utility class to parse a request string and build a `HttpRequest`
@@ -114,10 +149,6 @@ class HttpRequestParser
                                var parts = line.split_once_on('=')
                                if parts.length > 1 then
                                        var decoded = parts[1].replace('+', " ").from_percent_encoding
-                                       if decoded == null then
-                                               print "decode error"
-                                               continue
-                                       end
                                        http_request.post_args[parts[0]] = decoded
                                        http_request.all_args[parts[0]] = decoded
                                else
index a1bffc1..c088325 100644 (file)
@@ -194,11 +194,7 @@ class OptionEnum
 
        redef fun pretty_default
        do
-               if default_value != null then
-                       return " ({values[default_value]})"
-               else
-                       return ""
-               end
+               return " ({values[default_value]})"
        end
 end
 
index f6cd69a..91f8e63 100644 (file)
@@ -80,7 +80,7 @@ class OrderedTree[E: Object]
 
        # Sort roots and other elements using a comparator method
        # This method basically sorts roots then each group of children
-       fun sort_with(comparator: Comparator[E])
+       fun sort_with(comparator: Comparator)
        do
                comparator.sort(roots)
                for a in sub.values do
index da1b4c4..31d007b 100644 (file)
@@ -35,7 +35,7 @@ redef interface Iterator[E]
 
        # Filter: sort with a given `comparator`.
        # Important: require O(n) memory.
-       fun sort_with(comparator: Comparator[E]): Iterator[E]
+       fun sort_with(comparator: Comparator): Iterator[E]
        do
                var a = self.to_a
                comparator.sort(a)
index 3149b21..7a26f90 100644 (file)
@@ -24,7 +24,9 @@ module poset
 #  * transitivity: `(self.has_edge(e,f) and self.has_edge(f,g)) implies self.has_edge(e,g)`
 class POSet[E: Object]
        super Collection[E]
-       super Comparator[E]
+       super Comparator
+
+       redef type COMPARED: E is fixed
 
        redef fun iterator do return elements.keys.iterator
 
index 89f4a1b..f1044e8 100644 (file)
 # limitations under the License.
 
 # Module to manage standard C signals
+#
+# Common usage imply 5 steps:
+#
+# 1. Implement the `SignalHandler` interface
+# 2. `redef receive_signal_unsafe` to handle `sigsegv`
+# 3. `redef receive_signal` to handle other signals safely
+# 4, Notify what signals to handle with `handle_signal`
+# 5. If using the safe handler method, routinely call `check_signals`
+#
+# Usage example:
+#
+# ~~~~
+# class MyReceiver
+#      super SignalHandler
+#
+#      redef fun receive_signal(signal)
+#      do
+#              print "received safely {signal}"
+#              if signal == sigalarm then print "Alarm!"
+#      end
+#      redef fun receive_signal_unsafe( signal ) do print "received unsafely {signal}"
+# end
+#
+# var r = new MyReceiver
+#
+# # Handle `sigsegv` signal unsafely (the only way for this one)
+# r.handle_signal(sigsegv, false)
+#
+# # Handle `sigint` and `sigalarm` safely
+# r.handle_signal(sigint, true)
+# r.handle_signal(sigalarm, true)
+#
+# Ask system to receive a `sigalarm` signal in 1 second
+# set_alarm(1)
+#
+# loop
+#      # Check signals and callback `receive_signal`
+#      var hit = check_signals
+#
+#      if hit then break
+# end
+# ~~~~
 module signals
 
 `{
index d016a4c..05cbed0 100644 (file)
@@ -105,6 +105,9 @@ class Sqlite3DB
                if err.is_ok then return null
                return err.to_s
        end
+
+       # Returns the id for the last successful insert on the current connection.
+       fun last_insert_rowid: Int do return native_connection.last_insert_rowid
 end
 
 # A prepared Sqlite3 statement, created from `Sqlite3DB::prepare` or `Sqlite3DB::select`
index d2cdb2c..843f8eb 100644 (file)
@@ -152,6 +152,16 @@ interface Iterator[E]
 
        # Iterate over `self`
        fun iterator: Iterator[E] do return self
+
+       # Post-iteration hook.
+       #
+       # Used to inform `self` that the iteration is over.
+       # Specific iterators can use this to free some resources.
+       #
+       # Is automatically invoked at the end of `for` structures.
+       #
+       # Do nothing by default.
+       fun finish do end
 end
 
 # A collection that contains only one item.
@@ -526,6 +536,16 @@ interface MapIterator[K: Object, V]
 
        # Set a new `item` at `key`.
        #fun item=(item: E) is abstract
+
+       # Post-iteration hook.
+       #
+       # Used to inform `self` that the iteration is over.
+       # Specific iterators can use this to free some resources.
+       #
+       # Is automatically invoked at the end of `for` structures.
+       #
+       # Do nothing by default.
+       fun finish do end
 end
 
 # Iterator on a 'keys' point of view of a map
index b6319e1..cfcc376 100644 (file)
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# This module contains classes used to sorts arrays.
+# This module contains classes used to compare things and sorts arrays.
+#
 # In order to provide your own sort class you should define a subclass of `Comparator` with
-# a custom `Comparator::compare` function.
+# a custom `Comparator::compare` function and a specific `COMPARED` virtual type.
 module sorter
 
 import range
 import array
 
 # This abstract class generalizes ways to sort an array
-interface Comparator[E]
+interface Comparator
+       # What to compare to
+       type COMPARED: nullable Object
+
        # Compare `a` and `b`.
        # Returns:
        #       -1 if a < b
        #       0  if a = b
        #       1  if a > b
-       fun compare(a: E, b: E): Int is abstract
+       fun compare(a: COMPARED, b: COMPARED): Int is abstract
+
+       # Is `seq` sorted?
+       #
+       #     assert default_comparator.is_sorted([1,2,2,3])   == true
+       #     assert default_comparator.is_sorted([1,10,2,3])  == false
+       #     assert alpha_comparator.is_sorted([1,10,2,3])    == true
+       fun is_sorted(seq: SequenceRead[COMPARED]): Bool
+       do
+               if seq.length <= 1 then return true
+               var prev = seq.first
+               for e in seq do
+                       if compare(prev, e) > 0 then return false
+                       prev = e
+               end
+               return true
+       end
+
+       # Returns the minimum between `a` and `b`.
+       #
+       #     assert default_comparator.min(2,10) == 2
+       #     assert alpha_comparator.min(2,10)   == 10
+       #
+       # If both are equivalent, then returns `a`.
+       #
+       #     var m = alpha_comparator.min(1, "1")
+       #     assert m == 1
+       #     assert m != "1"
+       fun min(a,b: COMPARED): COMPARED
+       do
+               if compare(a,b) > 0 then return b else return a
+       end
+
+       # Returns the maximum between `a` and `b`.
+       #
+       #     assert default_comparator.max(2,10) == 10
+       #     assert alpha_comparator.max(2,10)   == 2
+       #
+       # If both are equivalent, then returns `a`.
+       #
+       #     var m = alpha_comparator.max(1, "1")
+       #     assert m == 1
+       #     assert m != "1"
+       fun max(a,b: COMPARED): COMPARED
+       do
+               if compare(a,b) < 0 then return b else return a
+       end
 
        # Sort `array` using the `compare` function.
        #
-       #     var s = new DefaultComparator[Int]
-       #     var a = [5, 2, 3, 1, 4]
-       #     s.sort(a)
-       #     assert a == [1, 2, 3, 4, 5]
-       fun sort(array: Array[E]) do sub_sort(array, 0, array.length-1)
+       #     var a = [10, 2, 3, 1, 4]
+       #     default_comparator.sort(a)
+       #     assert a == [1, 2, 3, 4, 10]
+       #     alpha_comparator.sort(a)
+       #     assert a == [1, 10, 2, 3, 4]
+       fun sort(array: Array[COMPARED]) do sub_sort(array, 0, array.length-1)
 
        # Sort `array` between `from` and `to` indices
-       private fun sub_sort(array: Array[E], from: Int, to: Int)
+       private fun sub_sort(array: Array[COMPARED], from: Int, to: Int)
        do
                if from >= to then
                        return
@@ -50,11 +101,10 @@ interface Comparator[E]
        # Quick-sort `array` between `from` and `to` indices
        # Worst case: O(n^2), Average case: O(n lg n)
        #
-       #     var s = new DefaultComparator[Int]
        #     var a = [5, 2, 3, 1, 4]
-       #     s.quick_sort(a, 0, a.length - 1)
+       #     default_comparator.quick_sort(a, 0, a.length - 1)
        #     assert a == [1, 2, 3, 4, 5]
-       fun quick_sort(array: Array[E], from: Int, to: Int) do
+       fun quick_sort(array: Array[COMPARED], from: Int, to: Int) do
                var pivot = array[from]
                var i = from
                var j = to
@@ -76,11 +126,10 @@ interface Comparator[E]
        # Bubble-sort `array` between `from` and `to` indices
        # Worst case: O(n^2), average case: O(n^2)
        #
-       #     var s = new DefaultComparator[Int]
        #     var a = [5, 2, 3, 1, 4]
-       #     s.bubble_sort(a, 0, a.length - 1)
+       #     default_comparator.bubble_sort(a, 0, a.length - 1)
        #     assert a == [1, 2, 3, 4, 5]
-       fun bubble_sort(array: Array[E], from: Int, to: Int)
+       fun bubble_sort(array: Array[COMPARED], from: Int, to: Int)
        do
                var i = from
                while i < to do
@@ -105,12 +154,10 @@ interface Comparator[E]
        # Insertion-sort `array` between `from` and `to` indices
        # Worst case: O(n^2), average case: O(n^2)
        #
-       #     var s = new DefaultComparator[Int]
        #     var a = [5, 2, 3, 1, 4]
-       #     s.insertion_sort(a, 0, a.length - 1)
+       #     default_comparator.insertion_sort(a, 0, a.length - 1)
        #     assert a == [1, 2, 3, 4, 5]
-       fun insertion_sort(array: Array[E], from: Int, to: Int) do
-               var last = array.length
+       fun insertion_sort(array: Array[COMPARED], from: Int, to: Int) do
                for i in [from..to] do
                        var j = i
                        while j > 0 and compare(array[j], array[j - 1]) < 0 do
@@ -123,11 +170,10 @@ interface Comparator[E]
        # Merge-sort `array` between `from` and `to` indices
        # Worst case: O(n lg n), average: O(n lg n)
        #
-       #     var s = new DefaultComparator[Int]
        #     var a = [5, 2, 3, 1, 4]
-       #     s.merge_sort(a, 0, a.length - 1)
+       #     default_comparator.merge_sort(a, 0, a.length - 1)
        #     assert a == [1, 2, 3, 4, 5]
-       fun merge_sort(array: Array[E], from, to: Int) do
+       fun merge_sort(array: Array[COMPARED], from, to: Int) do
                if from >= to then return
                var mid = (to + from) / 2
                merge_sort(array, from, mid)
@@ -135,10 +181,10 @@ interface Comparator[E]
                merge(array, from, mid, to)
        end
 
-       private fun merge(array: Array[E], from, mid, to: Int) do
-               var l = new Array[E]
+       private fun merge(array: Array[COMPARED], from, mid, to: Int) do
+               var l = new Array[COMPARED]
                for i in [from..mid] do l.add array[i]
-               var r = new Array[E]
+               var r = new Array[COMPARED]
                for i in [mid + 1..to] do r.add array[i]
                var i = 0
                var j = 0
@@ -162,11 +208,10 @@ interface Comparator[E]
        # Heap-sort `array` between `from` and `to` indices
        # Worst case: O(n lg n), average: O(n lg n)
        #
-       #     var s = new DefaultComparator[Int]
        #     var a = [5, 2, 3, 1, 4]
-       #     s.heap_sort(a, 0, a.length - 1)
+       #     default_comparator.heap_sort(a, 0, a.length - 1)
        #     assert a == [1, 2, 3, 4, 5]
-       fun heap_sort(array: Array[E], from, to: Int) do
+       fun heap_sort(array: Array[COMPARED], from, to: Int) do
                var size = build_heap(array)
                for j in [from..to[ do
                        array.swap_at(0, size)
@@ -175,7 +220,7 @@ interface Comparator[E]
                end
        end
 
-       private fun build_heap(array: Array[E]): Int do
+       private fun build_heap(array: Array[COMPARED]): Int do
                var size = array.length - 1
                var i = size / 2
                while i >= 0 do
@@ -185,7 +230,7 @@ interface Comparator[E]
                return size
        end
 
-       private fun heapify(array: Array[E], from, to: Int) do
+       private fun heapify(array: Array[COMPARED], from, to: Int) do
                var l = from * 2
                var r = l + 1
                var largest: Int
@@ -205,25 +250,14 @@ interface Comparator[E]
 
 end
 
-# Deprecated class, use `Comparator` instead
-interface AbstractSorter[E]
-       super Comparator[E]
-end
-
 # This comparator uses the operator `<=>` to compare objects.
 # see `default_comparator` for an easy-to-use general stateless default comparator.
-class DefaultComparator[E: Comparable]
-       super Comparator[E]
+class DefaultComparator
+       super Comparator
+       redef type COMPARED: Comparable
        # Return a <=> b
        redef fun compare(a, b) do return a <=> b
-
-       init do end
-end
-
-# Deprecated class, use `DefaultComparator` instead
-class ComparableSorter[E: Comparable]
-       super DefaultComparator[E]
 end
 
 # Easy-to-use general stateless default comparator that uses `<=>` to compare things.
-fun default_comparator: Comparator[Comparable] do return once new DefaultComparator[Comparable]
+fun default_comparator: DefaultComparator do return once new DefaultComparator
index 36878e5..30e48e3 100644 (file)
@@ -65,6 +65,7 @@ class IFStream
        redef fun close
        do
                var i = _file.io_close
+               _buffer.clear
                end_reached = true
        end
 
@@ -327,27 +328,49 @@ redef class String
 
        # Correctly join two path using the directory separator.
        #
-       # Using a standard "{self}/{path}" does not work when `self` is the empty string.
-       # This method ensure that the join is valid.
+       # Using a standard "{self}/{path}" does not work in the following cases:
+       #
+       # * `self` is empty.
+       # * `path` ends with `'/'`.
+       # * `path` starts with `'/'`.
        #
-       #     assert "hello".join_path("world")      ==  "hello/world"
-       #     assert "hel/lo".join_path("wor/ld")      ==  "hel/lo/wor/ld"
-       #     assert "".join_path("world")      ==  "world"
-       #     assert "/hello".join_path("/world")      ==  "/world"
+       # This method ensures that the join is valid.
        #
-       # Note: you may want to use `simplify_path` on the result
+       #     assert "hello".join_path("world")   == "hello/world"
+       #     assert "hel/lo".join_path("wor/ld") == "hel/lo/wor/ld"
+       #     assert "".join_path("world")        == "world"
+       #     assert "hello".join_path("/world")  == "/world"
+       #     assert "hello/".join_path("world")  == "hello/world"
+       #     assert "hello/".join_path("/world") == "/world"
        #
-       # Note: I you want to join a great number of path, you can write
+       # Note: You may want to use `simplify_path` on the result.
        #
-       #     [p1, p2, p3, p4].join("/")
+       # Note: This method works only with POSIX paths.
        fun join_path(path: String): String
        do
                if path.is_empty then return self
                if self.is_empty then return path
                if path.chars[0] == '/' then return path
+               if self.last == '/' then return "{self}{path}"
                return "{self}/{path}"
        end
 
+       # Convert the path (`self`) to a program name.
+       #
+       # Ensure the path (`self`) will be treated as-is by POSIX shells when it is
+       # used as a program name. In order to do that, prepend `./` if needed.
+       #
+       #     assert "foo".to_program_name == "./foo"
+       #     assert "/foo".to_program_name == "/foo"
+       #     assert "".to_program_name == "./" # At least, your shell will detect the error.
+       fun to_program_name: String do
+               if self.has_prefix("/") then
+                       return self
+               else
+                       return "./{self}"
+               end
+       end
+
        # Alias for `join_path`
        #
        #     assert "hello" / "world"      ==  "hello/world"
index ba4db63..a2e0966 100644 (file)
@@ -23,10 +23,32 @@ in "C header" `{
 redef class Int
        # Returns a random `Int` in `[0 .. self[`.
        fun rand: Int is extern "kernel_Int_Int_rand_0"
+
+       # Returns the result of a binary AND operation on `self` and `i`
+       #
+       #    assert 0x10.bin_and(0x01) == 0
        fun bin_and(i: Int): Int is extern "kernel_Int_Int_binand_0"
+
+       # Returns the result of a binary OR operation on `self` and `i`
+       #
+       #    assert 0x10.bin_or(0x01) == 0x11
        fun bin_or(i: Int): Int is extern "kernel_Int_Int_binor_0"
+
+       # Returns the result of a binary XOR operation on `self` and `i`
+       #
+       #    assert 0x101.bin_xor(0x110) == 0x11
        fun bin_xor(i: Int): Int is extern "kernel_Int_Int_binxor_0"
+
+       # Returns the 1's complement of `self`
+       #
+       #    assert 0x2F.bin_not == -48
+       fun bin_not: Int is extern "kernel_Int_Int_binnot_0"
+
+       # Returns the square root of `self`
+       #
+       #    assert 16.sqrt == 4
        fun sqrt: Int `{ return sqrt(recv); `}
+
        # Returns the greatest common divisor of `self` and `o`
        #
        #     assert 54.gcd(24)   == 6
index 466a443..e13aac3 100644 (file)
@@ -21,6 +21,7 @@
 #define kernel_Int_Int_binand_0(self, p0) (self & p0)
 #define kernel_Int_Int_binor_0(self, p0) (self | p0)
 #define kernel_Int_Int_binxor_0(self, p0) (self ^ p0)
+#define kernel_Int_Int_binnot_0(self) (~self)
 #define kernel_Float_Float_sqrt_0(self) sqrt(self)
 #define kernel_Float_Float_cos_0(self) cos(self)
 #define kernel_Float_Float_sin_0(self) sin(self)
index c4cce96..ceab407 100644 (file)
@@ -47,7 +47,7 @@ interface Queue[E]
        # assert a.take == 2
        # assert a.take == 1
        #
-       # var h = new MinHeapCmp[Int]
+       # var h = new MinHeap[Int].default
        # h.add 2
        # h.add 1
        # h.add 10
@@ -206,11 +206,10 @@ end
 # A min-heap implemented over an array
 #
 # The order is given by the `comparator`.
-# If `E` is Comparable, then the subclass `MinHeapCmp` can be used instead.
 #
 # ~~~
 # var a = [3,4,1,2]
-# var h = new MinHeap[Int](new DefaultComparator[Int])
+# var h = new MinHeap[Int].default
 # h.add_all(a)
 # assert h.peek == 1
 # var b = h.take_all
@@ -220,7 +219,20 @@ class MinHeap[E: Object]
        super Queue[E]
 
        private var items = new Array[E]
-       var comparator: Comparator[E]
+
+       # The comparator used to order the Heap
+       var comparator: Comparator
+
+       # Use the `default_comparator` on Comparable elements
+       #
+       # Require self isa MinHeap[Comparable]
+       init default
+       do
+               assert self isa MinHeap[Comparable]
+               init(default_comparator)
+       end
+
+       init(comparator: Comparator) do self.comparator = comparator
 
        redef fun is_empty do return items.is_empty
        redef fun length do return items.length
@@ -307,18 +319,3 @@ class MinHeap[E: Object]
                return true
        end
 end
-
-# A `MinHeap` for `Comparable` that does not need a specific `Comparator`
-#
-# ~~~
-# var a = [3,4,1,2]
-# var h = new MinHeapCmp[Int]
-# h.add_all(a)
-# assert h.peek == 1
-# var b = h.take_all
-# assert b == [1, 2, 3, 4]
-# ~~~
-class MinHeapCmp[E: Comparable]
-       super MinHeap[E]
-       init is old_style_init do super(new DefaultComparator[E])
-end
index 6b70e21..6765305 100644 (file)
@@ -1,13 +1,11 @@
 # 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
+# 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
+# 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
+# You are allowed to redistribute it and sell it, alone or is a part of
 # another product.
 
 # Input and output streams of characters
@@ -209,7 +207,7 @@ abstract class BufferedIStream
                        fill_buffer
                end
                return s.to_s
-       end   
+       end
 
        redef fun append_line_to(s)
        do
@@ -258,7 +256,7 @@ abstract class BufferedIStream
        # Fill the buffer
        protected fun fill_buffer is abstract
 
-       # Is the last fill_buffer reach the end 
+       # Is the last fill_buffer reach the end
        protected fun end_reached: Bool is abstract
 
        # Allocate a `_buffer` for a given `capacity`.
@@ -399,7 +397,9 @@ redef interface Object
        `}
 end
 
-# Stream to a String. Mainly used for compatibility with OStream type and tests.
+# Stream to a String.
+#
+# Mainly used for compatibility with OStream type and tests.
 class StringOStream
        super OStream
 
@@ -415,3 +415,33 @@ class StringOStream
        protected var closed = false
        redef fun close do closed = true
 end
+
+# Stream from a String.
+#
+# Mainly used for compatibility with IStream type and tests.
+class StringIStream
+       super IStream
+
+       # The string to read from.
+       var source: String
+
+       # The current position in the string.
+       private var cursor: Int = 0
+
+       redef fun read_char do
+               if cursor < source.length then
+                       var c = source[cursor].ascii
+
+                       cursor += 1
+                       return c
+               else
+                       return -1
+               end
+       end
+
+       redef fun close do
+               source = ""
+       end
+
+       redef fun eof do return cursor >= source.length
+end
index 3eede86..f78c0ac 100644 (file)
@@ -1775,12 +1775,12 @@ redef class Int
 end
 
 redef class Float
-       # Pretty print self, print needoed decimals up to a max of 3.
+       # Pretty representation of `self`, with decimals as needed from 1 to a maximum of 3
        #
-       #     assert 12.34.to_s        == "12.34"
-       #     assert (-0120.03450).to_s  == "-120.035"
+       #     assert 12.34.to_s       == "12.34"
+       #     assert (-0120.030).to_s == "-120.03"
        #
-       # see `to_precision` for a different precision.
+       # see `to_precision` for a custom precision.
        redef fun to_s do
                var str = to_precision( 3 )
                if is_inf != 0 or is_nan then return str
@@ -1799,13 +1799,15 @@ redef class Float
                return str
        end
 
-       # `self` representation with `nb` digits after the '.'.
+       # `String` representation of `self` with the given number of `decimals`
        #
-       #     assert 12.345.to_precision(1) == "12.3"
-       #     assert 12.345.to_precision(2) == "12.35"
-       #     assert 12.345.to_precision(3) == "12.345"
-       #     assert 12.345.to_precision(4) == "12.3450"
-       fun to_precision(nb: Int): String
+       #     assert 12.345.to_precision(0)    == "12"
+       #     assert 12.345.to_precision(3)    == "12.345"
+       #     assert (-12.345).to_precision(3) == "-12.345"
+       #     assert (-0.123).to_precision(3)  == "-0.123"
+       #     assert 0.999.to_precision(2)     == "1.00"
+       #     assert 0.999.to_precision(4)     == "0.9990"
+       fun to_precision(decimals: Int): String
        do
                if is_nan then return "nan"
 
@@ -1816,25 +1818,34 @@ redef class Float
                        return  "-inf"
                end
 
-               if nb == 0 then return self.to_i.to_s
+               if decimals == 0 then return self.to_i.to_s
                var f = self
-               for i in [0..nb[ do f = f * 10.0
+               for i in [0..decimals[ do f = f * 10.0
                if self > 0.0 then
                        f = f + 0.5
                else
                        f = f - 0.5
                end
                var i = f.to_i
-               if i == 0 then return "0.0"
-               var s = i.to_s
+               if i == 0 then return "0." + "0"*decimals
+
+               # Prepare both parts of the float, before and after the "."
+               var s = i.abs.to_s
                var sl = s.length
-               if sl > nb then
-                       var p1 = s.substring(0, s.length-nb)
-                       var p2 = s.substring(s.length-nb, nb)
-                       return p1 + "." + p2
+               var p1
+               var p2
+               if sl > decimals then
+                       # Has something before the "."
+                       p1 = s.substring(0, sl-decimals)
+                       p2 = s.substring(sl-decimals, decimals)
                else
-                       return "0." + ("0"*(nb-sl)) + s
+                       p1 = "0"
+                       p2 = "0"*(decimals-sl) + s
                end
+
+               if i < 0 then p1 = "-" + p1
+
+               return p1 + "." + p2
        end
 
        # `self` representation with `nb` digits after the '.'.
@@ -2119,7 +2130,8 @@ end
 #
 # Note: it caching is not usefull, see `alpha_comparator`
 class CachedAlphaComparator
-       super Comparator[Object]
+       super Comparator
+       redef type COMPARED: Object
 
        private var cache = new HashMap[Object, String]
 
@@ -2137,7 +2149,7 @@ end
 
 # see `alpha_comparator`
 private class AlphaComparator
-       super Comparator[Object]
+       super Comparator
        redef fun compare(a, b) do return a.to_s <=> b.to_s
 end
 
@@ -2149,7 +2161,7 @@ end
 #     var a = [1, 2, 3, 10, 20]
 #     alpha_comparator.sort(a)
 #     assert a == [1, 10, 2, 20, 3]
-fun alpha_comparator: Comparator[Object] do return once new AlphaComparator
+fun alpha_comparator: Comparator do return once new AlphaComparator
 
 # The arguments of the program as given by the OS
 fun args: Sequence[String]
index 2d38c2f..d0f1ac8 100755 (executable)
@@ -26,7 +26,7 @@
 exclude="parser_abs.nit\|nitcc_lexer0"
 
 # Directories needed for explicit -I options.
-includedirs="src contrib/tnitter/src/ contrib/benitlux/src/ examples/ contrib/wiringPi/lib/"
+includedirs="src contrib/tnitter/src/ contrib/benitlux/src/ examples/ contrib/wiringPi/lib/ contrib/opportunity/src/"
 
 # Flag -I
 incl=""
diff --git a/share/man/Makefile b/share/man/Makefile
new file mode 100644 (file)
index 0000000..3eeeb03
--- /dev/null
@@ -0,0 +1,21 @@
+# 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.
+
+IN=$(wildcard nit*.md)
+OUT=$(patsubst %.md,man1/%.1,$(IN))
+
+all: $(OUT)
+
+man1/%.1: %.md
+       pandoc $< -t man -s -o $@
diff --git a/share/man/README.md b/share/man/README.md
new file mode 100644 (file)
index 0000000..a167029
--- /dev/null
@@ -0,0 +1,21 @@
+# Manual pages for nit commands
+
+Pages, in markdown, in this directory where initially generated thanks to the `--stub-man` options of the commands.
+
+Transformation to real man pages (troff) are done with `pandoc -t man -s` command.
+See the `Makefile`.
+
+
+Once generated, manpages can then be checked individually with `man -l`
+
+~~~
+man -l man1/nitg.1
+~~~
+
+For global access, one can set the `MANPATH` environment variable to this `man` directory (not the `man1` subdirectory).
+Do not forget to append a trailing column (`:`) to keep existing manpages accessible.
+
+~~~
+export MANPATH=/path/to/nit/share/man:
+man nitg
+~~~
diff --git a/share/man/nit.md b/share/man/nit.md
new file mode 100644 (file)
index 0000000..26b2adb
--- /dev/null
@@ -0,0 +1,99 @@
+% NIT(1)
+
+# NAME
+
+Interprets and debugs Nit programs.
+
+# SYNOPSYS
+
+nit [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`--discover-call-trace`
+:   Trace calls of the first invocation of a method
+
+`-d`
+:   Launches the target program with the debugger attached to it
+
+`-c`
+:   Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned
+
+`--socket`
+:   Launches the target program with raw output on the network via sockets
+
+`--websocket`
+:   Launches the target program with output on the network via websockets
+
+`--port`
+:   Sets the debug port (Defaults to 22125) - Must be contained between 0 and 65535
+
+`-o`
+:   compatibility (does noting)
+
+`-m`
+:   Additionals module to min-in
+
+`-e`
+:   Specifies the program from command-line
+
+`-n`
+:   Repeatedly run the program for each line in file-name arguments
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitdoc.md b/share/man/nitdoc.md
new file mode 100644 (file)
index 0000000..d39c076
--- /dev/null
@@ -0,0 +1,114 @@
+% NITDOC(1)
+
+# NAME
+
+Generates HTML pages of API documentation from Nit source files.
+
+# SYNOPSYS
+
+nitdoc [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`-d`, `--dir`
+:   output directory
+
+`--source`
+:   link for source (%f for filename, %l for first line, %L for last line)
+
+`--sharedir`
+:   directory containing nitdoc assets
+
+`--shareurl`
+:   use shareurl instead of copy shared files
+
+`--no-dot`
+:   do not generate graphes with graphviz
+
+`--private`
+:   also generate private API
+
+`--custom-title`
+:   custom title for homepage
+
+`--custom-footer-text`
+:   custom footer text
+
+`--custom-overview-text`
+:   custom intro text for homepage
+
+`--custom-brand`
+:   custom link to external site
+
+`--github-upstream`
+:   Git branch where edited commits will be pulled into (ex: user:repo:branch)
+
+`--github-base-sha1`
+:   Git sha1 of base commit used to create pull request
+
+`--github-gitdir`
+:   Git working directory used to resolve path name (ex: /home/me/myproject/)
+
+`--piwik-tracker`
+:   Piwik tracker URL (ex: nitlanguage.org/piwik/)
+
+`--piwik-site-id`
+:   Piwik site ID
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitg.md b/share/man/nitg.md
new file mode 100644 (file)
index 0000000..1c45f1d
--- /dev/null
@@ -0,0 +1,177 @@
+% NITG(1)
+
+# NAME
+
+Compiles Nit programs.
+
+# SYNOPSYS
+
+nitg [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`-o`, `--output`
+:   Output file
+
+`--dir`
+:   Output directory
+
+`--no-cc`
+:   Do not invoke C compiler
+
+`--no-main`
+:   Do not generate main entry point
+
+`--make-flags`
+:   Additional options to make
+
+`--compile-dir`
+:   Directory used to generate temporary files
+
+`--hardening`
+:   Generate contracts in the C code against bugs in the compiler
+
+`--no-shortcut-range`
+:   Always insantiate a range and its iterator on 'for' loops
+
+`--no-check-covariance`
+:   Disable type tests of covariant parameters (dangerous)
+
+`--no-check-attr-isset`
+:   Disable isset tests before each attribute access (dangerous)
+
+`--no-check-assert`
+:   Disable the evaluation of explicit 'assert' and 'as' (dangerous)
+
+`--no-check-autocast`
+:   Disable implicit casts on unsafe expression usage (dangerous)
+
+`--no-check-null`
+:   Disable tests of null receiver (dangerous)
+
+`--no-check-all`
+:   Disable all tests (dangerous)
+
+`--typing-test-metrics`
+:   Enable static and dynamic count of all type tests
+
+`--invocation-metrics`
+:   Enable static and dynamic count of all method invocations
+
+`--isset-checks-metrics`
+:   Enable static and dynamic count of isset checks before attributes access
+
+`--stacktrace`
+:   Control the generation of stack traces
+
+`--no-gcc-directive`
+:   Disable a advanced gcc directives for optimization
+
+`--release`
+:   Compile in release mode and finalize application
+
+`--global`
+:   Use global compilation
+
+`--separate`
+:   Use separate compilation
+
+`--no-inline-intern`
+:   Do not inline call to intern methods
+
+`--no-union-attribute`
+:   Put primitive attibutes in a box instead of an union
+
+`--no-shortcut-equal`
+:   Always call == in a polymorphic way
+
+`--inline-coloring-numbers`
+:   Inline colors and ids (semi-global)
+
+`--inline-some-methods`
+:   Allow the separate compiler to inline some methods (semi-global)
+
+`--direct-call-monomorph`
+:   Allow the separate compiler to direct call monomorph sites (semi-global)
+
+`--skip-dead-methods`
+:   Do not compile dead methods (semi-global)
+
+`--semi-global`
+:   Enable all semi-global optimizations
+
+`--colo-dead-methods`
+:   Force colorization of dead methods
+
+`--tables-metrics`
+:   Enable static size measuring of tables used for vft, typing and resolution
+
+`--erasure`
+:   Erase generic types
+
+`--no-check-erasure-cast`
+:   Disable implicit casts on unsafe return with erasure-typing policy (dangerous)
+
+`--rta`
+:   Activate RTA (implicit with --global and --separate)
+
+`-m`
+:   Additionals module to min-in
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitlight.md b/share/man/nitlight.md
new file mode 100644 (file)
index 0000000..54b2896
--- /dev/null
@@ -0,0 +1,84 @@
+% NITLIGHT(1)
+
+# NAME
+
+Generates HTML of highlited code from Nit source files.
+
+# SYNOPSYS
+
+nitlight [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`-f`, `--fragment`
+:   Omit document header and footer
+
+`--first-line`
+:   Start the source file at this line (default: 1)
+
+`--last-line`
+:   End the source file at this line (default: to the end)
+
+`-d`, `--dir`
+:   Output html files in a specific directory (required if more than one module)
+
+`--full`
+:   Process also imported modules
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitls.md b/share/man/nitls.md
new file mode 100644 (file)
index 0000000..705a099
--- /dev/null
@@ -0,0 +1,93 @@
+% NITLS(1)
+
+# NAME
+
+Lists the projects and/or paths of Nit sources files.
+
+# SYNOPSYS
+
+nitls [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`-k`, `--keep`
+:   Ignore errors and files that are not a Nit source file
+
+`-r`, `--recursive`
+:   Process directories recussively
+
+`-t`, `--tree`
+:   List source files in their groups and projects
+
+`-s`, `--source`
+:   List source files
+
+`-P`, `--project`
+:   List projects paths (default)
+
+`-d`, `--depends`
+:   List dependencies of given modules
+
+`-p`, `--path`
+:   List only path (instead of name + path)
+
+`-M`
+:   List dependencies suitable for a rule in a Makefile. Alias for -d, -p and -s
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitmetrics.md b/share/man/nitmetrics.md
new file mode 100644 (file)
index 0000000..5aeb779
--- /dev/null
@@ -0,0 +1,120 @@
+% NITMETRICS(1)
+
+# NAME
+
+Computes various metrics on Nit programs.
+
+# SYNOPSYS
+
+nitmetrics [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`--all`
+:   Compute all metrics
+
+`--mmodules`
+:   Compute metrics about mmodules
+
+`--mclasses`
+:   Compute metrics about mclasses
+
+`--mendel`
+:   Compute mendel metrics
+
+`--inheritance`
+:   Compute metrics about inheritance usage
+
+`--refinement`
+:   Compute metrics about refinement usage
+
+`--self`
+:   Compute metrics about the usage of explicit and implicit self
+
+`--ast`
+:   Compute metrics about the usage of nodes and identifiers in the AST
+
+`--nullables`
+:   Compute metrics on nullables send
+
+`--static-types`
+:   Compute explicit static types metrics
+
+`--tables`
+:   Compute tables metrics
+
+`--rta`
+:   Compute RTA metrics
+
+`--csv`
+:   Export metrics in CSV format
+
+`--generate_hyperdoc`
+:   Generate Hyperdoc
+
+`--poset`
+:   Complete metrics on posets
+
+`-d`, `--dir`
+:   Directory where some statistics files are generated
+
+`--no-colors`
+:   Disable colors in console outputs
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitpick.md b/share/man/nitpick.md
new file mode 100644 (file)
index 0000000..f852d27
--- /dev/null
@@ -0,0 +1,69 @@
+% NITPICK(1)
+
+# NAME
+
+Collect potential style and code issues.
+
+# SYNOPSYS
+
+nitpick [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitpretty.md b/share/man/nitpretty.md
new file mode 100644 (file)
index 0000000..3238ced
--- /dev/null
@@ -0,0 +1,84 @@
+% NITPRETTY(1)
+
+# NAME
+
+Pretty print Nit code from Nit source files.
+
+# SYNOPSYS
+
+nitpretty [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`--dir`
+:   Working directory (default is '.nitpretty')
+
+`-o`, `--output`
+:   Output name (default is pretty.nit)
+
+`--diff`
+:   Show diff between source and output
+
+`--meld`
+:   Show diff between source and output using meld
+
+`--check`
+:   Check format of Nit source files
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitserial.md b/share/man/nitserial.md
new file mode 100644 (file)
index 0000000..056b692
--- /dev/null
@@ -0,0 +1,75 @@
+% NITSERIAL(1)
+
+# NAME
+
+Generates a serialization support module
+
+# SYNOPSYS
+
+nitserial [*options*]...
+
+# OPTIONS
+
+`-o`, `--output`
+:   Output file (can also be 'stdout')
+
+`--dir`
+:   Output directory
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitunit.md b/share/man/nitunit.md
new file mode 100644 (file)
index 0000000..24d2e1e
--- /dev/null
@@ -0,0 +1,99 @@
+% NITUNIT(1)
+
+# NAME
+
+Executes the unit tests from Nit source files.
+
+# SYNOPSYS
+
+nitunit [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+`--full`
+:   Process also imported modules
+
+`-o`, `--output`
+:   Output name (default is 'nitunit.xml')
+
+`--dir`
+:   Working directory (default is '.nitunit')
+
+`--no-act`
+:   Does not compile and run tests
+
+`-p`, `--pattern`
+:   Only run test case with name that match pattern. Examples: 'TestFoo', 'TestFoo*', 'TestFoo::test_foo', 'TestFoo::test_foo*', 'test_foo', 'test_foo*'
+
+`-t`, `--target-file`
+:   Specify test suite location.
+
+`--gen-suite`
+:   Generate test suite skeleton for a module
+
+`-f`, `--force`
+:   Force test generation even if file exists
+
+`--private`
+:   Also generate test case for private methods
+
+`--only-show`
+:   Only display skeleton, do not write file
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
diff --git a/share/man/nitx.md b/share/man/nitx.md
new file mode 100644 (file)
index 0000000..73cee66
--- /dev/null
@@ -0,0 +1,69 @@
+% NITX(1)
+
+# NAME
+
+Displays specific pieces of API information from Nit source files.
+
+# SYNOPSYS
+
+nitx [*options*]...
+
+# OPTIONS
+
+`-W`, `--warn`
+:   Show more warnings
+
+`-w`, `--warning`
+:   Show/hide a specific warning
+
+`-q`, `--quiet`
+:   Do not show warnings
+
+`--stop-on-first-error`
+:   Stop on first error
+
+`--no-color`
+:   Do not use color to display errors and warnings
+
+`--log`
+:   Generate various log files
+
+`--log-dir`
+:   Directory where to generate log files
+
+`-h`, `-?`, `--help`
+:   Show Help (This screen)
+
+`--version`
+:   Show version and exit
+
+`--set-dummy-tool`
+:   Set toolname and version to DUMMY. Useful for testing
+
+`-v`, `--verbose`
+:   Verbose
+
+`--bash-completion`
+:   Generate bash_completion file for this program
+
+`--stub-man`
+:   Generate a stub manpage in pandoc markdown format
+
+`--disable-phase`
+:   DEBUG: Disable a specific phase; use `list` to get the list.
+
+`-I`, `--path`
+:   Set include path for loaders (may be used more than once)
+
+`--only-parse`
+:   Only proceed to parse step of loaders
+
+`--only-metamodel`
+:   Stop after meta-model processing
+
+`--ignore-visibility`
+:   Do not check, and produce errors, on visibility issues.
+
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
index 8e17b4e..630c1c0 100644 (file)
@@ -53,6 +53,12 @@ class ASTBuilder
                return new ABlockExpr.make
        end
 
+       # Make a new, empty, loop of statements
+       fun make_loop: ALoopExpr
+       do
+               return new ALoopExpr.make
+       end
+
        # Make a new variable read
        fun make_var_read(variable: Variable, mtype: MType): AVarExpr
        do
@@ -84,6 +90,12 @@ class ASTBuilder
                return new ADoExpr.make
        end
 
+       # Make a new break for a given escapemark
+       fun make_break(escapemark: EscapeMark): ABreakExpr
+       do
+               return new ABreakExpr.make(escapemark)
+       end
+
        # Make a new condinionnal
        # `mtype` is the return type of the whole if, in case of a ternary operator.
        fun make_if(condition: AExpr, mtype: nullable MType): AIfExpr
@@ -142,6 +154,7 @@ redef class AExpr
        # Note: this method, aimed to `ABlockExpr` is promoted to `AExpr` because of the limitations of the hierarchies generated by sablecc3
        fun add(expr: AExpr)
        do
+               print "add not implemented in {inspect}"
                abort
        end
 end
@@ -172,29 +185,54 @@ redef class ABlockExpr
        end
 end
 
+redef class ALoopExpr
+       private init make
+       do
+               _n_kwloop = new TKwloop
+               self.is_typed = true
+               n_block = new ABlockExpr
+               n_block.is_typed = true
+       end
+
+       redef fun add(expr: AExpr)
+       do
+               n_block.add expr
+       end
+end
+
 redef class ADoExpr
        private init make
        do
                _n_kwdo = new TKwdo
-               escapemark = new EscapeMark(null, false)
+               self.is_typed = true
+               n_block = new ABlockExpr
+               n_block.is_typed = true
        end
 
        # Make a new break expression of the given do
        fun make_break: ABreakExpr
        do
-               var escapemark = self.escapemark
+               var escapemark = self.break_mark
                if escapemark == null then
-                       escapemark = new EscapeMark(null, false)
-                       self.escapemark = escapemark
+                       escapemark = new EscapeMark(null)
+                       self.break_mark = escapemark
                end
                return new ABreakExpr.make(escapemark)
        end
+
+       redef fun add(expr: AExpr)
+       do
+               n_block.add expr
+       end
 end
 
 redef class ABreakExpr
        private init make(escapemark: EscapeMark)
        do
+               _n_kwbreak = new TKwbreak
                self.escapemark = escapemark
+               escapemark.escapes.add self
+               self.is_typed = true
        end
 end
 
@@ -251,7 +289,6 @@ redef class ACallExpr
                if args != null then
                        self.n_args.n_exprs.add_all(args)
                end
-               var mtype = recv.mtype.as(not null)
                self.callsite = callsite
                self.mtype = callsite.msignature.return_mtype
                self.is_typed = true
index c98d54e..85222a9 100644 (file)
@@ -22,47 +22,46 @@ import semantize
 import platform
 import c_tools
 private import annotation
+import mixin
 
 # Add compiling options
 redef class ToolContext
        # --output
-       var opt_output: OptionString = new OptionString("Output file", "-o", "--output")
+       var opt_output = new OptionString("Output file", "-o", "--output")
        # --dir
-       var opt_dir: OptionString = new OptionString("Output directory", "--dir")
+       var opt_dir = new OptionString("Output directory", "--dir")
        # --no-cc
-       var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc")
+       var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc")
        # --no-main
-       var opt_no_main: OptionBool = new OptionBool("Do not generate main entry point", "--no-main")
+       var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main")
        # --cc-paths
-       var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
+       var opt_cc_path = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path")
        # --make-flags
-       var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags")
+       var opt_make_flags = new OptionString("Additional options to make", "--make-flags")
        # --compile-dir
-       var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir")
+       var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir")
        # --hardening
-       var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
-       # --no-shortcut-range
-       var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
+       var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening")
        # --no-check-covariance
-       var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
+       var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance")
        # --no-check-attr-isset
-       var opt_no_check_attr_isset: OptionBool = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
+       var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset")
        # --no-check-assert
-       var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
+       var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert")
        # --no-check-autocast
-       var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
+       var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast")
        # --no-check-null
-       var opt_no_check_null: OptionBool = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
+       var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null")
        # --no-check-all
-       var opt_no_check_all: OptionBool = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
+       var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all")
        # --typing-test-metrics
-       var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
+       var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
        # --invocation-metrics
-       var opt_invocation_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
+       var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
        # --isset-checks-metrics
-       var opt_isset_checks_metrics: OptionBool = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
+       var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
        # --stacktrace
-       var opt_stacktrace: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace")
+       var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
        # --no-gcc-directives
        var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
        # --release
@@ -71,7 +70,7 @@ redef class ToolContext
        redef init
        do
                super
-               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range)
+               self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
                self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
                self.option_context.add_option(self.opt_stacktrace)
@@ -181,7 +180,6 @@ class MakefileToolchain
        do
                gather_cc_paths
 
-               var mainmodule = compiler.mainmodule
                var compile_dir = compile_dir
 
                # Generate the .h and .c files
@@ -229,7 +227,6 @@ class MakefileToolchain
                compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h"
 
                # FFI
-               var m2m = toolcontext.modelbuilder.mmodule2nmodule
                for m in compiler.mainmodule.in_importation.greaters do
                        compiler.finalize_ffi_for_module(m)
                end
@@ -310,7 +307,16 @@ class MakefileToolchain
 
        fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk"
 
-       fun default_outname(mainmodule: MModule): String do return mainmodule.name
+       fun default_outname(mainmodule: MModule): String
+       do
+               # Search a non fictive module
+               var res = mainmodule.name
+               while mainmodule.is_fictive do
+                       mainmodule = mainmodule.in_importation.direct_greaters.first
+                       res = mainmodule.name
+               end
+               return res
+       end
 
        # Combine options and platform informations to get the final path of the outfile
        fun outfile(mainmodule: MModule): String
@@ -342,7 +348,6 @@ class MakefileToolchain
                end
 
                var linker_options = new HashSet[String]
-               var m2m = toolcontext.modelbuilder.mmodule2nmodule
                for m in mainmodule.in_importation.greaters do
                        var libs = m.collect_linker_libs
                        if libs != null then linker_options.add_all(libs)
@@ -450,7 +455,7 @@ abstract class AbstractCompiler
        # The real main module of the program
        var realmainmodule: MModule
 
-       # The modeulbuilder used to know the model and the AST
+       # The modelbuilder used to know the model and the AST
        var modelbuilder: ModelBuilder is protected writable
 
        # Is hardening asked? (see --hardening)
@@ -474,7 +479,7 @@ abstract class AbstractCompiler
 
        # The list of all associated files
        # Used to generate .c files
-       var files: List[CodeFile] = new List[CodeFile]
+       var files = new List[CodeFile]
 
        # Initialize a visitor specific for a compiler engine
        fun new_visitor: VISITOR is abstract
@@ -541,8 +546,6 @@ abstract class AbstractCompiler
        # Compile C headers
        # This method call compile_header_strucs method that has to be refined
        fun compile_header do
-               var v = self.header
-               var toolctx = modelbuilder.toolcontext
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
@@ -891,6 +894,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@ -906,6 +910,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@ -1044,7 +1049,7 @@ abstract class AbstractCompilerVisitor
        fun get_property(name: String, recv: MType): MMethod
        do
                assert recv isa MClassType
-               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule)
+               return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule)
        end
 
        fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
@@ -1081,39 +1086,45 @@ abstract class AbstractCompilerVisitor
 
        fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
 
-       # Transform varargs, in raw arguments, into a single argument of type `Array`
-       # Note: this method modify the given `args`
-       # If there is no vararg, then `args` is not modified.
-       fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable])
+       # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
+       # This method is used to manage varargs in signatures and returns the real array
+       # of runtime variables to use in the call.
+       fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
        do
-               var recv = args.first
-               var vararg_rank = msignature.vararg_rank
-               if vararg_rank >= 0 then
-                       assert args.length >= msignature.arity + 1 # because of self
-                       var rawargs = args
-                       args = new Array[RuntimeVariable]
-
-                       args.add(rawargs.first) # recv
-
-                       for i in [0..vararg_rank[ do
-                               args.add(rawargs[i+1])
-                       end
+               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               var res = new Array[RuntimeVariable]
+               res.add(recv)
 
-                       var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity
-                       var vararg = new Array[RuntimeVariable]
-                       for i in [vararg_rank..vararg_lastrank] do
-                               vararg.add(rawargs[i+1])
-                       end
+               if args.is_empty then return res
 
-                       var elttype = msignature.mparameters[vararg_rank].mtype
-                       args.add(self.vararg_instance(mpropdef, recv, vararg, elttype))
+               var vararg_rank = msignature.vararg_rank
+               var vararg_len = args.length - msignature.arity
+               if vararg_len < 0 then vararg_len = 0
 
-                       for i in [vararg_lastrank+1..rawargs.length-1[ do
-                               args.add(rawargs[i+1])
+               for i in [0..msignature.arity[ do
+                       if i == vararg_rank then
+                               var ne = args[i]
+                               if ne isa AVarargExpr then
+                                       var e = self.expr(ne.n_expr, null)
+                                       res.add(e)
+                                       continue
+                               end
+                               var vararg = new Array[RuntimeVariable]
+                               for j in [vararg_rank..vararg_rank+vararg_len] do
+                                       var e = self.expr(args[j], null)
+                                       vararg.add(e)
+                               end
+                               var elttype = msignature.mparameters[vararg_rank].mtype
+                               var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
+                               res.add(arg)
+                       else
+                               var j = i
+                               if i > vararg_rank then j += vararg_len
+                               var e = self.expr(args[j], null)
+                               res.add(e)
                        end
-                       rawargs.clear
-                       rawargs.add_all(args)
                end
+               return res
        end
 
        # Type handling
@@ -1217,7 +1228,7 @@ abstract class AbstractCompilerVisitor
 
        # Checks
 
-       # Add a check and an abort for a null reciever if needed
+       # Add a check and an abort for a null receiver if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
                if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
@@ -1232,7 +1243,7 @@ abstract class AbstractCompilerVisitor
 
        # Names handling
 
-       private var names: HashSet[String] = new HashSet[String]
+       private var names = new HashSet[String]
        private var last: Int = 0
 
        # Return a new name based on `s` and unique in the visitor
@@ -1266,6 +1277,14 @@ abstract class AbstractCompilerVisitor
                return name
        end
 
+       # Insert a C label for associated with an escapemark
+       fun add_escape_label(e: nullable EscapeMark)
+       do
+               if e == null then return
+               if e.escapes.is_empty then return
+               add("BREAK_{escapemark_name(e)}: (void)0;")
+       end
+
        private var escapemark_names = new HashMap[EscapeMark, String]
 
        # Return a "const char*" variable associated to the classname of the dynamic type of an object
@@ -1274,7 +1293,7 @@ abstract class AbstractCompilerVisitor
 
        # Variables handling
 
-       protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable]
+       protected var variables = new HashMap[Variable, RuntimeVariable]
 
        # Return the local runtime_variable associated to a Nit local variable
        fun variable(variable: Variable): RuntimeVariable
@@ -1353,6 +1372,18 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       # Generate an integer value
+       fun bool_instance(value: Bool): RuntimeVariable
+       do
+               var res = self.new_var(self.get_class("Bool").mclass_type)
+               if value then
+                       self.add("{res} = 1;")
+               else
+                       self.add("{res} = 0;")
+               end
+               return res
+       end
+
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
@@ -1373,6 +1404,19 @@ abstract class AbstractCompilerVisitor
                return res
        end
 
+       fun value_instance(object: Object): RuntimeVariable
+       do
+               if object isa Int then
+                       return int_instance(object)
+               else if object isa Bool then
+                       return bool_instance(object)
+               else if object isa String then
+                       return string_instance(object)
+               else
+                       abort
+               end
+       end
+
        # Generate an array value
        fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract
 
@@ -1813,6 +1857,7 @@ redef class MMethodDef
        fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var modelbuilder = v.compiler.modelbuilder
+               var val = constant_value
                if modelbuilder.mpropdef2npropdef.has_key(self) then
                        var npropdef = modelbuilder.mpropdef2npropdef[self]
                        var oldnode = v.current_node
@@ -1827,6 +1872,8 @@ redef class MMethodDef
                        self.compile_parameter_check(v, arguments)
                        nclassdef.compile_to_c(v, self, arguments)
                        v.current_node = oldnode
+               else if val != null then
+                       v.ret(v.value_instance(val))
                else
                        abort
                end
@@ -2356,8 +2403,7 @@ redef class AExpr
        # Do not call this method directly, use `v.stmt` instead
        private fun stmt(v: AbstractCompilerVisitor)
        do
-               var res = expr(v)
-               if res != null then v.add("{res};")
+               expr(v)
        end
 end
 
@@ -2399,12 +2445,6 @@ redef class AVarExpr
 end
 
 redef class AVarAssignExpr
-       redef fun stmt(v)
-       do
-               var variable = self.variable.as(not null)
-               var i = v.expr(self.n_value, variable.declared_type)
-               v.assign(v.variable(variable), i)
-       end
        redef fun expr(v)
        do
                var variable = self.variable.as(not null)
@@ -2430,11 +2470,7 @@ redef class ASelfExpr
        redef fun expr(v) do return v.frame.arguments.first
 end
 
-redef class AContinueExpr
-       redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};")
-end
-
-redef class ABreakExpr
+redef class AEscapeExpr
        redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
 end
 
@@ -2497,10 +2533,7 @@ redef class ADoExpr
        redef fun stmt(v)
        do
                v.stmt(self.n_block)
-               var escapemark = self.escapemark
-               if escapemark != null then
-                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-               end
+               v.add_escape_label(break_mark)
        end
 end
 
@@ -2511,9 +2544,9 @@ redef class AWhileExpr
                var cond = v.expr_bool(self.n_expr)
                v.add("if (!{cond}) break;")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
        end
 end
 
@@ -2522,47 +2555,15 @@ redef class ALoopExpr
        do
                v.add("for(;;) \{")
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
        end
 end
 
 redef class AForExpr
        redef fun stmt(v)
        do
-               # Shortcut on explicit range
-               # Avoid the instantiation of the range and the iterator
-               var nexpr = self.n_expr
-               if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then
-                       var from = v.expr(nexpr.n_expr, null)
-                       var to = v.expr(nexpr.n_expr2, null)
-                       var variable = v.variable(variables.first)
-                       var one = v.new_expr("1", v.get_class("Int").mclass_type)
-
-                       v.assign(variable, from)
-                       v.add("for(;;) \{ /* shortcut range */")
-
-                       var ok
-                       if nexpr isa AOrangeExpr then
-                               ok = v.send(v.get_property("<", variable.mtype), [variable, to])
-                       else
-                               ok = v.send(v.get_property("<=", variable.mtype), [variable, to])
-                       end
-                       assert ok != null
-                       v.add("if(!{ok}) break;")
-
-                       v.stmt(self.n_block)
-
-                       v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
-                       var succ = v.send(v.get_property("successor", variable.mtype), [variable, one])
-                       assert succ != null
-                       v.assign(variable, succ)
-                       v.add("\}")
-                       v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
-                       return
-               end
-
                var cl = v.expr(self.n_expr, null)
                var it_meth = self.method_iterator
                assert it_meth != null
@@ -2595,12 +2596,18 @@ redef class AForExpr
                        abort
                end
                v.stmt(self.n_block)
-               v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(continue_mark)
                var next_meth = self.method_next
                assert next_meth != null
                v.compile_callsite(next_meth, [it])
                v.add("\}")
-               v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;")
+               v.add_escape_label(break_mark)
+
+               var method_finish = self.method_finish
+               if method_finish != null then
+                       # TODO: Find a way to call this also in long escape (e.g. return)
+                       v.compile_callsite(method_finish, [it])
+               end
        end
 end
 
@@ -2741,7 +2748,7 @@ redef class ACrangeExpr
                var i2 = v.expr(self.n_expr2, null)
                var mtype = self.mtype.as(MClassType)
                var res = v.init_instance(mtype)
-               var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
+               v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
                return res
        end
 end
@@ -2753,7 +2760,7 @@ redef class AOrangeExpr
                var i2 = v.expr(self.n_expr2, null)
                var mtype = self.mtype.as(MClassType)
                var res = v.init_instance(mtype)
-               var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
+               v.compile_callsite(init_callsite.as(not null), [res, i1, i2])
                return res
        end
 end
@@ -2833,11 +2840,9 @@ redef class ASendExpr
        redef fun expr(v)
        do
                var recv = v.expr(self.n_expr, null)
-               var args = [recv]
-               for a in self.raw_arguments do
-                       args.add(v.expr(a, null))
-               end
-               return v.compile_callsite(self.callsite.as(not null), args)
+               var callsite = self.callsite.as(not null)
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               return v.compile_callsite(callsite, args)
        end
 end
 
@@ -2845,13 +2850,12 @@ redef class ASendReassignFormExpr
        redef fun stmt(v)
        do
                var recv = v.expr(self.n_expr, null)
-               var args = [recv]
-               for a in self.raw_arguments do
-                       args.add(v.expr(a, null))
-               end
+               var callsite = self.callsite.as(not null)
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+
                var value = v.expr(self.n_value, null)
 
-               var left = v.compile_callsite(self.callsite.as(not null), args)
+               var left = v.compile_callsite(callsite, args)
                assert left != null
 
                var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value])
@@ -2866,14 +2870,12 @@ redef class ASuperExpr
        redef fun expr(v)
        do
                var recv = v.frame.arguments.first
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       args.add(v.expr(a, null))
-               end
 
                var callsite = self.callsite
                if callsite != null then
-                       # Add additionnals arguments for the super init call
+                       var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+
+                       # Add additional arguments for the super init call
                        if args.length == 1 then
                                for i in [0..callsite.msignature.arity[ do
                                        args.add(v.frame.arguments[i+1])
@@ -2884,12 +2886,14 @@ redef class ASuperExpr
                        return res
                end
 
+               var mpropdef = self.mpropdef.as(not null)
+               var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
                if args.length == 1 then
                        args = v.frame.arguments
                end
 
                # stantard call-next-method
-               return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args)
+               return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
        end
 end
 
@@ -2912,11 +2916,10 @@ redef class ANewExpr
                else
                        recv = v.new_expr("({ctype})0/*special!*/", mtype)
                end
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       args.add(v.expr(a, null))
-               end
-               var res2 = v.compile_callsite(self.callsite.as(not null), args)
+
+               var callsite = self.callsite.as(not null)
+               var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+               var res2 = v.compile_callsite(callsite, args)
                if res2 != null then
                        #self.debug("got {res2} from {mproperty}. drop {recv}")
                        return res2
@@ -2935,12 +2938,13 @@ redef class AAttrExpr
 end
 
 redef class AAttrAssignExpr
-       redef fun stmt(v)
+       redef fun expr(v)
        do
                var recv = v.expr(self.n_expr, null)
                var i = v.expr(self.n_value, null)
                var mproperty = self.mproperty.as(not null)
                v.write_attribute(mproperty, recv, i)
+               return i
        end
 end
 
@@ -3019,9 +3023,6 @@ end
 # Create a tool context to handle options and paths
 var toolcontext = new ToolContext
 
-var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
-toolcontext.option_context.add_option(opt_mixins)
-
 toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs."
 
 # We do not add other options, so process them now!
@@ -3040,7 +3041,6 @@ end
 
 # Here we load an process all modules passed on the command line
 var mmodules = modelbuilder.parse(arguments)
-var mixins = modelbuilder.parse(opt_mixins.value)
 
 if mmodules.is_empty then return
 modelbuilder.run_phases
@@ -3048,8 +3048,5 @@ modelbuilder.run_phases
 for mmodule in mmodules do
        toolcontext.info("*** PROCESS {mmodule} ***", 1)
        var ms = [mmodule]
-       if not mixins.is_empty then
-               ms.add_all mixins
-       end
        toolcontext.run_global_phases(ms)
 end
index a6fc4e2..cc24d6e 100644 (file)
@@ -125,11 +125,9 @@ redef class AAnnotation
        # revision number. If the working tree is dirty, it will append another field with "d" for dirty.
        private fun as_version(modelbuilder: ModelBuilder): String
        do
-               var annotation_name = n_atid.n_id.text
                var version_fields = new Array[Object]
 
                var args = n_args
-               var platform_name
                if args.length < 1 then
                        modelbuilder.error(self, "Annotation error: \"{name}\" expects at least a single argument.")
                        return ""
index ed9b466..e447080 100644 (file)
@@ -279,8 +279,6 @@ class BucketsColorer[H: Object, E: Object]
        private var colors = new HashMap[E, Int]
        private var conflicts = new HashMap[E, Set[E]]
 
-       init do end
-
        # Start bucket coloring
        fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do
                compute_conflicts(buckets)
index 37689d8..e9c740a 100644 (file)
@@ -82,7 +82,7 @@ extern void nitni_global_ref_decr(void*);
                return res
        end
 
-       private var compiled_callbacks: Array[NitniCallback] = new Array[NitniCallback]
+       private var compiled_callbacks = new Array[NitniCallback]
 
        # Returns true if callbacks has yet to be generated and register it as being generated
        private fun check_callback_compilation(cb: NitniCallback): Bool
@@ -98,7 +98,6 @@ redef class AMethPropdef
        do
                var mmodule = mpropdef.mclassdef.mmodule
                var mainmodule = v.compiler.mainmodule
-               var amainmodule = v.compiler.modelbuilder.mmodule2nmodule[mainmodule]
                var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule]
                var mclass_type = mpropdef.mclassdef.bound_mtype
 
@@ -428,7 +427,6 @@ redef class MExplicitSuper
                var mproperty = from.mproperty
                assert mproperty isa MMethod
                var mclass_type = from.mclassdef.mclass.mclass_type
-               var mmodule = from.mclassdef.mmodule
 
                # In nitni files, declare internal function as extern
                var internal_csignature = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, internal_call_context)
index 2196071..7652368 100644 (file)
@@ -342,7 +342,7 @@ class GlobalCompilerVisitor
 
                var valtype = value.mtype.as(MClassType)
                var res = self.new_var(mtype)
-               if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
+               if not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
                        self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
                        self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);")
                        return res
@@ -520,45 +520,21 @@ class GlobalCompilerVisitor
                return recvtype
        end
 
-       # Subpart of old call function
-       # Gets the receiver boxed and casted if necessary
-       private fun get_recv(recvtype: MClassType, args: Array[RuntimeVariable]): RuntimeVariable
+       redef fun call(m, recvtype, args)
        do
-               return self.autoadapt(self.autobox(args.first, recvtype), recvtype)
-       end
+               var recv_type = get_recvtype(m, recvtype, args)
+               var recv = self.autoadapt(self.autobox(args.first, recvtype), recvtype)
+               if m.is_extern then recv = unbox_extern(recv, recv_type)
+
+               args = args.to_a
+               args.first = recv
 
-       # Finalizes a call to a method ´m´ on type ´recvtype´ with arguments ´args´
-       private fun finalize_call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
-       do
                assert args.length == m.msignature.arity + 1 else debug("Invalid arity for {m}. {args.length} arguments given.")
 
                var rm = new CustomizedRuntimeFunction(m, recvtype)
                return rm.call(self, args)
        end
 
-       redef fun call(m, recvtype, args)
-       do
-               var recv_type = get_recvtype(m, recvtype, args)
-               var recv = get_recv(recv_type, args)
-               if m.is_extern then recv = unbox_extern(recv, recv_type)
-               var new_args = args.to_a
-               self.varargize(m, m.msignature.as(not null), new_args)
-               new_args.first = recv
-               return finalize_call(m, recv_type, new_args)
-       end
-
-       # Does a call without encapsulating varargs into an array
-       # Avoids multiple encapsulation when calling a super in a variadic function
-       fun call_without_varargize(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
-       do
-               var recv_type = get_recvtype(m, recvtype, args)
-               var recv = get_recv(recv_type, args)
-               if m.is_extern then recv = unbox_extern(recv, recv_type)
-               var new_args = args.to_a
-               new_args.first = recv
-               return finalize_call(m, recv_type, new_args)
-       end
-
        redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable
        do
                var types = self.collect_types(args.first)
@@ -580,7 +556,7 @@ class GlobalCompilerVisitor
                                return res
                        end
                        var propdef = m.lookup_next_definition(self.compiler.mainmodule, mclasstype)
-                       var res2 = self.call_without_varargize(propdef, mclasstype, args)
+                       var res2 = self.call(propdef, mclasstype, args)
                        if res != null then self.assign(res, res2.as(not null))
                        return res
                end
@@ -602,7 +578,7 @@ class GlobalCompilerVisitor
                        else
                                self.add("case {self.compiler.classid(t)}: /* test {t} */")
                        end
-                       var res2 = self.call_without_varargize(propdef, t, args)
+                       var res2 = self.call(propdef, t, args)
                        if res != null then self.assign(res, res2.as(not null))
                        self.add "break;"
                end
index 8c400f9..3645f54 100644 (file)
@@ -22,19 +22,19 @@ import rapid_type_analysis
 # Add separate compiler specific options
 redef class ToolContext
        # --separate
-       var opt_separate: OptionBool = new OptionBool("Use separate compilation", "--separate")
+       var opt_separate = new OptionBool("Use separate compilation", "--separate")
        # --no-inline-intern
-       var opt_no_inline_intern: OptionBool = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
+       var opt_no_inline_intern = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
        # --no-union-attribute
-       var opt_no_union_attribute: OptionBool = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
+       var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
        # --no-shortcut-equate
-       var opt_no_shortcut_equate: OptionBool = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
+       var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
        # --inline-coloring-numbers
-       var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
+       var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
        # --inline-some-methods
-       var opt_inline_some_methods: OptionBool = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
+       var opt_inline_some_methods = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods")
        # --direct-call-monomorph
-       var opt_direct_call_monomorph: OptionBool = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
+       var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph")
        # --skip-dead-methods
        var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods")
        # --semi-global
@@ -42,7 +42,7 @@ redef class ToolContext
        # --no-colo-dead-methods
        var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods")
        # --tables-metrics
-       var opt_tables_metrics: OptionBool = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
+       var opt_tables_metrics = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics")
 
        redef init
        do
@@ -963,7 +963,6 @@ class SeparateCompilerVisitor
        redef fun unbox_signature_extern(m, args)
        do
                var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true)
-               var recv = args.first
                if not m.mproperty.is_init and m.is_extern then
                        args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type)
                end
@@ -1063,14 +1062,12 @@ class SeparateCompilerVisitor
        redef fun compile_callsite(callsite, args)
        do
                var rta = compiler.runtime_type_analysis
-               var recv = args.first.mtype
                var mmethod = callsite.mproperty
                # TODO: Inlining of new-style constructors
                if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and not mmethod.is_root_init then
                        var tgs = rta.live_targets(callsite)
                        if tgs.length == 1 then
                                # DIRECT CALL
-                               self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
                                var res0 = before_send(mmethod, args)
                                var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
                                if res0 != null then
@@ -1086,8 +1083,6 @@ class SeparateCompilerVisitor
        end
        redef fun send(mmethod, arguments)
        do
-               self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
-
                if arguments.first.mcasttype.ctype != "val*" then
                        # In order to shortcut the primitive, we need to find the most specific method
                        # Howverr, because of performance (no flattening), we always work on the realmainmodule
index af3a43c..c23476d 100644 (file)
@@ -20,11 +20,11 @@ intrude import separate_compiler
 # Add separate erased compiler specific options
 redef class ToolContext
        # --erasure
-       var opt_erasure: OptionBool = new OptionBool("Erase generic types", "--erasure")
+       var opt_erasure = new OptionBool("Erase generic types", "--erasure")
        # --rta
        var opt_rta = new OptionBool("Activate RTA (implicit with --global and --separate)", "--rta")
        # --no-check-erasure-cast
-       var opt_no_check_erasure_cast: OptionBool = new OptionBool("Disable implicit casts on unsafe return with erasure-typing policy (dangerous)", "--no-check-erasure-cast")
+       var opt_no_check_erasure_cast = new OptionBool("Disable implicit casts on unsafe return with erasure-typing policy (dangerous)", "--no-check-erasure-cast")
 
        redef init
        do
@@ -549,7 +549,6 @@ class SeparateErasureCompilerVisitor
                end
 
                var class_ptr
-               var type_table
                if value.mtype.ctype == "val*" then
                        class_ptr = "{value}->class->"
                else
index f13d69f..41386d2 100644 (file)
@@ -190,12 +190,8 @@ redef class MGroup
 
        redef fun tpl_namespace do
                var tpl = new Template
-               if mproject != null then
-                       tpl.add mproject.tpl_namespace
-               else if parent != null then
-                       tpl.add parent.tpl_namespace
-               end
-               if mproject != null and mproject.root != self then
+               tpl.add mproject.tpl_namespace
+               if mproject.root != self then
                        tpl.add "::"
                        tpl.add tpl_link
                end
index 200e650..516b17e 100644 (file)
@@ -240,7 +240,9 @@ end
 
 # Comparator used to sort boxes by order
 private class OrderComparator
-       super Comparator[TplSidebarElt]
+       super Comparator
+
+       redef type COMPARED: TplSidebarElt
 
        redef fun compare(a, b) do
                if a.order < b.order then return -1
index 7be54ce..49a880b 100644 (file)
@@ -96,7 +96,6 @@ class JavaLanguage
 
                var jni_signature_alt
                var return_type
-               var c_return_type
                var params = new Array[String]
                params.add "nit_ffi_jni_env"
                params.add "java_class"
@@ -105,19 +104,16 @@ class JavaLanguage
                if mproperty.is_init then
                        jni_signature_alt = mclass_type.jni_signature_alt
                        return_type = mclass_type
-                       c_return_type = mclass_type.cname
                else
                        params.add "recv"
                        if signature.return_mtype != null then
                                var ret_mtype = signature.return_mtype
                                ret_mtype = ret_mtype.resolve_for(mclass_type, mclass_type, mmodule, true)
                                return_type = signature.return_mtype
-                               c_return_type = mclass_type.cname
                                jni_signature_alt = return_type.jni_signature_alt
                        else
                                jni_signature_alt = "Void"
                                return_type = null
-                               c_return_type = null
                        end
                end
 
@@ -262,16 +258,15 @@ redef class AMethPropdef
                end
        end
 
-       # Insert additionnal explicit calls to get the current `JNIEnv`
+       # Insert additional explicit calls to get the current `JNIEnv`
        #
        # This forces declaration of callbacks to Nit. The callbacks will be available in Java
        # but will be used mainly by the FFI itself.
        #
-       # The developper can aso customize the JNIEnv used by the FFI by redefing `Sys::jni_env`.
+       # The developer can also customize the JNIEnv used by the FFI by redefining `Sys::jni_env`.
        private fun insert_artificial_callbacks(toolcontext: ToolContext)
        do
                var fcc = foreign_callbacks
-               assert fcc != null
 
                var modelbuilder = toolcontext.modelbuilder
                var mmodule = mpropdef.mclassdef.mmodule
@@ -450,7 +445,7 @@ redef class MType
        # Type name in Java
        #
        # * Primitives common to both languages use their Java primitive type
-       # * Nit extern Java classes are reprensented by their full Java type
+       # * Nit extern Java classes are represented by their full Java type
        # * Other Nit objects are represented by `int` in Java. It holds the
        #       pointer to the underlying C structure.
        #       TODO create static Java types to store and hide the pointer
@@ -502,7 +497,6 @@ redef class MClassType
        do
                var ftype = mclass.ftype
                if ftype isa ForeignJavaType then
-                       var ori_jni_type = jni_type
                        var jni_type = ftype.java_type.
                                replace('.', "/").replace(' ', "").replace('\n', "")
 
index a50b45b..9e3775d 100644 (file)
@@ -117,17 +117,7 @@ redef class AReturnExpr
        end
 end
 
-redef class AContinueExpr
-       redef fun after_simple_misc(v)
-       do
-               var e = n_expr
-               if e != null then
-                       e.warn_parentheses(v)
-               end
-       end
-end
-
-redef class ABreakExpr
+redef class AEscapeExpr
        redef fun after_simple_misc(v)
        do
                var e = n_expr
index 06075b4..fe5375c 100644 (file)
@@ -52,7 +52,6 @@ class HighlightVisitor
        do
                var stack2 = new Array[HTMLTag]
                var stack = new Array[Prod]
-               var closes = new Array[Prod]
                var line = 0
                var c: nullable Token = first_token
                var hv = new HighlightVisitor
@@ -932,4 +931,3 @@ redef class AExpr
                return t.infobox(v)
        end
 end
-
diff --git a/src/interpreter/breakpoint.nit b/src/interpreter/breakpoint.nit
deleted file mode 100644 (file)
index ef04a3b..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2013 Lucas Bajolet <lucas.bajolet@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.
-
-# Classes and methods relative to the management of Breakpoints for the Debugger
-module breakpoint
-
-# Contains all the informations of a Breakpoint for the Debugger
-class Breakpoint
-
-       # Line to break on
-       var line: Int
-
-       # File concerned by the breakpoint
-       var file: String
-
-       # Maximum times to break on self
-       var max_breaks: Int
-
-       init(line: Int, file: String)
-       do
-               self.line = line
-               self.file = file
-               self.max_breaks = -1
-       end
-
-       fun set_max_breaks(breaks: Int)
-       do
-               self.max_breaks = breaks
-       end
-
-       # When the breakpoint is encountered, the check-in function should be called
-       fun check_in
-       do
-               if self.max_breaks > 0 then self.max_breaks -= 1
-       end
-
-       # Checks if the breakpoint is still valid (that is, if it has a remaining breaks number > 0 or == -1)
-       fun is_valid: Bool
-       do
-               if max_breaks == 0 then
-                       return false
-               else
-                       return true
-               end
-       end
-
-end
index 586c04c..771d4a2 100644 (file)
@@ -17,7 +17,6 @@
 # Debugging of a nit program using the NaiveInterpreter
 module debugger
 
-import breakpoint
 intrude import naive_interpreter
 import nitx
 intrude import semantize::local_var_init
@@ -120,9 +119,9 @@ redef class ToolContext
        end
 
        # -d
-       var opt_debugger_mode: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d")
+       var opt_debugger_mode = new OptionBool("Launches the target program with the debugger attached to it", "-d")
        # -c
-       var opt_debugger_autorun: OptionBool = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c")
+       var opt_debugger_autorun = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c")
 
        redef init
        do
@@ -167,6 +166,20 @@ redef class ModelBuilder
        end
 end
 
+# Contains all the informations of a Breakpoint for the Debugger
+class Breakpoint
+
+       # Line to break on
+       var line: Int
+
+       # File concerned by the breakpoint
+       var file: String
+
+       redef init do
+               if not file.has_suffix(".nit") then file += ".nit"
+       end
+end
+
 # The class extending `NaiveInterpreter` by adding debugging methods
 class Debugger
        super NaiveInterpreter
@@ -257,19 +270,8 @@ class Debugger
        end
 
        # Same as a regular call but for a runtime injected module
-       #
        fun rt_call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
        do
-               args = call_commons(mpropdef, args)
-               return rt_call_without_varargs(mpropdef, args)
-       end
-
-       # Common code to call and this function
-       #
-       # Call only executes the variadic part, this avoids
-       # double encapsulation of variadic parameters into an Array
-       fun rt_call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
-       do
                if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then
                        self.discover_call_trace.add mpropdef
                        self.debug("Discovered {mpropdef}")
@@ -324,7 +326,6 @@ class Debugger
                        var mmod = e.mmodule
                        if mmod != null then
                                self.mainmodule = mmod
-                               var local_classdefs = mmod.mclassdefs
                                var sys_type = mmod.sys_type
                                if sys_type == null then
                                        print "Fatal error, cannot find Class Sys !\nAborting"
@@ -379,14 +380,6 @@ class Debugger
                var breakpoint = find_breakpoint(curr_file, n.location.line_start)
 
                if breakpoints.keys.has(curr_file) and breakpoint != null then
-
-                       breakpoint.check_in
-
-                       if not breakpoint.is_valid
-                       then
-                               remove_breakpoint(curr_file, n.location.line_start)
-                       end
-
                        n.debug("Execute stmt {n.to_s}")
                        while read_cmd do end
                end
@@ -399,7 +392,6 @@ class Debugger
                var identifiers_in_instruction = get_identifiers_in_current_instruction(n.location.text)
 
                for i in identifiers_in_instruction do
-                       var variable = seek_variable(i, frame)
                        for j in self.traces do
                                if j.is_variable_traced_in_frame(i, frame) then
                                        n.debug("Traced variable {i} used")
@@ -465,7 +457,7 @@ class Debugger
        #
        # Returns a boolean value, representing whether or not to
        # continue reading commands from the console input
-       fun process_debug_command(command:String): Bool
+       fun process_debug_command(command: String): Bool
        do
                # Step-out command
                if command == "finish"
@@ -478,10 +470,17 @@ class Debugger
                # Step-over command
                else if command == "n" then
                        return step_over
+               # Shows help
+               else if command == "help" then
+                       help
+                       return true
                # Opens a new NitIndex prompt on current model
                else if command == "nitx" then
                        new NitIndex.with_infos(modelbuilder, self.mainmodule).prompt
                        return true
+               else if command == "bt" or command == "backtrack" then
+                       print stack_trace
+                       return true
                # Continues execution until the end
                else if command == "c" then
                        return continue_exec
@@ -503,41 +502,72 @@ class Debugger
                        print stack_trace
                        exit(0)
                else
-                       var parts_of_command = command.split_with(' ')
+                       var parts = command.split_with(' ')
+                       var cname = parts.first
                        # Shows the value of a variable in the current frame
-                       if parts_of_command[0] == "p" or parts_of_command[0] == "print" then
-                               print_command(parts_of_command)
+                       if cname == "p" or cname == "print" then
+                               print_command(parts)
                        # Places a breakpoint on line x of file y
-                       else if parts_of_command[0] == "break" or parts_of_command[0] == "b"
-                       then
-                               process_place_break_fun(parts_of_command)
-                       # Places a temporary breakpoint on line x of file y
-                       else if parts_of_command[0] == "tbreak" and (parts_of_command.length == 2 or parts_of_command.length == 3)
-                       then
-                               process_place_tbreak_fun(parts_of_command)
+                       else if cname == "break" or cname == "b" then
+                               process_place_break_fun(parts)
                        # Removes a breakpoint on line x of file y
-                       else if parts_of_command[0] == "d" or parts_of_command[0] == "delete" then
-                               process_remove_break_fun(parts_of_command)
+                       else if cname == "d" or cname == "delete" then
+                               process_remove_break_fun(parts)
                        # Sets an alias for a variable
-                       else if parts_of_command.length == 3 and parts_of_command[1] == "as"
-                       then
-                               add_alias(parts_of_command[0], parts_of_command[2])
+                       else if parts.length == 2 and parts[1] == "as" then
+                               process_alias(parts)
                        # Modifies the value of a variable in the current frame
-                       else if parts_of_command.length >= 3 and parts_of_command[1] == "=" then
-                               process_mod_function(parts_of_command)
+                       else if parts.length == 3 and parts[1] == "=" then
+                               process_mod_function(parts)
                        # Traces the modifications on a variable
-                       else if parts_of_command.length >= 2 and parts_of_command[0] == "trace" then
-                               process_trace_command(parts_of_command)
+                       else if cname == "trace" then
+                               process_trace_command(parts)
                        # Untraces the modifications on a variable
-                       else if parts_of_command.length == 2 and parts_of_command[0] == "untrace" then
-                               process_untrace_command(parts_of_command)
+                       else if cname == "untrace" then
+                               process_untrace_command(parts)
                        else
-                               print "Unknown command \"{command}\""
+                               bad_command(command)
                        end
                end
                return true
        end
 
+       # Produces help for the commands of the debugger
+       fun help do
+               print ""
+               print "Help :"
+               print "-----------------------------------"
+               print ""
+               print "Variables"
+               print " * Modification: var_name = value (Warning: var_name must be primitive)"
+               print " * Alias: var_name as alias"
+               print ""
+               print "Printing"
+               print " * Variables: p(rint) var_name (Use * to print all local variables)"
+               print " * Collections: p(rint) var_name '[' start_index (.. end_index) ']'"
+               print ""
+               print "Breakpoints"
+               print " * File/line: b(reak) file_name line_number"
+               print " * Remove: d(elete) id"
+               print ""
+               print "Tracepoints"
+               print " * Variable: trace var_name break/print"
+               print " * Untrace variable: untrace var_name"
+               print ""
+               print "Flow control"
+               print " * Next instruction (same-level): n"
+               print " * Next instruction: s"
+               print " * Finish current method: finish"
+               print " * Continue until next breakpoint or end: c"
+               print ""
+               print "General commands"
+               print " * quit: Quits the debugger"
+               print " * abort: Aborts the interpretation, prints the stack trace before leaving"
+               print " * nitx: Ask questions to the model about its entities (classes, methods, etc.)"
+               print " * nit: Inject dynamic code for interpretation"
+               print ""
+       end
+
        #######################################################################
        ##               Processing specific command functions               ##
        #######################################################################
@@ -580,75 +610,96 @@ class Debugger
                return false
        end
 
+       fun bad_command(cmd: String) do
+               print "Unrecognized command {cmd}. Use 'help' to show help."
+       end
+
        # Prints the demanded variable in the command
        #
        # The name of the variable in in position 1 of the array 'parts_of_command'
-       fun print_command(parts_of_command: Array[String])
+       fun print_command(parts: Array[String])
        do
-               if parts_of_command[1] == "*" then
+               if parts.length != 2 then
+                       bad_command(parts.join(" "))
+                       return
+               end
+               if parts[1] == "*" then
                        var map_of_instances = frame.map
 
-                       var keys = map_of_instances.iterator
-
                        var self_var = seek_variable("self", frame)
                        print "self: {self_var.to_s}"
 
                        for instance in map_of_instances.keys do
                                print "{instance.to_s}: {map_of_instances[instance].to_s}"
                        end
-               else if parts_of_command[1] == "stack" then
-                       print self.stack_trace
-               else if parts_of_command[1].chars.has('[') and parts_of_command[1].chars.has(']') then
-                       process_array_command(parts_of_command)
+               else if parts[1].chars.has('[') and parts[1].chars.has(']') then
+                       process_array_command(parts)
                else
-                       var instance = seek_variable(get_real_variable_name(parts_of_command[1]), frame)
+                       var instance = seek_variable(get_real_variable_name(parts[1]), frame)
 
                        if instance != null
                        then
                                print_instance(instance)
                        else
-                               print "Cannot find variable {parts_of_command[1]}"
+                               print "Cannot find variable {parts[1]}"
                        end
                end
        end
 
+       # Process the input command to set an alias for a variable
+       fun process_alias(parts: Array[String]) do
+               if parts.length != 3 then
+                       bad_command(parts.join(" "))
+                       return
+               end
+               add_alias(parts.first, parts.last)
+       end
+
        # Processes the input string to know where to put a breakpoint
-       fun process_place_break_fun(parts_of_command: Array[String])
+       fun process_place_break_fun(parts: Array[String])
        do
-               var bp = get_breakpoint_from_command(parts_of_command)
+               if parts.length != 3 then
+                       bad_command(parts.join(" "))
+                       return
+               end
+               var bp = get_breakpoint_from_command(parts)
                if bp != null then
                        place_breakpoint(bp)
                end
        end
 
        # Returns a breakpoint containing the informations stored in the command
-       fun get_breakpoint_from_command(parts_of_command: Array[String]): nullable Breakpoint
+       fun get_breakpoint_from_command(parts: Array[String]): nullable Breakpoint
        do
-               if parts_of_command[1].is_numeric then
-                       return new Breakpoint(parts_of_command[1].to_i, curr_file)
-               else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
-                       return new Breakpoint(parts_of_command[2].to_i, parts_of_command[1])
+               if parts[1].is_numeric then
+                       return new Breakpoint(parts[1].to_i, curr_file)
+               else if parts.length >= 3 and parts[2].is_numeric then
+                       return new Breakpoint(parts[2].to_i, parts[1])
                else
                        return null
                end
        end
 
        # Processes the command of removing a breakpoint on specified line and file
-       fun process_remove_break_fun(parts_of_command: Array[String])
+       fun process_remove_break_fun(parts: Array[String])
        do
-               if parts_of_command[1].is_numeric then
-                       remove_breakpoint(self.curr_file, parts_of_command[1].to_i)
-               else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then
-                       remove_breakpoint(parts_of_command[1], parts_of_command[2].to_i)
+               if parts.length != 2 then
+                       bad_command(parts.join(" "))
+                       return
+               end
+               if parts[1].is_numeric then
+                       remove_breakpoint(self.curr_file, parts[1].to_i)
+               else if parts.length >= 3 and parts[2].is_numeric then
+                       remove_breakpoint(parts[1], parts[2].to_i)
                end
        end
 
        # Processes an array print command
-       fun process_array_command(parts_of_command: Array[String])
+       fun process_array_command(parts: Array[String])
        do
-               var index_of_first_brace = parts_of_command[1].chars.index_of('[')
-               var variable_name = get_real_variable_name(parts_of_command[1].substring(0,index_of_first_brace))
-               var braces = parts_of_command[1].substring_from(index_of_first_brace)
+               var index_of_first_brace = parts[1].chars.index_of('[')
+               var variable_name = get_real_variable_name(parts[1].substring(0,index_of_first_brace))
+               var braces = parts[1].substring_from(index_of_first_brace)
 
                var indexes = remove_braces(braces)
 
@@ -676,27 +727,32 @@ class Debugger
        # Processes the modification function to modify a variable dynamically
        #
        # Command of type variable = value
-       fun process_mod_function(parts_of_command: Array[String])
+       fun process_mod_function(parts: Array[String])
        do
-               parts_of_command[0] = get_real_variable_name(parts_of_command[0])
-               var parts_of_variable = parts_of_command[0].split_with(".")
+               if parts.length != 3 then
+                       bad_command(parts.join(" "))
+                       return
+               end
+               var p0 = parts[0]
+               p0 = get_real_variable_name(p0)
+               var parts_of_variable = p0.split_with(".")
 
                if parts_of_variable.length > 1 then
                        var last_part = parts_of_variable.pop
-                       var first_part = parts_of_command[0].substring(0,parts_of_command[0].length - last_part.length - 1)
+                       var first_part = p0.substring(0,p0.length - last_part.length - 1)
                        var papa = seek_variable(first_part, frame)
 
                        if papa != null and papa isa MutableInstance then
                                var attribute = get_attribute_in_mutable_instance(papa, last_part)
 
                                if attribute != null then
-                                       modify_argument_of_complex_type(papa, attribute, parts_of_command[2])
+                                       modify_argument_of_complex_type(papa, attribute, parts[2])
                                end
                        end
                else
                        var target = seek_variable(parts_of_variable[0], frame)
                        if target != null then
-                               modify_in_frame(target, parts_of_command[2])
+                               modify_in_frame(target, parts[2])
                        end
                end
        end
@@ -704,42 +760,46 @@ class Debugger
        # Processes the untrace variable command
        #
        # Command pattern : "untrace variable"
-       fun process_untrace_command(parts_of_command: Array[String])
+       fun process_untrace_command(parts: Array[String])
        do
-               var variable_name = get_real_variable_name(parts_of_command[1])
+               if parts.length != 2 then
+                       bad_command(parts.join(" "))
+                       return
+               end
+               var variable_name = get_real_variable_name(parts[1])
                if untrace_variable(variable_name) then
-                       print "Untraced variable {parts_of_command[1]}"
+                       print "Untraced variable {parts[1]}"
                else
-                       print "{parts_of_command[1]} is not traced"
+                       print "{parts[1]} is not traced"
                end
        end
 
        # Processes the trace variable command
        #
        # Command pattern : "trace variable [break/print]"
-       fun process_trace_command(parts_of_command: Array[String])
+       fun process_trace_command(parts: Array[String])
        do
-               var variable_name = get_real_variable_name(parts_of_command[1])
+               if parts.length != 3 then
+                       bad_command(parts.join(" "))
+                       return
+               end
+               var variable_name = get_real_variable_name(parts[1])
                var breaker:Bool
 
                if seek_variable(variable_name, frame) == null then
-                       print "Cannot find a variable called {parts_of_command[1]}"
+                       print "Cannot find a variable called {parts[1]}"
                        return
                end
 
-               if parts_of_command.length == 3 then
-                       if parts_of_command[2] == "break" then
-                               breaker = true
-                       else
-                               breaker = false
-                       end
+               if parts[2] == "break" then
+                       breaker = true
                else
                        breaker = false
                end
 
                trace_variable(variable_name, breaker)
 
-               print "Successfully tracing {parts_of_command[1]}"
+               print "Successfully tracing {parts[1]}"
        end
 
        #######################################################################
@@ -832,14 +892,12 @@ class Debugger
 
                var trigger_char_escape = false
                var trigger_string_escape = false
-               var trigger_concat_in_string = false
 
                for i in instruction.chars do
                        if trigger_char_escape then
                                if i == '\'' then trigger_char_escape = false
                        else if trigger_string_escape then
                                if i == '{' then
-                                       trigger_concat_in_string = true
                                        trigger_string_escape = false
                                else if i == '\"' then trigger_string_escape = false
                        else
@@ -857,7 +915,6 @@ class Debugger
                                else if i == '\"' then
                                        trigger_string_escape = true
                                else if i == '}' then
-                                       trigger_concat_in_string = false
                                        trigger_string_escape = true
                                else
                                        if instruction_buffer.length > 0 and not instruction_buffer.is_numeric and not (instruction_buffer.chars[0] >= 'A' and instruction_buffer.chars[0] <= 'Z') then result_array.push(instruction_buffer.to_s)
@@ -1084,8 +1141,6 @@ class Debugger
        do
                var collection_length_attribute = get_attribute_in_mutable_instance(collection, "length")
 
-               var real_collection_length: nullable Int = null
-
                if collection_length_attribute != null then
                        var primitive_length_instance = collection.attributes[collection_length_attribute]
                        if primitive_length_instance isa PrimitiveInstance[Int] then
@@ -1215,17 +1270,6 @@ class Debugger
                end
        end
 
-       #Places a breakpoint that will trigger once and be destroyed afterwards
-       fun process_place_tbreak_fun(parts_of_command: Array[String])
-       do
-               var bp = get_breakpoint_from_command(parts_of_command)
-               if bp != null
-               then
-                       bp.set_max_breaks(1)
-                       place_breakpoint(bp)
-               end
-       end
-
        #######################################################################
        ##                  Breakpoint removing functions                    ##
        #######################################################################
index 1121ed0..e16f060 100644 (file)
@@ -20,10 +20,11 @@ module naive_interpreter
 import literal
 import semantize
 private import parser::tables
+import mixin
 
 redef class ToolContext
        # --discover-call-trace
-       var opt_discover_call_trace: OptionBool = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
+       var opt_discover_call_trace = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace")
 
        redef init
        do
@@ -57,7 +58,7 @@ class NaiveInterpreter
        # The modelbuilder that know the AST and its associations with the model
        var modelbuilder: ModelBuilder
 
-       # The main moduleof the program (used to lookup methoda
+       # The main module of the program (used to lookup method)
        var mainmodule: MModule
 
        # The command line arguments of the interpreted program
@@ -65,6 +66,7 @@ class NaiveInterpreter
        # arguments[1] is the first argument
        var arguments: Array[String]
 
+       # The main Sys instance
        var mainobj: nullable Instance
 
        init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String])
@@ -112,43 +114,26 @@ class NaiveInterpreter
        # Set this mark to skip the evaluation until the end of the specified method frame
        var returnmark: nullable Frame = null
 
-       # Is a break executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_break`
-       var breakmark: nullable EscapeMark = null
-
-       # Is a continue executed?
-       # Set this mark to skip the evaluation until a labeled statement catch it with `is_continue`
-       var continuemark: nullable EscapeMark = null
+       # Is a break or a continue executed?
+       # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
+       var escapemark: nullable EscapeMark = null
 
        # Is a return or a break or a continue executed?
        # Use this function to know if you must skip the evaluation of statements
-       fun is_escaping: Bool do return returnmark != null or breakmark != null or continuemark != null
+       fun is_escaping: Bool do return returnmark != null or escapemark != null
 
        # The value associated with the current return/break/continue, if any.
        # Set the value when you set a escapemark.
        # Read the value when you catch a mark or reach the end of a method
        var escapevalue: nullable Instance = null
 
-       # If there is a break and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no break or if `escapemark` is null then return false.
-       # Use this function to catch a potential break.
-       fun is_break(escapemark: nullable EscapeMark): Bool
-       do
-               if escapemark != null and self.breakmark == escapemark then
-                       self.breakmark = null
-                       return true
-               else
-                       return false
-               end
-       end
-
-       # If there is a continue and is associated with `escapemark`, then return true an clear the mark.
-       # If there is no continue or if `escapemark` is null then return false.
-       # Use this function to catch a potential continue.
-       fun is_continue(escapemark: nullable EscapeMark): Bool
+       # If there is a break/continue and is associated with `escapemark`, then return true and clear the mark.
+       # If there is no break/continue or if `escapemark` is null then return false.
+       # Use this function to catch a potential break/continue.
+       fun is_escape(escapemark: nullable EscapeMark): Bool
        do
-               if escapemark != null and self.continuemark == escapemark then
-                       self.continuemark = null
+               if escapemark != null and self.escapemark == escapemark then
+                       self.escapemark = null
                        return true
                else
                        return false
@@ -225,13 +210,13 @@ class NaiveInterpreter
                return new PrimitiveInstance[Float](ic.mclass_type, val)
        end
 
-       # The unique intance of the `true` value.
+       # The unique instance of the `true` value.
        var true_instance: Instance
 
-       # The unique intance of the `false` value.
+       # The unique instance of the `false` value.
        var false_instance: Instance
 
-       # The unique intance of the `null` value.
+       # The unique instance of the `null` value.
        var null_instance: Instance
 
        # Return a new array made of `values`.
@@ -247,6 +232,19 @@ class NaiveInterpreter
                return res
        end
 
+       fun value_instance(object: Object): Instance
+       do
+               if object isa Int then
+                       return int_instance(object)
+               else if object isa Bool then
+                       return bool_instance(object)
+               else if object isa String then
+                       return string_instance(object)
+               else
+                       abort
+               end
+       end
+
        # Return a new native string initialized with `txt`
        fun native_string_instance(txt: String): Instance
        do
@@ -256,13 +254,22 @@ class NaiveInterpreter
                return new PrimitiveInstance[Buffer](ic.mclass_type, val)
        end
 
+       # Return a new String instance for `txt`
+       fun string_instance(txt: String): Instance
+       do
+               var nat = native_string_instance(txt)
+               var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.length)])
+               assert res != null
+               return res
+       end
+
        # The current frame used to store local variables of the current method executed
        fun frame: Frame do return frames.first
 
        # The stack of all frames. The first one is the current one.
-       var frames: List[Frame] = new List[Frame]
+       var frames = new List[Frame]
 
-       # Return a stack stace. One line per function
+       # Return a stack trace. One line per function
        fun stack_trace: String
        do
                var b = new FlatBuffer
@@ -309,55 +316,58 @@ class NaiveInterpreter
                f.map[v] = value
        end
 
-       # Store known method, used to trace methods as thez are reached
+       # Store known methods, used to trace methods as they are reached
        var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]
 
-       # Common code for calls to injected methods and normal methods
-       fun call_commons(mpropdef: MMethodDef, args: Array[Instance]): Array[Instance]
+       # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
+       # This method is used to manage varargs in signatures and returns the real array
+       # of instances to use in the call.
+       # Return `null` if one of the evaluation of the arguments return null.
+       fun varargize(mpropdef: MMethodDef, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
        do
-               var vararg_rank = mpropdef.msignature.vararg_rank
-               if vararg_rank >= 0 then
-                       assert args.length >= mpropdef.msignature.arity + 1 # because of self
-                       var rawargs = args
-                       args = new Array[Instance]
+               var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
+               var res = new Array[Instance]
+               res.add(recv)
 
-                       args.add(rawargs.first) # recv
+               if args.is_empty then return res
 
-                       for i in [0..vararg_rank[ do
-                               args.add(rawargs[i+1])
-                       end
-
-                       var vararg_lastrank = vararg_rank + rawargs.length-1-mpropdef.msignature.arity
-                       var vararg = new Array[Instance]
-                       for i in [vararg_rank..vararg_lastrank] do
-                               vararg.add(rawargs[i+1])
-                       end
-                       # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis
-                       var elttype = mpropdef.msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, args.first.mtype.as(MClassType))
-                       args.add(self.array_instance(vararg, elttype))
+               var vararg_rank = msignature.vararg_rank
+               var vararg_len = args.length - msignature.arity
+               if vararg_len < 0 then vararg_len = 0
 
-                       for i in [vararg_lastrank+1..rawargs.length-1[ do
-                               args.add(rawargs[i+1])
+               for i in [0..msignature.arity[ do
+                       if i == vararg_rank then
+                               var ne = args[i]
+                               if ne isa AVarargExpr then
+                                       var e = self.expr(ne.n_expr)
+                                       if e == null then return null
+                                       res.add(e)
+                                       continue
+                               end
+                               var vararg = new Array[Instance]
+                               for j in [vararg_rank..vararg_rank+vararg_len] do
+                                       var e = self.expr(args[j])
+                                       if e == null then return null
+                                       vararg.add(e)
+                               end
+                               var elttype = msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
+                               res.add(self.array_instance(vararg, elttype))
+                       else
+                               var j = i
+                               if i > vararg_rank then j += vararg_len
+                               var e = self.expr(args[j])
+                               if e == null then return null
+                               res.add(e)
                        end
                end
-               return args
+               return res
        end
 
        # Execute `mpropdef` for a `args` (where `args[0]` is the receiver).
-       # Return a falue if `mpropdef` is a function, or null if it is a procedure.
-       # The call is direct/static. There is no message-seding/late-binding.
+       # Return a value if `mpropdef` is a function, or null if it is a procedure.
+       # The call is direct/static. There is no message-sending/late-binding.
        fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
        do
-               args = call_commons(mpropdef, args)
-               return call_without_varargs(mpropdef, args)
-       end
-
-       # Common code to call and this function
-       #
-       # Call only executes the variadic part, this avoids
-       # double encapsulation of variadic parameters into an Array
-       fun call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
-       do
                if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then
                        self.discover_call_trace.add mpropdef
                        self.debug("Discovered {mpropdef}")
@@ -366,6 +376,7 @@ class NaiveInterpreter
 
                # Look for the AST node that implements the property
                var mproperty = mpropdef.mproperty
+               var val = mpropdef.constant_value
                if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then
                        var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef]
                        self.parameter_check(npropdef, mpropdef, args)
@@ -374,6 +385,8 @@ class NaiveInterpreter
                        var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef]
                        self.parameter_check(nclassdef, mpropdef, args)
                        return nclassdef.call(self, mpropdef, args)
+               else if val != null then
+                       return value_instance(val)
                else
                        fatal("Fatal Error: method {mpropdef} not found in the AST")
                        abort
@@ -418,7 +431,7 @@ class NaiveInterpreter
        end
 
        # Execute a full `callsite` for given `args`
-       # Use this method, instead of `send` to execute and control the aditionnal behavior of the call-sites
+       # Use this method, instead of `send` to execute and control the additional behavior of the call-sites
        fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance
        do
                var initializers = callsite.mpropdef.initializers
@@ -447,8 +460,8 @@ class NaiveInterpreter
        end
 
        # Execute `mproperty` for a `args` (where `args[0]` is the receiver).
-       # Return a falue if `mproperty` is a function, or null if it is a procedure.
-       # The call is polimotphic. There is a message-seding/late-bindng according to te receiver (args[0]).
+       # Return a value if `mproperty` is a function, or null if it is a procedure.
+       # The call is polymorphic. There is a message-sending/late-binding according to the receiver (args[0]).
        fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance
        do
                var recv = args.first
@@ -495,6 +508,7 @@ class NaiveInterpreter
                var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
                self.mainmodule.linearize_mclassdefs(cds)
                for cd in cds do
+                       if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue
                        var n = self.modelbuilder.mclassdef2nclassdef[cd]
                        for npropdef in n.n_propdefs do
                                if npropdef isa AAttrPropdef then
@@ -507,7 +521,7 @@ class NaiveInterpreter
                return res
        end
 
-       var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
+       private var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]
 
        # Fill the initial values of the newly created instance `recv`.
        # `recv.mtype` is used to know what must be filled.
@@ -694,7 +708,7 @@ redef class AMethPropdef
                if auto_super_call then
                        # standard call-next-method
                        var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype)
-                       v.call_without_varargs(superpd, arguments)
+                       v.call(superpd, arguments)
                end
 
                if mpropdef.is_intern or mpropdef.is_extern then
@@ -743,14 +757,14 @@ redef class AMethPropdef
                        var txt = recv.mtype.to_s
                        return v.native_string_instance(txt)
                else if pname == "==" then
-                       # == is correclt redefined for instances
+                       # == is correctly redefined for instances
                        return v.bool_instance(args[0] == args[1])
                else if pname == "!=" then
                        return v.bool_instance(args[0] != args[1])
                else if pname == "is_same_type" then
                        return v.bool_instance(args[0].mtype == args[1].mtype)
                else if pname == "is_same_instance" then
-                       return v.bool_instance(args[1] != null and args[0].eq_is(args[1]))
+                       return v.bool_instance(args[0].eq_is(args[1]))
                else if pname == "exit" then
                        exit(args[1].to_i)
                        abort
@@ -797,6 +811,8 @@ redef class AMethPropdef
                                return v.int_instance(args[0].to_i.bin_or(args[1].to_i))
                        else if pname == "bin_xor" then
                                return v.int_instance(args[0].to_i.bin_xor(args[1].to_i))
+                       else if pname == "bin_not" then
+                               return v.int_instance(args[0].to_i.bin_not)
                        else if pname == "native_int_to_s" then
                                return v.native_string_instance(recvval.to_s)
                        else if pname == "strerror_ext" then
@@ -1097,7 +1113,7 @@ redef class AClassdef
                        if not mpropdef.is_intro then
                                # standard call-next-method
                                var superpd = mpropdef.lookup_next_definition(v.mainmodule, args.first.mtype)
-                               v.call_without_varargs(superpd, args)
+                               v.call(superpd, args)
                        end
                        return null
                else
@@ -1197,7 +1213,7 @@ redef class ASelfExpr
        end
 end
 
-redef class AContinueExpr
+redef class AEscapeExpr
        redef fun stmt(v)
        do
                var ne = self.n_expr
@@ -1206,20 +1222,7 @@ redef class AContinueExpr
                        if i == null then return
                        v.escapevalue = i
                end
-               v.continuemark = self.escapemark
-       end
-end
-
-redef class ABreakExpr
-       redef fun stmt(v)
-       do
-               var ne = self.n_expr
-               if ne != null then
-                       var i = v.expr(ne)
-                       if i == null then return
-                       v.escapevalue = i
-               end
-               v.breakmark = self.escapemark
+               v.escapemark = self.escapemark
        end
 end
 
@@ -1285,7 +1288,7 @@ redef class ADoExpr
        redef fun stmt(v)
        do
                v.stmt(self.n_block)
-               v.is_break(self.escapemark) # Clear the break (if any)
+               v.is_escape(self.break_mark) # Clear the break (if any)
        end
 end
 
@@ -1297,8 +1300,8 @@ redef class AWhileExpr
                        if cond == null then return
                        if not cond.is_true then return
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@ -1309,8 +1312,8 @@ redef class ALoopExpr
        do
                loop
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
+                       if v.is_escape(self.break_mark) then return
+                       v.is_escape(self.continue_mark) # Clear the break
                        if v.is_escaping then return
                end
        end
@@ -1328,7 +1331,7 @@ redef class AForExpr
                #self.debug("iter {iter}")
                loop
                        var isok = v.callsite(method_is_ok, [iter]).as(not null)
-                       if not isok.is_true then return
+                       if not isok.is_true then break
                        if self.variables.length == 1 then
                                var item = v.callsite(method_item, [iter]).as(not null)
                                #self.debug("item {item}")
@@ -1342,11 +1345,15 @@ redef class AForExpr
                                abort
                        end
                        v.stmt(self.n_block)
-                       if v.is_break(self.escapemark) then return
-                       v.is_continue(self.escapemark) # Clear the break
-                       if v.is_escaping then return
+                       if v.is_escape(self.break_mark) then break
+                       v.is_escape(self.continue_mark) # Clear the break
+                       if v.is_escaping then break
                        v.callsite(method_next, [iter])
                end
+               var method_finish = self.method_finish
+               if method_finish != null then
+                       v.callsite(method_finish, [iter])
+               end
        end
 end
 
@@ -1458,9 +1465,7 @@ redef class AStringFormExpr
        redef fun expr(v)
        do
                var txt = self.value.as(not null)
-               var nat = v.native_string_instance(txt)
-               var res = v.send(v.force_get_primitive_method("to_s", nat.mtype), [nat]).as(not null)
-               return res
+               return v.string_instance(txt)
        end
 end
 
@@ -1560,7 +1565,6 @@ redef class AAsNotnullExpr
        do
                var i = v.expr(self.n_expr)
                if i == null then return null
-               var mtype = v.unanchor_type(self.mtype.as(not null))
                if i.mtype isa MNullType then
                        fatal(v, "Cast failed")
                end
@@ -1594,12 +1598,8 @@ redef class ASendExpr
        do
                var recv = v.expr(self.n_expr)
                if recv == null then return null
-               var args = [recv]
-               for a in self.raw_arguments do
-                       var i = v.expr(a)
-                       if i == null then return null
-                       args.add(i)
-               end
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               if args == null then return null
 
                var res = v.callsite(callsite, args)
                return res
@@ -1611,12 +1611,8 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr)
                if recv == null then return
-               var args = [recv]
-               for a in self.raw_arguments do
-                       var i = v.expr(a)
-                       if i == null then return
-                       args.add(i)
-               end
+               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               if args == null then return
                var value = v.expr(self.n_value)
                if value == null then return
 
@@ -1636,16 +1632,12 @@ redef class ASuperExpr
        redef fun expr(v)
        do
                var recv = v.frame.arguments.first
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       var i = v.expr(a)
-                       if i == null then return null
-                       args.add(i)
-               end
 
                var callsite = self.callsite
                if callsite != null then
-                       # Add additionnals arguments for the super init call
+                       var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+                       if args == null then return null
+                       # Add additional arguments for the super init call
                        if args.length == 1 then
                                for i in [0..callsite.msignature.arity[ do
                                        args.add(v.frame.arguments[i+1])
@@ -1656,14 +1648,17 @@ redef class ASuperExpr
                        return res
                end
 
+               # standard call-next-method
+               var mpropdef = self.mpropdef
+               mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
+
+               var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
+               if args == null then return null
+
                if args.length == 1 then
                        args = v.frame.arguments
                end
-
-               # stantard call-next-method
-               var mpropdef = self.mpropdef
-               mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)
-               var res = v.call_without_varargs(mpropdef, args)
+               var res = v.call(mpropdef, args)
                return res
        end
 end
@@ -1674,12 +1669,8 @@ redef class ANewExpr
                var mtype = v.unanchor_type(self.mtype.as(not null))
                var recv: Instance = new MutableInstance(mtype)
                v.init_instance(recv)
-               var args = [recv]
-               for a in self.n_args.n_exprs do
-                       var i = v.expr(a)
-                       if i == null then return null
-                       args.add(i)
-               end
+               var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+               if args == null then return null
                var res2 = v.callsite(callsite, args)
                if res2 != null then
                        #self.debug("got {res2} from {mproperty}. drop {recv}")
index 664415a..eac5853 100644 (file)
@@ -35,8 +35,6 @@ end
 # Print class tables metrics for the classes of the program main
 fun compute_tables_metrics(main: MModule)
 do
-       var model = main.model
-
        var nc = 0 # Number of runtime classes
        var nl = 0 # Number of usages of class definitions (a class definition can be used more than once)
        var nhp = 0 # Number of usages of properties (a property can be used more than once)
diff --git a/src/mixin.nit b/src/mixin.nit
new file mode 100644 (file)
index 0000000..94d0c32
--- /dev/null
@@ -0,0 +1,110 @@
+# 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.
+
+# Loading and additional module refinements at link-time.
+#
+# Used to factorize some code used by the engines.
+module mixin
+
+import modelbuilder
+
+redef class ToolContext
+       # --mixin
+       var opt_mixins = new OptionArray("Additionals module to min-in", "-m", "--mixin")
+       # --define
+       var opt_defines = new OptionArray("Define a specific property", "-D", "--define")
+
+       redef init
+       do
+               super
+               option_context.add_option(opt_mixins, opt_defines)
+       end
+
+       redef fun make_main_module(mmodules)
+       do
+               var mixins = opt_mixins.value
+               if not mixins.is_empty then
+                       mmodules.add_all modelbuilder.parse(opt_mixins.value)
+                       modelbuilder.run_phases
+               end
+
+               var mainmodule = super
+
+               var defines = opt_defines.value
+               if not defines.is_empty then
+                       var location = mainmodule.location
+                       var model = mainmodule.model
+
+                       if mainmodule == mmodules.first then
+                               mainmodule = new MModule(model, null, mainmodule.name + "-d", location)
+                               mainmodule.set_imported_mmodules(mmodules)
+                               mainmodule.is_fictive = true
+                       end
+
+                       var recv = mainmodule.object_type
+                       var mclassdef = new MClassDef(mainmodule, recv, location)
+                       mclassdef.add_in_hierarchy
+
+                       for define in defines do
+                               var spl = define.split_once_on('=')
+                               var name = spl.first
+                               var val = null
+                               if spl.length > 1 then val = spl[1]
+                               var prop = mainmodule.try_get_primitive_method(name, recv.mclass)
+                               if prop == null then
+                                       error(null, "Error: --define: no top-level function `{name}`")
+                                       continue
+                               end
+                               var ret = prop.intro.msignature.return_mtype
+                               var v
+                               if ret == null then
+                                       error(null, "Error: --define: Method `{prop}` is not a function")
+                                       continue
+                               else if ret.to_s == "Bool" then
+                                       if val == null or val == "true" then
+                                               v = true
+                                       else if val == "false" then
+                                               v = false
+                                       else
+                                               error(null, "Error: --define: Method `{prop}` need a Bool.")
+                                               continue
+                                       end
+                               else if ret.to_s == "Int" then
+                                       if val != null and val.is_numeric then
+                                               v = val.to_i
+                                       else
+                                               error(null, "Error: --define: Method `{prop}` need a Int.")
+                                               continue
+                                       end
+                               else if ret.to_s == "String" then
+                                       if val != null then
+                                               v = val
+                                       else
+                                               error(null, "Error: --define: Method `{prop}` need a String.")
+                                               continue
+                                       end
+                               else
+                                       error(null, "Error: --define: Method `{prop}` return an unmanaged type {ret}.")
+                                       continue
+                               end
+                               var pd = new MMethodDef(mclassdef, prop, location)
+                               pd.msignature = prop.intro.msignature
+                               pd.constant_value = v
+                       end
+                       check_errors
+               end
+
+               return mainmodule
+       end
+end
index f989302..64e82c7 100644 (file)
@@ -293,7 +293,8 @@ redef class MModule
 end
 
 private class MClassDefSorter
-       super AbstractSorter[MClassDef]
+       super Comparator
+       redef type COMPARED: MClassDef
        var mmodule: MModule
        redef fun compare(a, b)
        do
@@ -305,7 +306,8 @@ private class MClassDefSorter
 end
 
 private class MPropDefSorter
-       super AbstractSorter[MPropDef]
+       super Comparator
+       redef type COMPARED: MPropDef
        var mmodule: MModule
        redef fun compare(pa, pb)
        do
@@ -1224,7 +1226,6 @@ class MVirtualType
                if is_fixed(mmodule, resolved_reciever) then return res
                # If the resolved type isa intern class, then there is no possible valid redefinition in any potential subclass. self is just fixed. so simply return the resolution
                if res isa MClassType and res.mclass.kind == enum_kind then return res
-               # TODO: Add 'fixed' virtual type in the specification.
                # TODO: What if bound to a MParameterType?
                # Note that Nullable types can always be redefined by the non nullable version, so there is no specific case on it.
 
@@ -1273,7 +1274,6 @@ end
 #
 # Note that parameter types are shared among class refinements.
 # Therefore parameter only have an internal name (see `to_s` for details).
-# TODO: Add a `name_for` to get better messages.
 class MParameterType
        super MType
 
@@ -1975,6 +1975,16 @@ class MMethodDef
 
        # Is the method definition extern?
        var is_extern = false is writable
+
+       # An optional constant value returned in functions.
+       #
+       # Only some specific primitife value are accepted by engines.
+       # Is used when there is no better implementation available.
+       #
+       # Currently used only for the implementation of the `--define`
+       # command-line option.
+       # SEE: module `mixin`.
+       var constant_value: nullable Object = null is writable
 end
 
 # A local definition of an attribute
index bc324b7..03ca3eb 100644 (file)
@@ -62,16 +62,19 @@ end
 # Compare modules and groups using the
 # FIXME do not use Object, but a better common interface of MModule and MGroup
 private class LinexComparator
-       super Comparator[Object]
+       super Comparator
+
+       redef type COMPARED: Object
+
        var mins = new HashMap [MGroup, nullable MModule]
        var maxs = new HashMap [MGroup, nullable MModule]
-       fun min(o: Object): nullable MModule do
+       fun mini(o: Object): nullable MModule do
                if o isa MModule then return o
                assert o isa MGroup
                if not mins.has_key(o) then computeminmax(o)
                return mins[o]
        end
-       fun max(o: Object): nullable MModule do
+       fun maxi(o: Object): nullable MModule do
                if o isa MModule then return o
                assert o isa MGroup
                if not maxs.has_key(o) then computeminmax(o)
@@ -84,14 +87,14 @@ private class LinexComparator
                        return
                end
                var subs = tree.sub[o]
-               var minres = min(subs.first)
-               var maxres = max(subs.first)
+               var minres = mini(subs.first)
+               var maxres = maxi(subs.first)
                var order = minres.model.mmodule_importation_hierarchy
                for o2 in subs do
-                       var c = min(o2)
+                       var c = mini(o2)
                        if c == null then continue
                        if minres == null or order.compare(minres, c) > 0 then minres = c
-                       c = max(o2)
+                       c = maxi(o2)
                        if c == null then continue
                        if maxres == null or order.compare(maxres, c) < 0 then maxres = c
                end
@@ -100,8 +103,8 @@ private class LinexComparator
                #if minres != maxres then print "* {o} {minres}..{maxres}"
        end
        redef fun compare(a,b) do
-               var ma = min(a)
-               var mb = min(b)
+               var ma = mini(a)
+               var mb = mini(b)
                if ma == null then
                        if mb == null then return 0 else return -1
                else if mb == null then
index ff1a8d9..9297793 100644 (file)
@@ -525,9 +525,9 @@ end
 
 # Sort mentities by their name
 class MEntityNameSorter
-       super AbstractSorter[MEntity]
+       super Comparator
+       redef type COMPARED: MEntity
        redef fun compare(a, b) do return a.name <=> b.name
-       init do end
 end
 
 # Sort MConcerns based on the module importation hierarchy ranking
@@ -540,9 +540,8 @@ end
 # If both `a` and `b` have the same ranking,
 # ordering is based on lexicographic comparison of `a.name` and `b.name`
 class MConcernRankSorter
-       super AbstractSorter[MConcern]
-
-       init do end
+       super Comparator
+       redef type COMPARED: MConcern
 
        redef fun compare(a, b) do
                if a.concern_rank == b.concern_rank then
index d702369..30a0617 100644 (file)
@@ -53,8 +53,8 @@ redef class ToolContext
 
        private var modelbuilder_real: nullable ModelBuilder = null
 
-       # Run `process_mainmodule` on all phases
-       fun run_global_phases(mmodules: Array[MModule])
+       # Combine module to make a single one if required.
+       fun make_main_module(mmodules: Array[MModule]): MModule
        do
                assert not mmodules.is_empty
                var mainmodule
@@ -62,10 +62,17 @@ redef class ToolContext
                        mainmodule = mmodules.first
                else
                        # We need a main module, so we build it by importing all modules
-                       mainmodule = new MModule(modelbuilder.model, null, mmodules.first.name, new Location(mmodules.first.location.file, 0, 0, 0, 0))
+                       mainmodule = new MModule(modelbuilder.model, null, mmodules.first.name + "-m", new Location(mmodules.first.location.file, 0, 0, 0, 0))
                        mainmodule.is_fictive = true
                        mainmodule.set_imported_mmodules(mmodules)
                end
+               return mainmodule
+       end
+
+       # Run `process_mainmodule` on all phases
+       fun run_global_phases(mmodules: Array[MModule])
+       do
+               var mainmodule = make_main_module(mmodules)
                for phase in phases_list do
                        if phase.disabled then continue
                        phase.process_mainmodule(mainmodule, mmodules)
@@ -738,11 +745,13 @@ class ModelBuilder
        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: ANode, name: String, recv: MClass, mmodule: MModule): MMethod
+       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
-                       self.toolcontext.fatal_error(n.hot_location, "Fatal Error: {recv} must have a property named {name}.")
+                       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
index 2a5e9fb..441a6c2 100644 (file)
@@ -258,7 +258,6 @@ redef class ModelBuilder
        private fun check_supertypes(nmodule: AModule, nclassdef: AClassdef)
        do
                var mmodule = nmodule.mmodule.as(not null)
-               var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object")
                var mclass = nclassdef.mclass.as(not null)
                var mclassdef = nclassdef.mclassdef.as(not null)
 
@@ -410,7 +409,7 @@ redef class ModelBuilder
 
        # Register the nclassdef associated to each mclassdef
        # FIXME: why not refine the `MClassDef` class with a nullable attribute?
-       var mclassdef2nclassdef: HashMap[MClassDef, AClassdef] = new HashMap[MClassDef, AClassdef]
+       var mclassdef2nclassdef = new HashMap[MClassDef, AClassdef]
 
        # Return the static type associated to the node `ntype`.
        # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types)
index 74e4932..6a34e2e 100644 (file)
@@ -38,7 +38,7 @@ end
 redef class ModelBuilder
        # Register the npropdef associated to each mpropdef
        # FIXME: why not refine the `MPropDef` class with a nullable attribute?
-       var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef]
+       var mpropdef2npropdef = new HashMap[MPropDef, APropdef]
 
        # Build the properties of `nclassdef`.
        # REQUIRE: all superclasses are built.
@@ -202,6 +202,7 @@ redef class ModelBuilder
                # Can we just inherit?
                if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then
                        self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3)
+                       mclassdef.mclass.root_init = longest
                        return
                end
 
@@ -217,6 +218,7 @@ redef class ModelBuilder
                        var msignature = new MSignature(mparameters, null)
                        defined_init.new_msignature = msignature
                        self.toolcontext.info("{mclassdef} extends its basic constructor signature to {defined_init}{msignature}", 3)
+                       mclassdef.mclass.root_init = defined_init
                        return
                end
 
@@ -230,6 +232,7 @@ redef class ModelBuilder
                mpropdef.msignature = new MSignature(new Array[MParameter], null) # always an empty real signature
                nclassdef.mfree_init = mpropdef
                self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3)
+               mclassdef.mclass.root_init = mpropdef
        end
 
        # Check the visibility of `mtype` as an element of the signature of `mpropdef`.
@@ -241,7 +244,7 @@ redef class ModelBuilder
                # Extract visibility information of the main part of `mtype`
                # It is a case-by case
                var vis_type: nullable MVisibility = null # The own visibility of the type
-               var mmodule_type: nullable MModule = null # The origial module of the type
+               var mmodule_type: nullable MModule = null # The original module of the type
                mtype = mtype.as_notnullable
                if mtype isa MClassType then
                        vis_type = mtype.mclass.visibility
@@ -288,12 +291,21 @@ redef class MPropDef
 end
 
 redef class AClassdef
-       var build_properties_is_done: Bool = false
+       var build_properties_is_done = false
 
        # The free init (implicitely constructed by the class if required)
        var mfree_init: nullable MMethodDef = null
 end
 
+redef class MClass
+       # The base init of the class.
+       # Used to get the common new_msignature and initializers
+       #
+       # TODO: Where to put this information is not clear because unlike other
+       # informations, the initialisers are stable in a same class.
+       var root_init: nullable MMethodDef = null
+end
+
 redef class MClassDef
        # What is the `APropdef` associated to a `MProperty`?
        # Used to check multiple definition of a property.
@@ -440,7 +452,7 @@ redef class ASignature
                var ntype = self.n_type
                if ntype != null then
                        self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype)
-                       if self.ret_type == null then return false # Skip errir
+                       if self.ret_type == null then return false # Skip error
                end
 
                self.is_visited = true
@@ -582,6 +594,16 @@ redef class AMethPropdef
                var mmodule = mclassdef.mmodule
                var nsig = self.n_signature
 
+               if mpropdef.mproperty.is_root_init and not mclassdef.is_intro then
+                       var root_init = mclassdef.mclass.root_init
+                       if root_init != null then
+                               # Inherit the initializers by refinement
+                               mpropdef.new_msignature = root_init.new_msignature
+                               assert mpropdef.initializers.is_empty
+                               mpropdef.initializers.add_all root_init.initializers
+                       end
+               end
+
                # Retrieve info from the signature AST
                var param_names = new Array[String] # Names of parameters from the AST
                var param_types = new Array[MType] # Types of parameters from the AST
@@ -718,12 +740,12 @@ redef class AAttrPropdef
        # Is the node tagged `noinit`?
        var noinit = false
 
-       # Is the node taggeg lazy?
+       # Is the node tagged lazy?
        var is_lazy = false
 
-       # The guard associated to a lasy attribute.
+       # The guard associated to a lazy attribute.
        # Because some engines does not have a working `isset`,
-       # this additionnal attribute is used to guard the lazy initialization.
+       # this additional attribute is used to guard the lazy initialization.
        # TODO: to remove once isset is correctly implemented
        var mlazypropdef: nullable MAttributeDef
 
@@ -830,7 +852,7 @@ redef class AAttrPropdef
        redef fun build_signature(modelbuilder)
        do
                var mpropdef = self.mpropdef
-               if mpropdef == null then return # Error thus skiped
+               if mpropdef == null then return # Error thus skipped
                var mclassdef = mpropdef.mclassdef
                var mmodule = mclassdef.mmodule
                var mtype: nullable MType = null
@@ -843,10 +865,10 @@ redef class AAttrPropdef
                        if mtype == null then return
                end
 
-               # Inherit the type from the getter (usually an abstact getter)
+               # Inherit the type from the getter (usually an abstract getter)
                if mtype == null and mreadpropdef != null and not mreadpropdef.is_intro then
                        var msignature = mreadpropdef.mproperty.intro.msignature
-                       if msignature == null then return # Error, thus skiped
+                       if msignature == null then return # Error, thus skipped
                        mtype = msignature.return_mtype
                end
 
@@ -918,12 +940,10 @@ redef class AAttrPropdef
        redef fun check_signature(modelbuilder)
        do
                var mpropdef = self.mpropdef
-               if mpropdef == null then return # Error thus skiped
-               var mclassdef = mpropdef.mclassdef
-               var mmodule = mclassdef.mmodule
+               if mpropdef == null then return # Error thus skipped
                var ntype = self.n_type
                var mtype = self.mpropdef.static_mtype
-               if mtype == null then return # Error thus skiped
+               if mtype == null then return # Error thus skipped
 
                # Lookup for signature in the precursor
                # FIXME all precursors should be considered
@@ -1046,7 +1066,7 @@ redef class ATypePropdef
        redef fun build_signature(modelbuilder)
        do
                var mpropdef = self.mpropdef
-               if mpropdef == null then return # Error thus skiped
+               if mpropdef == null then return # Error thus skipped
                var mclassdef = mpropdef.mclassdef
                var mmodule = mclassdef.mmodule
                var mtype: nullable MType = null
@@ -1062,10 +1082,10 @@ redef class ATypePropdef
        redef fun check_signature(modelbuilder)
        do
                var mpropdef = self.mpropdef
-               if mpropdef == null then return # Error thus skiped
+               if mpropdef == null then return # Error thus skipped
 
                var bound = self.mpropdef.bound
-               if bound == null then return # Error thus skiped
+               if bound == null then return # Error thus skipped
 
                modelbuilder.check_visibility(n_type, bound, mpropdef)
 
index b44d034..f9d3c9a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Save and load `Model` from/to Neo4j base.
+# Save and load a `Model` to/from a Neo4j graph.
 #
 # Nit models are composed by MEntities.
 # This module creates NeoNode for each MEntity found in a `Model` and save them
 # into Neo4j database.
 #
-# see `Neo4jClient`.
+# SEE: `Neo4jClient`
 #
 # NeoNodes can also be translated back to MEntities to rebuild a Nit `Model`.
 #
-# Structure of the nit `Model` in base:
+# Structure of the nit `Model` in the graph:
 #
 # Note : Any null or empty attribute will not be saved in the database.
 #
 #
 # * labels: `MClass`, `model_name` and `MEntity`.
 # * `full_name`: fully qualified name.
-# * `arity`: number of generic formal parameters. 0 if the class is not generic.
 # * `kind`: kind of the class (`interface`, `abstract class`, etc.)
 # * `visibility`: visibility of the class.
+# * `parameter_names`: JSON array listing the name of each formal generic
+# parameter (in order of declaration).
 # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type`
 #
 # Arguments in the `CLASSTYPE` are named following the `parameter_names`
@@ -82,8 +83,6 @@
 # * labels: `MClassDef`, `model_name` and `MEntity`.
 # * `is_intro`: Does this definition introduce the class?
 # * `location`: origin of the definition. SEE: `Location.to_s`
-# * `parameter_names`: JSON array listing the name of each formal generic
-# parameter (in order of declaration).
 # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the
 # classdef.
 # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`.
 # * `(:MMethodDef)-[:SIGNATURE]->(:MSignature)`: signature attached to the
 # property definition.
 #
+# Additional relationship for `MAttributeDef`:
+#
+# * `(:MAttributeDef)-[:TYPE]->(:MType)`: static type of the attribute,
+# if specified.
+#
 # Additional relationship for `MVirtualTypeDef`:
 #
 # * `(:MVirtualTypeDef)-[:BOUND]->(:MType)`: type to which the virtual type
 # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments.
 #
 # Arguments are named following the `parameter_names` attribute of the
-# `MClassDef` that introduces the class referred by `CLASS`.
+# `MClass` referred by `CLASS`.
 #
 # Additional relationship for `MVirtualType`:
 #
@@ -446,7 +450,6 @@ class NeoModel
                node.labels.add "MModule"
                node["full_name"] = mmodule.full_name
                node["location"] = mmodule.location.to_s
-               var mgroup = mmodule.mgroup
                for parent in mmodule.in_importation.direct_greaters do
                        node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent)))
                end
@@ -633,6 +636,10 @@ class NeoModel
                        end
                else if mpropdef isa MAttributeDef then
                        node.labels.add "MAttributeDef"
+                       var static_mtype = mpropdef.static_mtype
+                       if static_mtype != null then
+                               node.out_edges.add(new NeoEdge(node, "TYPE", to_node(static_mtype)))
+                       end
                else if mpropdef isa MVirtualTypeDef then
                        node.labels.add "MVirtualTypeDef"
                        var bound = mpropdef.bound
@@ -663,6 +670,8 @@ class NeoModel
                else if node.labels.has("MAttributeDef") then
                        mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location)
                        mentities[node] = mpropdef
+                       var static_mtype = node.out_nodes("TYPE")
+                       if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first)
                else if node.labels.has("MVirtualTypeDef") then
                        mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location)
                        mentities[node] = mpropdef
index 24bb00e..9a95194 100644 (file)
@@ -27,10 +27,9 @@ toolcontext.tooldescription = "Usage: nit [OPTION]... <file.nit>...\nInterprets
 # Add an option "-o" to enable compatibilit with the tests.sh script
 var opt = new OptionString("compatibility (does noting)", "-o")
 toolcontext.option_context.add_option(opt)
-var opt_mixins = new OptionArray("Additionals module to min-in", "-m")
 var opt_eval = new OptionBool("Specifies the program from command-line", "-e")
 var opt_loop = new OptionBool("Repeatedly run the program for each line in file-name arguments", "-n")
-toolcontext.option_context.add_option(opt_mixins, opt_eval, opt_loop)
+toolcontext.option_context.add_option(opt_eval, opt_loop)
 # We do not add other options, so process them now!
 toolcontext.process_options(args)
 
@@ -66,20 +65,11 @@ else
        mmodules = modelbuilder.parse([progname])
 end
 
-mmodules.add_all modelbuilder.parse(opt_mixins.value)
 modelbuilder.run_phases
 
 if toolcontext.opt_only_metamodel.value then exit(0)
 
-var mainmodule: nullable MModule
-
-# Here we launch the interpreter on the main module
-if mmodules.length == 1 then
-       mainmodule = mmodules.first
-else
-       mainmodule = new MModule(model, null, mmodules.first.name, mmodules.first.location)
-       mainmodule.set_imported_mmodules(mmodules)
-end
+var mainmodule = toolcontext.make_main_module(mmodules)
 
 var self_mm = mainmodule
 var self_args = arguments
index f43c8d1..32294c2 100644 (file)
@@ -2248,7 +2248,6 @@ end
 
 redef class ASuperstringExpr
        redef fun accept_pretty_printer(v) do
-               var force_inline = self.force_inline
                for n_expr in n_exprs do v.visit n_expr
        end
 
index 5ad0aed..2fd0f82 100644 (file)
@@ -53,7 +53,7 @@ class NitModule
                if header != null then add header
 
                var name = name
-               if name != null then add "module {name}\n\n"
+               add "module {name}\n\n"
 
                for i in imports do add "import {i}\n"
                add "\n"
index f7f4b60..a4d6d44 100644 (file)
@@ -1347,14 +1347,19 @@ abstract class ALabelable
        var n_label: nullable ALabel = null is writable
 end
 
-# A `break` statement.
-class ABreakExpr
+# A `break` or a `continue`
+abstract class AEscapeExpr
        super AExpr
        super ALabelable
-       var n_kwbreak: TKwbreak is writable, noinit
        var n_expr: nullable AExpr = null is writable
 end
 
+# A `break` statement.
+class ABreakExpr
+       super AEscapeExpr
+       var n_kwbreak: TKwbreak is writable, noinit
+end
+
 # An `abort` statement
 class AAbortExpr
        super AExpr
@@ -1363,10 +1368,8 @@ end
 
 # A `continue` statement
 class AContinueExpr
-       super AExpr
-       super ALabelable
+       super AEscapeExpr
        var n_kwcontinue: nullable TKwcontinue = null is writable
-       var n_expr: nullable AExpr = null is writable
 end
 
 # A `do` statement
index 2a83ee0..4d67908 100644 (file)
@@ -246,8 +246,6 @@ private class ComputeProdLocationVisitor
                        end
                end
        end
-
-       init do end
 end
 
 private class TextCollectorVisitor
index a6adfee..897d7b7 100644 (file)
@@ -244,6 +244,9 @@ class RapidTypeAnalysis
                                        if mmethoddef.mproperty.is_root_init and not mmethoddef.is_intro then
                                                self.add_super_send(v.receiver, mmethoddef)
                                        end
+                               else if mmethoddef.constant_value != null then
+                                       # Make the return type live
+                                       v.add_type(mmethoddef.msignature.return_mtype.as(MClassType))
                                else
                                        abort
                                end
@@ -675,6 +678,8 @@ redef class AForExpr
                        abort
                end
                v.add_callsite(self.method_next)
+               var mf = self.method_finish
+               if mf != null then v.add_callsite(mf)
        end
 end
 
index 95b2acb..25874e2 100644 (file)
@@ -32,10 +32,6 @@ end
 
 private class AutoSuperInitVisitor
        super Visitor
-       init
-       do
-       end
-
        redef fun visit(n)
        do
                n.accept_auto_super_init(self)
@@ -210,7 +206,6 @@ redef class ASendExpr
        redef fun accept_auto_super_init(v)
        do
                var mproperty = self.callsite.mproperty
-               if mproperty == null then return
                if mproperty.is_init then
                        v.has_explicit_super_init = self
                end
index d4f6cb9..522a347 100644 (file)
@@ -165,7 +165,7 @@ private class FlowVisitor
        fun merge_continues_to(before_loop: FlowContext, escapemark: nullable EscapeMark)
        do
                if escapemark == null then return
-               for b in escapemark.continues do
+               for b in escapemark.escapes do
                        var before = b.before_flow_context
                        if before == null then continue # Forward error
                        before_loop.add_loop(before)
@@ -175,7 +175,7 @@ private class FlowVisitor
        fun merge_breaks(escapemark: nullable EscapeMark)
        do
                if escapemark == null then return
-               for b in escapemark.breaks do
+               for b in escapemark.escapes do
                        var before = b.before_flow_context
                        if before == null then continue # Forward error
                        self.make_merge_flow(self.current_flow_context, before)
@@ -319,22 +319,7 @@ redef class AReturnExpr
        end
 end
 
-redef class AContinueExpr
-       # The flow just before it become unreachable
-       fun before_flow_context: nullable FlowContext
-       do
-               var after = self.after_flow_context
-               if after == null then return null
-               return after.previous.first
-       end
-       redef fun accept_flow_visitor(v)
-       do
-               super
-               v.make_unreachable_flow
-       end
-end
-
-redef class ABreakExpr
+redef class AEscapeExpr
        # The flow just before it become unreachable
        fun before_flow_context: nullable FlowContext
        do
@@ -361,7 +346,7 @@ redef class ADoExpr
        redef fun accept_flow_visitor(v)
        do
                super
-               v.merge_breaks(self.escapemark)
+               v.merge_breaks(self.break_mark)
        end
 end
 
@@ -411,10 +396,10 @@ redef class AWhileExpr
                var after_block = v.current_flow_context
 
                before_loop.add_loop(after_block)
-               v.merge_continues_to(after_block, self.escapemark)
+               v.merge_continues_to(after_block, self.continue_mark)
 
                v.current_flow_context = after_expr.when_false
-               v.merge_breaks(self.escapemark)
+               v.merge_breaks(self.break_mark)
        end
 end
 
@@ -428,10 +413,10 @@ redef class ALoopExpr
                var after_block = v.current_flow_context
 
                before_loop.add_loop(after_block)
-               v.merge_continues_to(after_block, self.escapemark)
+               v.merge_continues_to(after_block, self.continue_mark)
 
                v.make_unreachable_flow
-               v.merge_breaks(self.escapemark)
+               v.merge_breaks(self.break_mark)
        end
 end
 
@@ -447,10 +432,10 @@ redef class AForExpr
                var after_block = v.current_flow_context
 
                before_loop.add_loop(after_block)
-               v.merge_continues_to(after_block, self.escapemark)
+               v.merge_continues_to(after_block, self.continue_mark)
 
                v.make_merge_flow(v.current_flow_context, before_loop)
-               v.merge_breaks(self.escapemark)
+               v.merge_breaks(self.break_mark)
        end
 end
 
index 3039745..00702d0 100644 (file)
@@ -50,15 +50,12 @@ class EscapeMark
        # The name of the label (unless the mark is an anonymous loop mark)
        var name: nullable String
 
-       # Is the mark attached to a loop (loop, while, for)
-       # Such a mark is a candidate to a labelless 'continue' or 'break'
-       var for_loop: Bool
+       # The associated `continue` mark, if any.
+       # If the mark attached to a loop (loop, while, for), a distinct mark is used.
+       private var continue_mark: nullable EscapeMark = null
 
-       # Each 'continue' attached to the mark
-       var continues = new Array[AContinueExpr]
-
-       # Each 'break' attached to the mark
-       var breaks = new Array[ABreakExpr]
+       # Each break/continue attached to the mark
+       var escapes = new Array[AEscapeExpr]
 end
 
 # Visit a npropdef and:
@@ -81,7 +78,7 @@ private class ScopeVisitor
        end
 
        # All stacked scope. `scopes.first` is the current scope
-       private var scopes = new List[Scope]
+       var scopes = new List[Scope]
 
        # Shift and check the last scope
        fun shift_scope
@@ -176,7 +173,8 @@ private class ScopeVisitor
                else
                        name = null
                end
-               var res = new EscapeMark(name, for_loop)
+               var res = new EscapeMark(name)
+               if for_loop then res.continue_mark = new EscapeMark(name)
                return res
        end
 
@@ -292,43 +290,47 @@ redef class ASelfExpr
        end
 end
 
-redef class AContinueExpr
-       # The escape mark associated with the continue
+redef class AEscapeExpr
+       # The escape mark associated with the break/continue
        var escapemark: nullable EscapeMark
+end
+
+redef class AContinueExpr
        redef fun accept_scope_visitor(v)
        do
                super
                var escapemark = v.get_escapemark(self, self.n_label)
                if escapemark == null then return # Skip error
-               if not escapemark.for_loop then
+               escapemark = escapemark.continue_mark
+               if escapemark == null then
                        v.error(self, "Error: cannot 'continue', only 'break'.")
+                       return
                end
-               escapemark.continues.add(self)
+               escapemark.escapes.add(self)
                self.escapemark = escapemark
        end
 end
 
 redef class ABreakExpr
-       # The escape mark associated with the break
-       var escapemark: nullable EscapeMark
        redef fun accept_scope_visitor(v)
        do
                super
                var escapemark = v.get_escapemark(self, self.n_label)
                if escapemark == null then return # Skip error
-               escapemark.breaks.add(self)
+               escapemark.escapes.add(self)
                self.escapemark = escapemark
        end
 end
 
 
 redef class ADoExpr
-       # The escape mark associated with the 'do' block
-       var escapemark: nullable EscapeMark
+       # The break escape mark associated with the 'do' block
+       var break_mark: nullable EscapeMark
+
        redef fun accept_scope_visitor(v)
        do
-               self.escapemark = v.make_escape_mark(n_label, false)
-               v.enter_visit_block(n_block, self.escapemark)
+               self.break_mark = v.make_escape_mark(n_label, false)
+               v.enter_visit_block(n_block, self.break_mark)
        end
 end
 
@@ -342,24 +344,34 @@ redef class AIfExpr
 end
 
 redef class AWhileExpr
-       # The escape mark associated with the 'while'
-       var escapemark: nullable EscapeMark
+       # The break escape mark associated with the 'while'
+       var break_mark: nullable EscapeMark
+
+       # The continue escape mark associated with the 'while'
+       var continue_mark: nullable EscapeMark
+
        redef fun accept_scope_visitor(v)
        do
                var escapemark = v.make_escape_mark(n_label, true)
-               self.escapemark = escapemark
+               self.break_mark = escapemark
+               self.continue_mark = escapemark.continue_mark
                v.enter_visit(n_expr)
                v.enter_visit_block(n_block, escapemark)
        end
 end
 
 redef class ALoopExpr
-       # The escape mark associated with the 'loop'
-       var escapemark: nullable EscapeMark
+       # The break escape mark associated with the 'loop'
+       var break_mark: nullable EscapeMark
+
+       # The continue escape mark associated with the 'loop'
+       var continue_mark: nullable EscapeMark
+
        redef fun accept_scope_visitor(v)
        do
                var escapemark = v.make_escape_mark(n_label, true)
-               self.escapemark = escapemark
+               self.break_mark = escapemark
+               self.continue_mark = escapemark.continue_mark
                v.enter_visit_block(n_block, escapemark)
        end
 end
@@ -368,8 +380,11 @@ redef class AForExpr
        # The automatic variables in order
        var variables: nullable Array[Variable]
 
-       # The escape mark associated with the 'for'
-       var escapemark: nullable EscapeMark
+       # The break escape mark associated with the 'for'
+       var break_mark: nullable EscapeMark
+
+       # The continue escape mark associated with the 'for'
+       var continue_mark: nullable EscapeMark
 
        redef fun accept_scope_visitor(v)
        do
@@ -388,7 +403,8 @@ redef class AForExpr
                end
 
                var escapemark = v.make_escape_mark(n_label, true)
-               self.escapemark = escapemark
+               self.break_mark = escapemark
+               self.continue_mark = escapemark.continue_mark
                v.enter_visit_block(n_block, escapemark)
 
                v.shift_scope
index 95d7b3a..d3d4da7 100644 (file)
@@ -47,7 +47,7 @@ private class TypeVisitor
        # The analyzed property
        var mpropdef: nullable MPropDef
 
-       var selfvariable: Variable = new Variable("self")
+       var selfvariable = new Variable("self")
 
        # Is `self` use restricted?
        # * no explicit `self`
@@ -195,6 +195,40 @@ private class TypeVisitor
                return sup
        end
 
+       # Special verification on != and == for null
+       # Return true
+       fun null_test(anode: ABinopExpr)
+       do
+               var mtype = anode.n_expr.mtype
+               var mtype2 = anode.n_expr2.mtype
+
+               if mtype == null or mtype2 == null then return
+
+               if not mtype2 isa MNullType then return
+
+               # Check of useless null
+               if not mtype isa MNullableType then
+                       if not anchor_to(mtype) isa MNullableType then
+                               modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.")
+                       end
+                       return
+               end
+
+               # Check for type adaptation
+               var variable = anode.n_expr.its_variable
+               if variable == null then return
+
+               if anode isa AEqExpr then
+                       anode.after_flow_context.when_true.set_var(variable, mtype2)
+                       anode.after_flow_context.when_false.set_var(variable, mtype.mtype)
+               else if anode isa ANeExpr then
+                       anode.after_flow_context.when_false.set_var(variable, mtype2)
+                       anode.after_flow_context.when_true.set_var(variable, mtype.mtype)
+               else
+                       abort
+               end
+       end
+
        fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty
        do
                return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name)
@@ -304,6 +338,15 @@ private class TypeVisitor
                return callsite
        end
 
+       fun try_get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite
+       do
+               var unsafe_type = self.anchor_to(recvtype)
+               var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name)
+               if mproperty == null then return null
+               return get_method(node, recvtype, name, recv_is_self)
+       end
+
+
        # Visit the expressions of args and check their conformity with the corresponding type in signature
        # The point of this method is to handle varargs correctly
        # Note: The signature must be correctly adapted
@@ -334,11 +377,18 @@ private class TypeVisitor
                        self.visit_expr_subtype(args[j], paramtype)
                end
                if vararg_rank >= 0 then
-                       var varargs = new Array[AExpr]
                        var paramtype = msignature.mparameters[vararg_rank].mtype
-                       for j in [vararg_rank..vararg_rank+vararg_decl] do
-                               varargs.add(args[j])
-                               self.visit_expr_subtype(args[j], paramtype)
+                       var first = args[vararg_rank]
+                       if vararg_decl == 0 and first isa AVarargExpr then
+                               var mclass = get_mclass(node, "Array")
+                               if mclass == null then return false # Forward error
+                               var array_mtype = mclass.get_mtype([paramtype])
+                               self.visit_expr_subtype(first.n_expr, array_mtype)
+                               first.mtype  = first.n_expr.mtype
+                       else
+                               for j in [vararg_rank..vararg_rank+vararg_decl] do
+                                       self.visit_expr_subtype(args[j], paramtype)
+                               end
                        end
                end
                return true
@@ -386,7 +436,6 @@ private class TypeVisitor
        fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType
        do
                if col.length == 1 then return col.first
-               var res = new Array[nullable MType]
                for t1 in col do
                        if t1 == null then continue # return null
                        var found = true
@@ -452,8 +501,8 @@ end
 
 redef class FlowContext
        # Store changes of types because of type evolution
-       private var vars: HashMap[Variable, nullable MType] = new HashMap[Variable, nullable MType]
-       private var cache: HashMap[Variable, nullable Array[nullable MType]] = new HashMap[Variable, nullable Array[nullable MType]]
+       private var vars = new HashMap[Variable, nullable MType]
+       private var cache = new HashMap[Variable, nullable Array[nullable MType]]
 
        # Adapt the variable to a static type
        # Warning1: do not modify vars directly.
@@ -738,7 +787,7 @@ redef class AContinueExpr
        do
                var nexpr = self.n_expr
                if nexpr != null then
-                       var mtype = v.visit_expr(nexpr)
+                       v.visit_expr(nexpr)
                end
                self.is_typed = true
        end
@@ -749,7 +798,7 @@ redef class ABreakExpr
        do
                var nexpr = self.n_expr
                if nexpr != null then
-                       var mtype = v.visit_expr(nexpr)
+                       v.visit_expr(nexpr)
                end
                self.is_typed = true
        end
@@ -762,9 +811,9 @@ redef class AReturnExpr
                var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype
                if nexpr != null then
                        if ret_type != null then
-                               var mtype = v.visit_expr_subtype(nexpr, ret_type)
+                               v.visit_expr_subtype(nexpr, ret_type)
                        else
-                               var mtype = v.visit_expr(nexpr)
+                               v.visit_expr(nexpr)
                                v.error(self, "Error: Return with value in a procedure.")
                        end
                else if ret_type != null then
@@ -846,6 +895,10 @@ redef class AForExpr
        var method_item: nullable CallSite
        var method_next: nullable CallSite
        var method_key: nullable CallSite
+       var method_finish: nullable CallSite
+
+       var method_lt: nullable CallSite
+       var method_successor: nullable CallSite
 
        private fun do_type_iterator(v: TypeVisitor, mtype: MType)
        do
@@ -937,6 +990,8 @@ redef class AForExpr
                end
                self.method_next = nextdef
 
+               self.method_finish = v.try_get_method(self, ittype, "finish", false)
+
                if is_map then
                        var keydef = v.get_method(self, ittype, "key", false)
                        if keydef == null then
@@ -945,6 +1000,19 @@ redef class AForExpr
                        end
                        self.method_key = keydef
                end
+
+               if self.variables.length == 1 and n_expr isa ARangeExpr then
+                       var variable = variables.first
+                       var vtype = variable.declared_type.as(not null)
+
+                       if n_expr isa AOrangeExpr then
+                               self.method_lt = v.get_method(self, vtype, "<", false)
+                       else
+                               self.method_lt = v.get_method(self, vtype, "<=", false)
+                       end
+
+                       self.method_successor = v.get_method(self, vtype, "successor", false)
+               end
        end
 
        redef fun accept_typing(v)
@@ -1195,9 +1263,9 @@ redef class AIsaExpr
 
                var variable = self.n_expr.its_variable
                if variable != null then
-                       var orig = self.n_expr.mtype
-                       var from = if orig != null then orig.to_s else "invalid"
-                       var to = if mtype != null then mtype.to_s else "invalid"
+                       #var orig = self.n_expr.mtype
+                       #var from = if orig != null then orig.to_s else "invalid"
+                       #var to = if mtype != null then mtype.to_s else "invalid"
                        #debug("adapt {variable}: {from} -> {to}")
                        self.after_flow_context.when_true.set_var(variable, mtype)
                end
@@ -1331,16 +1399,7 @@ redef class AEqExpr
        redef fun accept_typing(v)
        do
                super
-
-               var variable = self.n_expr.its_variable
-               if variable == null then return
-               var mtype = self.n_expr2.mtype
-               if not mtype isa MNullType then return
-               var vartype = v.get_variable(self, variable)
-               if not vartype isa MNullableType then return
-               self.after_flow_context.when_true.set_var(variable, mtype)
-               self.after_flow_context.when_false.set_var(variable, vartype.mtype)
-               #debug("adapt {variable}:{vartype} ; true->{mtype} false->{vartype.mtype}")
+               v.null_test(self)
        end
 end
 redef class ANeExpr
@@ -1348,16 +1407,7 @@ redef class ANeExpr
        redef fun accept_typing(v)
        do
                super
-
-               var variable = self.n_expr.its_variable
-               if variable == null then return
-               var mtype = self.n_expr2.mtype
-               if not mtype isa MNullType then return
-               var vartype = v.get_variable(self, variable)
-               if not vartype isa MNullableType then return
-               self.after_flow_context.when_false.set_var(variable, mtype)
-               self.after_flow_context.when_true.set_var(variable, vartype.mtype)
-               #debug("adapt {variable}:{vartype} ; true->{vartype.mtype} false->{mtype}")
+               v.null_test(self)
        end
 end
 redef class ALtExpr
@@ -1758,6 +1808,16 @@ redef class AIssetAttrExpr
        end
 end
 
+redef class AVarargExpr
+       redef fun accept_typing(v)
+       do
+               # This kind of pseudo-expression can be only processed trough a signature
+               # See `check_signature`
+               # Other cases are a syntax error.
+               v.error(self, "Syntax error: unexpected `...`")
+       end
+end
+
 ###
 
 redef class ADebugTypeExpr
index 9f31805..566e014 100644 (file)
@@ -136,7 +136,7 @@ class NitUnitExecutor
                var res = sys.system(cmd)
                var res2 = 0
                if res == 0 then
-                       res2 = sys.system("./{file}.bin >>'{file}.out1' 2>&1 </dev/null")
+                       res2 = sys.system("{file.to_program_name}.bin >>'{file}.out1' 2>&1 </dev/null")
                end
 
                var msg
@@ -200,7 +200,6 @@ redef class ModelBuilder
                if not mmodule2nmodule.has_key(mmodule) then return ts
 
                var nmodule = mmodule2nmodule[mmodule]
-               assert nmodule != null
 
                # usualy, only the original module must be imported in the unit test.
                var o = mmodule
index 25b1fc8..5936038 100644 (file)
@@ -244,7 +244,7 @@ class TestCase
                if toolcontext.opt_noact.value then return
                # execute
                var file = test_file
-               var res = sys.system("./{file}.bin > '{file}.out1' 2>&1 </dev/null")
+               var res = sys.system("{file.to_program_name}.bin > '{file}.out1' 2>&1 </dev/null")
                var f = new IFStream.open("{file}.out1")
                var msg = f.read_all
                f.close
index 33322d2..e174145 100644 (file)
@@ -103,7 +103,7 @@ class ToolContext
 
        # Messages
        private var messages = new Array[Message]
-       private var message_sorter = new ComparableSorter[Message]
+       private var message_sorter: Comparator = default_comparator
 
        # Output all current stacked messages.
        # If some errors occurred, exits the program.
@@ -267,12 +267,15 @@ class ToolContext
        # Option --bash-completion
        var opt_bash_completion = new OptionBool("Generate bash_completion file for this program", "--bash-completion")
 
+       # Option --stub-man
+       var opt_stub_man = new OptionBool("Generate a stub manpage in pandoc markdown format", "--stub-man")
+
        # Verbose level
        var verbose_level: Int = 0
 
        init
        do
-               option_context.add_option(opt_warn, opt_warning, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion)
+               option_context.add_option(opt_warn, opt_warning, opt_quiet, opt_stop_on_first_error, opt_no_color, opt_log, opt_log_dir, opt_help, opt_version, opt_set_dummy_tool, opt_verbose, opt_bash_completion, opt_stub_man)
        end
 
        # Name, usage and synopsis of the tool.
@@ -323,6 +326,37 @@ class ToolContext
                        exit 0
                end
 
+               if opt_stub_man.value then
+                       print """
+% {{{toolname.to_upper}}}(1)
+
+# NAME
+
+{{{tooldescription.split("\n")[1]}}}
+
+# SYNOPSYS
+
+{{{toolname}}} [*options*]...
+
+# OPTIONS
+"""
+                       for o in option_context.options do
+                               var first = true
+                               for n in o.names do
+                                       if first then first = false else printn ", "
+                                       printn "`{n}`"
+                               end
+                               print ""
+                               print ":   {o.helptext}"
+                               print ""
+                       end
+                       print """
+# SEE ALSO
+
+The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>"""
+                       exit 0
+               end
+
                var errors = option_context.get_errors
                if not errors.is_empty then
                        for e in errors do print "Error: {e}"
@@ -438,7 +472,7 @@ class BashCompletion
                addn "  COMPREPLY=()"
                addn "  cur=\"$\{COMP_WORDS[COMP_CWORD]\}\""
                addn "  prev=\"$\{COMP_WORDS[COMP_CWORD-1]\}\""
-               if option_names != null then
+               if not option_names.is_empty then
                        addn "  opts=\"{option_names.join(" ")}\""
                        addn "  if [[ $\{cur\} == -* ]] ; then"
                        addn "          COMPREPLY=( $(compgen -W \"$\{opts\}\" -- $\{cur\}) )"
index 6a209c3..6c8208a 100644 (file)
@@ -19,9 +19,19 @@ module transform
 import astbuilder
 import astvalidation
 import semantize
+intrude import semantize::scope
 
 redef class ToolContext
        var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase])
+
+       # --no-shortcut-range
+       var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range")
+
+       redef init
+       do
+               super
+               self.option_context.add_option(self.opt_no_shortcut_range)
+       end
 end
 
 private class TransformPhase
@@ -151,14 +161,113 @@ end
 redef class AWhileExpr
        redef fun accept_transform_visitor(v)
        do
-               # TODO
+               var nloop = v.builder.make_loop
+               var nif = v.builder.make_if(n_expr, null)
+               nloop.add nif
+
+               var nblock = n_block
+               if nblock != null then nif.n_then.add nblock
+
+               var escapemark = self.break_mark.as(not null)
+               var nbreak = v.builder.make_break(escapemark)
+               nif.n_else.add nbreak
+
+               nloop.break_mark = self.break_mark
+               nloop.continue_mark = self.continue_mark
+
+               replace_with(nloop)
        end
 end
 
 redef class AForExpr
        redef fun accept_transform_visitor(v)
        do
-               # TODO
+               var escapemark = self.break_mark
+               assert escapemark != null
+
+               var nblock = v.builder.make_block
+
+               var nexpr = n_expr
+
+               # Shortcut on explicit range
+               # Avoid the instantiation of the range and the iterator
+               if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then
+                       var variable = variables.first
+                       nblock.add v.builder.make_var_assign(variable, nexpr.n_expr)
+                       var to = nexpr.n_expr2
+                       nblock.add to
+
+                       var nloop = v.builder.make_loop
+                       nloop.break_mark = escapemark
+                       nblock.add nloop
+
+                       var is_ok = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_lt.as(not null), [to.make_var_read])
+
+                       var nif = v.builder.make_if(is_ok, null)
+                       nloop.add nif
+
+                       var nthen = nif.n_then
+                       var ndo = v.builder.make_do
+                       ndo.break_mark = escapemark.continue_mark
+                       nthen.add ndo
+
+                       ndo.add self.n_block.as(not null)
+
+                       var one = v.builder.make_int(1)
+                       var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one])
+                       nthen.add v.builder.make_var_assign(variable, succ)
+
+                       var nbreak = v.builder.make_break(escapemark)
+                       nif.n_else.add nbreak
+
+                       replace_with(nblock)
+                       return
+               end
+
+               nblock.add nexpr
+
+               var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null)
+               nblock.add iter
+
+               var nloop = v.builder.make_loop
+               nloop.break_mark = escapemark
+               nblock.add nloop
+
+               var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null)
+
+               var nif = v.builder.make_if(is_ok, null)
+               nloop.add nif
+
+               var nthen = nif.n_then
+               var ndo = v.builder.make_do
+               ndo.break_mark = escapemark.continue_mark
+               nthen.add ndo
+
+               if self.variables.length == 1 then
+                       var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
+                       ndo.add v.builder.make_var_assign(variables.first, item)
+               else if self.variables.length == 2 then
+                       var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null)
+                       ndo.add v.builder.make_var_assign(variables[0], key)
+                       var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null)
+                       ndo.add v.builder.make_var_assign(variables[1], item)
+               else
+                       abort
+               end
+
+               ndo.add self.n_block.as(not null)
+
+               nthen.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null)
+
+               var nbreak = v.builder.make_break(escapemark)
+               nif.n_else.add nbreak
+
+               var method_finish = self.method_finish
+               if method_finish != null then
+                       nblock.add v.builder.make_call(iter.make_var_read, method_finish, null)
+               end
+
+               replace_with(nblock)
        end
 end
 
index 0779a6a..3807ab7 100644 (file)
@@ -106,7 +106,6 @@ class VirtualMachine super NaiveInterpreter
                # Sub can be discovered inside a Generic type during the subtyping test
                if not sub.mclass.loaded then create_class(sub.mclass)
 
-               if anchor == null then anchor = sub
                if sup isa MGenericType then
                        var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass)
                        assert sub2.mclass == sup.mclass
diff --git a/tests/base_for_finish.nit b/tests/base_for_finish.nit
new file mode 100644 (file)
index 0000000..baaf29e
--- /dev/null
@@ -0,0 +1,60 @@
+# 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.
+
+import array
+
+class MyC
+       var data: Collection[Object]
+       fun iterator: MyI do return new MyI(data.iterator)
+end
+
+class MyI
+       super Iterator[Object]
+       var iter: Iterator[Object]
+       redef fun is_ok do return iter.is_ok
+       redef fun item do return iter.item
+       redef fun next do iter.next
+       redef fun finish do 0.output
+end
+
+fun test(a: MyC)
+do
+       for x in a do
+               x.output
+               for y in [10,20] do
+                       y.output
+                       if x == 2 then return
+               end
+               100.output
+       end
+       200.output
+end
+
+var a = new MyC([1,2,3])
+
+for x in a do
+       x.output
+end
+
+'\n'.output
+
+for x in a do
+       x.output
+       if x == 2 then break
+       100.output
+end
+
+'\n'.output
+
+test(a)
diff --git a/tests/base_init_auto_refine.nit b/tests/base_init_auto_refine.nit
new file mode 100644 (file)
index 0000000..afa534f
--- /dev/null
@@ -0,0 +1,22 @@
+# 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.
+
+import base_init_auto
+
+redef class A
+       redef init do 'A'.output
+end
+
+var a = new A(2)
+a.work
diff --git a/tests/base_vararg3.nit b/tests/base_vararg3.nit
new file mode 100644 (file)
index 0000000..6918fef
--- /dev/null
@@ -0,0 +1,45 @@
+# 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.
+
+import array
+
+class A
+       fun foo(a: Int...) do a.first.output
+end
+
+class B
+       super A
+       redef fun foo(a)
+       do
+               'a'.output
+               super
+               'b'.output
+               super(a...)
+               'c'.output
+               super(4,5,6)
+               'd'.output
+               super([5,6,7]...)
+               #alt3#super(a)
+               #alt4#super(1...)
+       end
+end
+
+var a = new A
+a.foo(1,2,3)
+a.foo([2,3,4]...)
+#alt1#a.foo([1,2,3])
+#alt2#a.foo(1...)
+
+var b = new B
+b.foo(3,4,5)
index 116d7d7..2eef2cb 100644 (file)
@@ -74,8 +74,9 @@ class E
 end
 
 class EltComparator
-       super Comparator[Elt]
-       redef fun compare(a: Elt, b: Elt): Int
+       super Comparator
+       redef type COMPARED: Elt
+       redef fun compare(a, b)
        do
                if _is_val1 then
                        return a.val1 <=> b.val1
index 32b661c..ae839ed 100644 (file)
@@ -16,7 +16,8 @@
 
 
 class BackIntComparator
-       super Comparator[Int]
+       super Comparator
+       redef type COMPARED: Int
        redef fun compare(a: Int, b: Int): Int
        do
                return b <=> a
@@ -26,7 +27,8 @@ class BackIntComparator
 end
 
 class DecimalComparator
-       super Comparator[Int]
+       super Comparator
+       redef type COMPARED: Int
        redef fun compare(a: Int, b: Int): Int
        do
                return (a%10) <=> (b%10)
@@ -51,7 +53,7 @@ end
 
 var q = get_an_array(50)
 print(q.join(" "))
-(new DefaultComparator[Int]).sort(q)
+(default_comparator).sort(q)
 print(q.join(" "))
 (new DecimalComparator).sort(q)
 print(q.join(" "))
index 3011fc1..0c14c1c 100644 (file)
@@ -1,3 +1,4 @@
 --log --log-dir out/test_nitc_logs ../examples/hello_world.nit
 base_simple3.nit
 -m test_mixin.nit ../examples/hello_world.nit
+test_define.nit -D text=hello -D num=42 -D flag
index 3b38c54..6a47146 100644 (file)
@@ -5,3 +5,4 @@
 --global ../examples/hello_world.nit -m test_mixin.nit -o out/nitg-hello_world_mixed ; out/nitg-hello_world_mixed
 --separate ../examples/hello_world.nit -m test_mixin.nit -o out/nitgs-hello_world_mixed ; out/nitgs-hello_world_mixed
 base_simple_import.nit base_simple.nit --dir out/ ; out/base_simple ; out/base_simple_import
+test_define.nit -D text=hello -D num=42 -D flag --dir out/ ; out/test_define
index c052cff..baf5b76 100644 (file)
@@ -5,6 +5,7 @@ shoot_logic
 bench_
 nit_args1
 nit_args3
+nit_args4
 nitvm_args1
 nitvm_args3
 nitc_args1
@@ -12,6 +13,7 @@ nitg_args1
 nitg_args3
 nitg_args5
 nitg_args6
+nitg_args8
 test_markdown_args1
 pep8analysis
 nitcc_parser_gen
index 41a85bf..b4226bc 100644 (file)
@@ -1,3 +1,5 @@
+base_eq_null_notnull.nit:36,6--14: Warning: expression is not null, since it is a `A`.
+base_eq_null_notnull.nit:43,2--10: Warning: expression is not null, since it is a `A`.
 true
 true
 true
diff --git a/tests/sav/base_for_finish.res b/tests/sav/base_for_finish.res
new file mode 100644 (file)
index 0000000..07ea0b2
--- /dev/null
@@ -0,0 +1,17 @@
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
+0
diff --git a/tests/sav/base_init_auto_refine.res b/tests/sav/base_init_auto_refine.res
new file mode 100644 (file)
index 0000000..445bff9
--- /dev/null
@@ -0,0 +1,2 @@
+A2
+-2
index a556a0a..46069e8 100644 (file)
@@ -1,3 +1,4 @@
+base_var_type_evolution_null3.nit:52,5--13: Warning: expression is not null, since it is a `Object`.
 1
 1
 5
index a556a0a..a10a5e3 100644 (file)
@@ -1,3 +1,4 @@
+alt/base_var_type_evolution_null3_alt1.nit:52,5--13: Warning: expression is not null, since it is a `Object`.
 1
 1
 5
diff --git a/tests/sav/base_vararg3.res b/tests/sav/base_vararg3.res
new file mode 100644 (file)
index 0000000..7d434e8
--- /dev/null
@@ -0,0 +1,6 @@
+1
+2
+a3
+b3
+c4
+d5
diff --git a/tests/sav/base_vararg3_alt1.res b/tests/sav/base_vararg3_alt1.res
new file mode 100644 (file)
index 0000000..1e24ee6
--- /dev/null
@@ -0,0 +1 @@
+alt/base_vararg3_alt1.nit:41,7--13: Type error: expected Int, got Array[Int]
diff --git a/tests/sav/base_vararg3_alt2.res b/tests/sav/base_vararg3_alt2.res
new file mode 100644 (file)
index 0000000..52bce29
--- /dev/null
@@ -0,0 +1 @@
+alt/base_vararg3_alt2.nit:42,7: Type error: expected Array[Int], got Int
diff --git a/tests/sav/base_vararg3_alt3.res b/tests/sav/base_vararg3_alt3.res
new file mode 100644 (file)
index 0000000..e7e44bc
--- /dev/null
@@ -0,0 +1 @@
+alt/base_vararg3_alt3.nit:33,9: Type error: expected Int, got Array[Int]
diff --git a/tests/sav/base_vararg3_alt4.res b/tests/sav/base_vararg3_alt4.res
new file mode 100644 (file)
index 0000000..caca43b
--- /dev/null
@@ -0,0 +1 @@
+alt/base_vararg3_alt4.nit:34,9: Type error: expected Array[Int], got Int
diff --git a/tests/sav/nit_args4.res b/tests/sav/nit_args4.res
new file mode 100644 (file)
index 0000000..483d841
--- /dev/null
@@ -0,0 +1,3 @@
+hello
+42
+true
diff --git a/tests/sav/nitg-e/fixme/base_for_finish.res b/tests/sav/nitg-e/fixme/base_for_finish.res
new file mode 100644 (file)
index 0000000..390c4e4
--- /dev/null
@@ -0,0 +1,16 @@
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
diff --git a/tests/sav/nitg-g/fixme/base_for_finish.res b/tests/sav/nitg-g/fixme/base_for_finish.res
new file mode 100644 (file)
index 0000000..390c4e4
--- /dev/null
@@ -0,0 +1,16 @@
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
diff --git a/tests/sav/nitg-s/fixme/base_for_finish.res b/tests/sav/nitg-s/fixme/base_for_finish.res
new file mode 100644 (file)
index 0000000..390c4e4
--- /dev/null
@@ -0,0 +1,16 @@
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
diff --git a/tests/sav/nitg-sg/fixme/base_for_finish.res b/tests/sav/nitg-sg/fixme/base_for_finish.res
new file mode 100644 (file)
index 0000000..390c4e4
--- /dev/null
@@ -0,0 +1,16 @@
+1
+2
+3
+0
+
+1
+100
+2
+0
+
+1
+10
+20
+100
+2
+10
diff --git a/tests/sav/nitg_args8.res b/tests/sav/nitg_args8.res
new file mode 100644 (file)
index 0000000..483d841
--- /dev/null
@@ -0,0 +1,3 @@
+hello
+42
+true
diff --git a/tests/sav/niti/error_needed_method_alt3.res b/tests/sav/niti/error_needed_method_alt3.res
deleted file mode 100644 (file)
index 34dd8c9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-alt/error_needed_method_alt3.nit:48,9--13: Fatal Error: NativeString must have a property named to_s.
index 4daa384..d1e2d76 100644 (file)
@@ -1 +1 @@
-alt/error_needed_method_alt4.nit:49,10--14: Fatal Error: NativeString must have a property named to_s.
+alt/error_needed_method_alt4.nit:49,10--14: Fatal Error: NativeString must have a property named to_s_with_length.
index e706f2c..481e468 100644 (file)
 ## Module importation hierarchy
 Number of nodes: 1
 Number of edges: 1 (1.00 per node)
-Number of direct edges: 0 (0.0 per node)
+Number of direct edges: 0 (0.00 per node)
 Distribution of greaters
  population: 1
  minimum value: 1
@@ -271,7 +271,7 @@ Distribution of direct greaters
  minimum value: 0
  maximum value: 0
  total value: 0
- average value: 0.0
+ average value: 0.00
  distribution:
   <=0: sub-population=1 (100.00%); cumulated value=0 (na%)
 Distribution of smallers
@@ -287,7 +287,7 @@ Distribution of direct smallers
  minimum value: 0
  maximum value: 0
  total value: 0
- average value: 0.0
+ average value: 0.00
  distribution:
   <=0: sub-population=1 (100.00%); cumulated value=0 (na%)
 ## Classdef hierarchy
@@ -310,7 +310,7 @@ Distribution of direct greaters
  total value: 6
  average value: 0.85
  distribution:
-  <=0: sub-population=1 (14.28%); cumulated value=0 (0.0%)
+  <=0: sub-population=1 (14.28%); cumulated value=0 (0.00%)
   <=1: sub-population=6 (85.71%); cumulated value=6 (100.00%)
 Distribution of smallers
  population: 7
@@ -328,7 +328,7 @@ Distribution of direct smallers
  total value: 6
  average value: 0.85
  distribution:
-  <=0: sub-population=6 (85.71%); cumulated value=0 (0.0%)
+  <=0: sub-population=6 (85.71%); cumulated value=0 (0.00%)
   <=8: sub-population=1 (14.28%); cumulated value=6 (100.00%)
 ## Class hierarchy
 Number of nodes: 7
@@ -350,7 +350,7 @@ Distribution of direct greaters
  total value: 6
  average value: 0.85
  distribution:
-  <=0: sub-population=1 (14.28%); cumulated value=0 (0.0%)
+  <=0: sub-population=1 (14.28%); cumulated value=0 (0.00%)
   <=1: sub-population=6 (85.71%); cumulated value=6 (100.00%)
 Distribution of smallers
  population: 7
@@ -368,7 +368,7 @@ Distribution of direct smallers
  total value: 6
  average value: 0.85
  distribution:
-  <=0: sub-population=6 (85.71%); cumulated value=0 (0.0%)
+  <=0: sub-population=6 (85.71%); cumulated value=0 (0.00%)
   <=8: sub-population=1 (14.28%); cumulated value=6 (100.00%)
 --- AST Metrics ---
 ## All nodes of the AST
@@ -449,8 +449,8 @@ Number of classes: 7
   Number of class kind: 4 (57.14%)
 
 Number of class definitions: 7
-Number of refined classes: 0 (0.0%)
-Average number of class refinments by classes: 0.0
+Number of refined classes: 0 (0.00%)
+Average number of class refinments by classes: 0.00
 Average number of class refinments by refined classes: na
 
 Number of properties: 18
@@ -714,8 +714,8 @@ Statistics of type usage:
          sum: 0
 --- Sends on Nullable Receiver ---
 Total number of sends: 19
-Number of sends on a nullable receiver: 0 (0.0%)
-Number of buggy sends (cannot determine the type of the receiver): 0 (0.0%)
+Number of sends on a nullable receiver: 0 (0.00%)
+Number of buggy sends (cannot determine the type of the receiver): 0 (0.00%)
 
 # RTA metrics
 
diff --git a/tests/sav/test_define.res b/tests/sav/test_define.res
new file mode 100644 (file)
index 0000000..8d40171
--- /dev/null
@@ -0,0 +1,3 @@
+some text
+1
+false
index 51ef296..f2856be 100644 (file)
@@ -1 +1,4 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
+f is closed? false
+f is closed? true
+* ** **** *******
index 9748c49..b52a9c5 100644 (file)
@@ -6,9 +6,9 @@ false
 ~~~Hash statistics~~~
 GET:
 number of get and has_key: 1
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
-average length of considered collections: 0.0
+average length of considered collections: 0.00
 average capacity of considered collections: 1.00 (NA%)
 
 STORE:
@@ -21,47 +21,47 @@ average capacity or considered collections: NA (NA%)
 ~~~Hash statistics~~~
 GET:
 number of get and has_key: 2
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
-average length of considered collections: 0.0
+average length of considered collections: 0.00
 average capacity of considered collections: 1.00 (NA%)
 
 STORE:
 number of stores: 1
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
-average length of considered collections: 0.0
+average length of considered collections: 0.00
 average capacity or considered collections: 1.00 (NA%)
 ~~~~~~
 ~~~Hash statistics~~~
 GET:
 number of get and has_key: 3
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
 average length of considered collections: 0.33
 average capacity of considered collections: 6.33 (1900.00%)
 
 STORE:
 number of stores: 1
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
-average length of considered collections: 0.0
+average length of considered collections: 0.00
 average capacity or considered collections: 1.00 (NA%)
 ~~~~~~
 true
 ~~~Hash statistics~~~
 GET:
 number of get and has_key: 4
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
 average length of considered collections: 0.50
 average capacity of considered collections: 9.00 (1800.00%)
 
 STORE:
 number of stores: 1
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
-average length of considered collections: 0.0
+average length of considered collections: 0.00
 average capacity or considered collections: 1.00 (NA%)
 ~~~~~~
 
@@ -70,22 +70,22 @@ false
 ~~~Hash statistics~~~
 GET:
 number of get and has_key: 5
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
 average length of considered collections: 0.60
 average capacity of considered collections: 10.60 (1766.67%)
 
 STORE:
 number of stores: 1
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
-average length of considered collections: 0.0
+average length of considered collections: 0.00
 average capacity or considered collections: 1.00 (NA%)
 ~~~~~~
 ~~~Hash statistics~~~
 GET:
 number of get and has_key: 6
-number of collisions: 0 (0.0%)
+number of collisions: 0 (0.00%)
 average length of collisions: NA
 average length of considered collections: 0.67
 average capacity of considered collections: 11.67 (1750.00%)
index 5e997e3..800327a 100644 (file)
@@ -5,7 +5,7 @@ _DUMMY_TOOL()
        COMPREPLY=()
        cur="${COMP_WORDS[COMP_CWORD]}"
        prev="${COMP_WORDS[COMP_CWORD-1]}"
-       opts="--warn --warning --quiet --stop-on-first-error --no-color --log --log-dir --help --version --set-dummy-tool --verbose --bash-completion --option-a --option-b"
+       opts="--warn --warning --quiet --stop-on-first-error --no-color --log --log-dir --help --version --set-dummy-tool --verbose --bash-completion --stub-man --option-a --option-b"
        if [[ ${cur} == -* ]] ; then
                COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
                return 0
index 8757721..91fa2fa 100644 (file)
@@ -12,6 +12,7 @@ Test for ToolContext, try --bash-completion.
   --set-dummy-tool        Set toolname and version to DUMMY. Useful for testing
   -v, --verbose           Verbose
   --bash-completion       Generate bash_completion file for this program
+  --stub-man              Generate a stub manpage in pandoc markdown format
   -a, --option-a          option a, do nothing
   -b, --option-b          option b, do nothing
   -c                      option c, do nothing
diff --git a/tests/test_define.nit b/tests/test_define.nit
new file mode 100644 (file)
index 0000000..4b8ebc7
--- /dev/null
@@ -0,0 +1,20 @@
+# 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.
+
+fun text: String do return "some text"
+fun num: Int do return 1
+fun flag: Bool do return false
+print text
+print num
+print flag
index 545440b..cae1cd1 100644 (file)
 
 import for_abuse
 
+var escape_f: nullable IStream = null
 for f in file_open("test_for_abuse.nit") do
+       escape_f = f
        print f.read_line
+       print "f is closed? {f.eof}"
 end
+print "f is closed? {escape_f.eof}"
 
 var array = ["*", "****", "**", "*******"]
 for q in array.sort_fa do
-       q.res = q.b.length <=> q.b.length
+       # IN:
+       #     q.a
+       #     q-b
+       # OUT
+       #     q.res
+       q.res = q.a.length <=> q.b.length
 end
-
+print array.join(" ")
index b070f63..8b49b8b 100644 (file)
@@ -128,15 +128,10 @@ if test.address_is_null then env.print_error("object test not initialized")
 
 # Retrieve field value with field ids
 var v_bool = env.get_boolean_field(test, f_bool)
-if v_bool == null then env.print_error("vbool not found")
 var v_char = env.get_char_field(test, f_char)
-if v_char == null then env.print_error("vchar not found")
 var v_i = env.get_int_field(test, f_i)
-if v_i == null then env.print_error("vi not found")
 var v_f = env.get_float_field(test, f_f)
-if v_f == null then env.print_error("vf not found")
 var v_test1 = env.get_object_field(test, f_test)
-if v_test1 == null then env.print_error("vtest1 not found")
 
 # Set the new values for the fields
 env.set_boolean_field(test, f_bool, true)
@@ -151,7 +146,6 @@ v_char = env.call_char_method(test, m_char, null)
 v_i = env.call_int_method(test, m_i, null)
 v_f = env.call_float_method(test, m_f, null)
 var v_test2 = env.call_object_method(test, m_test, null)
-if v_test2 == null then env.print_error("vtest2 not found")
 
 # assert the values of the fields
 print v_bool
index 32afdf0..14be6d4 100755 (executable)
@@ -36,6 +36,9 @@ paths=($paths)
 JNI_LIB_PATH=${paths[0]}
 shopt -u nullglob
 
+outdir="out"
+compdir=".nit_compile"
+
 usage()
 {
        e=`basename "$0"`
@@ -46,9 +49,74 @@ Usage: $e [options] modulenames
 -h          This help
 --engine    Use a specific engine (default=nitg)
 --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)
+--node      Run as a node in parallel, will not output context information
 END
 }
 
+# Run a command with a timeout and a time count.
+# Options:
+#   -o file    write the user time into file (REQUIRED). see `-o` in `man time`
+#   -a         append the time to the file (instead of overwriting it). see `-a` in `man time`
+saferun()
+{
+       local stop=false
+       local o=
+       local a=
+       while [ $stop = false ]; do
+               case $1 in
+                       -o) o="$2"; shift; shift;;
+                       -a) a="-a"; shift;;
+                       *) stop=true
+               esac
+       done
+       if test -n "$TIME"; then
+               $TIME -o "$o" $a $TIMEOUT "$@"
+       else
+               $TIMEOUT "$@"
+               if test -n "$a"; then echo 0 >> "$o"; else echo 0 > "$o"; fi
+       fi
+}
+
+# Output a timestamp attribute for XML, or an empty line
+timestamp()
+{
+       if test -n "$TIMESTAMP"; then
+               echo "timestamp='`$TIMESTAMP`'"
+       else
+               echo ""
+       fi
+
+}
+
+# Get platform specific commands ##########################
+
+# Detect a working timeout
+if sh -c "timelimit echo" 1>/dev/null 2>&1; then
+       TIMEOUT="timelimit -t 600"
+elif sh -c "timeout 1 echo" 1>/dev/null 2>&1; then
+       TIMEOUT="timeout 600s"
+else
+       echo "No timelimit or timeout command detected. Tests may hang :("
+fi
+
+# Detect a working time command
+if env time --quiet -f%U true 2>/dev/null; then
+       TIME="env time --quiet -f%U"
+elif env time -f%U true 2>/dev/null; then
+       TIME="env time -f%U"
+else
+       TIME=
+fi
+
+# Detect a working date command
+if date -Iseconds >/dev/null 2>&1; then
+       TIMESTAMP="date -Iseconds"
+else
+       TIMESTAMP=
+fi
+
 # $1 is the pattern of the test
 # $2 is the file to compare to
 # the result is:
@@ -62,15 +130,15 @@ function compare_to_result()
        local sav="$2"
        if [ ! -r "$sav" ]; then return 0; fi
        test "`cat "$sav"`" = "UNDEFINED" && return 1
-       diff -u "$sav" "out/$pattern.res" > "out/$pattern.diff.sav.log"
+       diff -u "$sav" "$outdir/$pattern.res" > "$outdir/$pattern.diff.sav.log"
        if [ "$?" == 0 ]; then
                return 1
        fi
-       sed '/[Ww]arning/d;/[Ee]rror/d' "out/$pattern.res" > "out/$pattern.res2"
-       sed '/[Ww]arning/d;/[Ee]rror/d' "$sav" > "out/$pattern.sav2"
-       grep '[Ee]rror' "out/$pattern.res" >/dev/null && echo "Error" >> "out/$pattern.res2"
-       grep '[Ee]rror' "$sav" >/dev/null && echo "Error" >> "out/$pattern.sav2"
-       diff -u "out/$pattern.sav2" "out/$pattern.res2" > "out/$pattern.diff.sav.log2"
+       sed '/[Ww]arning/d;/[Ee]rror/d' "$outdir/$pattern.res" > "$outdir/$pattern.res2"
+       sed '/[Ww]arning/d;/[Ee]rror/d' "$sav" > "$outdir/$pattern.sav2"
+       grep '[Ee]rror' "$outdir/$pattern.res" >/dev/null && echo "Error" >> "$outdir/$pattern.res2"
+       grep '[Ee]rror' "$sav" >/dev/null && echo "Error" >> "$outdir/$pattern.sav2"
+       diff -u "$outdir/$pattern.sav2" "$outdir/$pattern.res2" > "$outdir/$pattern.diff.sav.log2"
        if [ "$?" == 0 ]; then
                return 2
        else
@@ -96,7 +164,7 @@ function process_result()
        OLD=""
        LIST=""
        FIRST=""
-       echo >>$xml "<testcase classname='$pack' name='$description' time='`cat out/$pattern.time.out`' timestamp='`date -Iseconds`'>"
+       echo >>$xml "<testcase classname='$pack' name='$description' time='`cat $outdir/$pattern.time.out`' `timestamp`>"
        #for sav in "sav/$engine/fixme/$pattern.res" "sav/$engine/$pattern.res" "sav/fixme/$pattern.res" "sav/$pattern.res" "sav/$pattern.sav"; do
        for savdir in $savdirs; do
                sav=$savdir/fixme/$pattern.res
@@ -152,79 +220,79 @@ function process_result()
                esac
        done
        OLD=`echo "$OLD" | sed -e 's/   */ /g' -e 's/^ //' -e 's/ $//'`
-       grep 'NOT YET IMPLEMENTED' "out/$pattern.res" >/dev/null
+       grep 'NOT YET IMPLEMENTED' "$outdir/$pattern.res" >/dev/null
        NYI="$?"
        if [ -n "$SAV" ]; then
                if [ -n "$OLD" ]; then
-                       echo "[*ok*] out/$pattern.res $SAV - but $OLD remains!"
-                       echo >>$xml "<error message='ok out/$pattern.res - but $OLD remains'/>"
+                       echo "[*ok*] $outdir/$pattern.res $SAV - but $OLD remains!"
+                       echo >>$xml "<error message='ok $outdir/$pattern.res - but $OLD remains'/>"
                        remains="$remains $OLD"
                else
-                       echo "[ok] out/$pattern.res $SAV"
+                       echo "[ok] $outdir/$pattern.res $SAV"
                fi
                ok="$ok $pattern"
        elif [ -n "$FIXME" ]; then
                if [ -n "$OLD" ]; then
-                       echo "[*fixme*] out/$pattern.res $FIXME - but $OLD remains!"
-                       echo >>$xml "<error message='ok out/$pattern.res - but $OLD remains'/>"
+                       echo "[*fixme*] $outdir/$pattern.res $FIXME - but $OLD remains!"
+                       echo >>$xml "<error message='ok $outdir/$pattern.res - but $OLD remains'/>"
                        remains="$remains $OLD"
                else
-                       echo "[fixme] out/$pattern.res $FIXME"
+                       echo "[fixme] $outdir/$pattern.res $FIXME"
                        echo >>$xml "<skipped/>"
                fi
                todos="$todos $pattern"
        elif [ "x$NYI" = "x0" ]; then
-               echo "[todo] out/$pattern.res -> not yet implemented"
+               echo "[todo] $outdir/$pattern.res -> not yet implemented"
                echo >>$xml "<skipped/>"
                todos="$todos $pattern"
        elif [ -n "$SOSO" ]; then
-               echo "[======= soso out/$pattern.res $SOSO =======]"
-               echo >>$xml "<error message='soso out/$pattern.res $SOSO'/>"
+               echo "[======= soso $outdir/$pattern.res $SOSO =======]"
+               echo >>$xml "<error message='soso $outdir/$pattern.res $SOSO'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v out/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$SOSOF" ]; then
-               echo "[======= fixme soso out/$pattern.res $SOSOF =======]"
-               echo >>$xml "<error message='soso out/$pattern.res $SOSO'/>"
+               echo "[======= fixme soso $outdir/$pattern.res $SOSOF =======]"
+               echo >>$xml "<error message='soso $outdir/$pattern.res $SOSO'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v out/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$NSAV" ]; then
-               echo "[======= fail out/$pattern.res $NSAV =======]"
-               echo >>$xml "<error message='fail out/$pattern.res $NSAV'/>"
+               echo "[======= fail $outdir/$pattern.res $NSAV =======]"
+               echo >>$xml "<error message='fail $outdir/$pattern.res $NSAV'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v out/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$NFIXME" ]; then
-               echo "[======= changed out/$pattern.res $NFIXME ======]"
-               echo >>$xml "<error message='changed out/$pattern.res $NFIXME'/>"
+               echo "[======= changed $outdir/$pattern.res $NFIXME ======]"
+               echo >>$xml "<error message='changed $outdir/$pattern.res $NFIXME'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v out/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
-       elif [ -s out/$pattern.res ]; then
-               echo "[=== no sav ===] out/$pattern.res is not empty"
+       elif [ -s $outdir/$pattern.res ]; then
+               echo "[=== no sav ===] $outdir/$pattern.res is not empty"
                echo >>$xml "<error message='no sav and not empty'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v >>$xml out/$pattern.res
+               cat -v >>$xml $outdir/$pattern.res
                echo >>$xml "]]></system-out>"
                nos="$nos $pattern"
                echo "$ii" >> "$ERRLIST"
        else
                # no sav but empty res
-               echo "[0k] out/$pattern.res is empty"
+               echo "[0k] $outdir/$pattern.res is empty"
                ok="$ok $pattern"
        fi
-       if test -s out/$pattern.cmp.err; then
+       if test -s $outdir/$pattern.cmp.err; then
                echo >>$xml "<system-err><![CDATA["
-               cat -v >>$xml out/$pattern.cmp.err
+               cat -v >>$xml $outdir/$pattern.cmp.err
                echo >>$xml "]]></system-err>"
        fi
        echo >>$xml "</testcase>"
@@ -235,12 +303,12 @@ need_skip()
        test "$noskip" = true && return 1
        if echo "$1" | grep -f "$engine.skip" >/dev/null 2>&1; then
                echo "=> $2: [skip]"
-               echo >>$xml "<testcase classname='$3' name='$2' timestamp='`date -Iseconds`'><skipped/></testcase>"
+               echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
                return 0
        fi
        if test -n "$isinterpret" && echo "$1" | grep -f "exec.skip" >/dev/null 2>&1; then
                echo "=> $2: [skip exec]"
-               echo >>$xml "<testcase classname='$3' name='$2' timestamp='`date -Iseconds`'><skipped/></testcase>"
+               echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
                return 0
        fi
        return 1
@@ -273,11 +341,14 @@ find_nitc()
                echo "Could not find binary for engine $engine, aborting"
                exit 1
        fi
-       echo "Find binary for engine $engine: $recent $OPT"
+       if [ "x$isnode" = "xfalse" ]; then
+               echo "Found binary for engine $engine: $recent $OPT"
+       fi
        NITC=$recent
 }
 
 verbose=false
+isnode=false
 stop=false
 engine=nitg
 noskip=
@@ -289,6 +360,9 @@ while [ $stop = false ]; do
                -h) usage; exit;;
                --engine) engine="$2"; shift; shift;;
                --noskip) noskip=true; shift;;
+               --outdir) outdir="$2"; shift; shift;;
+               --compdir) compdir="$2"; shift; shift;;
+               --node) isnode=true; shift;;
                *) stop=true
        esac
 done
@@ -298,23 +372,23 @@ case $engine in
        nitg)
                engine=nitg-s;
                enginebinname=nitg;
-               OPT="--separate $OPT"
+               OPT="--separate $OPT --compile-dir $compdir"
                ;;
        nitg-s)
                enginebinname=nitg;
-               OPT="--separate $OPT"
+               OPT="--separate $OPT --compile-dir $compdir"
                ;;
        nitg-e)
                enginebinname=nitg;
-               OPT="--erasure $OPT"
+               OPT="--erasure $OPT --compile-dir $compdir"
                ;;
        nitg-sg)
                enginebinname=nitg;
-               OPT="--semi-global $OPT"
+               OPT="--semi-global $OPT --compile-dir $compdir"
                ;;
        nitg-g)
                enginebinname=nitg;
-               OPT="--global $OPT"
+               OPT="--global $OPT --compile-dir $compdir"
                ;;
        nit)
                engine=niti
@@ -330,7 +404,7 @@ case $engine in
                ;;
        emscripten)
                enginebinname=nitg
-               OPT="-m emscripten_nodejs.nit --semi-global $OPT"
+               OPT="-m emscripten_nodejs.nit --semi-global $OPT --compile-dir $compdir"
                savdirs="sav/nitg-sg/"
                ;;
        nitc)
@@ -351,30 +425,26 @@ savdirs="sav/$engine $savdirs sav/"
 # Set NIT_DIR if needed
 [ -z "$NIT_DIR" ] && export NIT_DIR=..
 
-if sh -c "timelimit echo" 1>/dev/null 2>&1; then
-       TIMEOUT="timelimit -t 600"
-elif sh -c "timeout 1 echo" 1>/dev/null 2>&1; then
-       TIMEOUT="timeout 600s"
-else
-       echo "No timelimit or timeout command detected. Tests may hang :("
-fi
-
 # Mark to distinguish files among tests
 # MARK=
 
-# File where error tests are outputed
-# Old ERRLIST is backuped
-ERRLIST=${ERRLIST:-errlist}
-ERRLIST_TARGET=$ERRLIST
-
 if [ $# = 0 ]; then
        usage;
        exit
 fi
 
+# CLEAN the out directory
+rm -rf "$outdir/" 2>/dev/null
+mkdir "$outdir" 2>/dev/null
+
+# File where error tests are outputed
+# Old ERRLIST is backuped
+ERRLIST=${ERRLIST:-errlist}
+ERRLIST_TARGET=$ERRLIST
+
 # Initiate new ERRLIST
 if [ "x$ERRLIST" = "x" ]; then
-       ERRLIST=/dev=null
+       ERRLIST=/dev/null
 else
        ERRLIST=$ERRLIST.tmp
        > "$ERRLIST"
@@ -383,12 +453,14 @@ fi
 ok=""
 nok=""
 todos=""
-xml="tests-$engine.xml"
-echo >$xml "<testsuites><testsuite>"
 
-# CLEAN the out directory
-rm -rf out/ 2>/dev/null
-mkdir out 2>/dev/null
+if [ "x$XMLDIR" = "x" ]; then
+       xml="tests-$engine.xml"
+else
+       xml="$XMLDIR/tests-$engine.xml"
+fi
+
+echo >$xml "<testsuites><testsuite>"
 
 for ii in "$@"; do
        if [ ! -f $ii ]; then
@@ -411,7 +483,7 @@ for ii in "$@"; do
 
        for i in "$ii" `./alterner.pl --start '#' --altsep '_' $ii`; do
                bf=`basename $i .nit`
-               ff="out/$bf"
+               ff="$outdir/$bf"
 
                # Sould we skip the alternative for this engine?
                need_skip $bf $bf $pack && continue
@@ -432,10 +504,10 @@ for ii in "$@"; do
                fi
 
                if [ -n "$isinterpret" ]; then
-                       cat > "./$ff.bin" <<END
+                       cat > "$ff.bin" <<END
 exec $NITC --no-color $OPT "$i" $includes -- "\$@"
 END
-                       chmod +x "./$ff.bin"
+                       chmod +x "$ff.bin"
                        > "$ff.cmp.err"
                        > "$ff.compile.log"
                        ERR=0
@@ -452,7 +524,7 @@ END
                                echo $NITC --no-color $OPT -o "$ffout" "$i" "$includes" $nocc
                        fi
                        NIT_NO_STACK=1 JNI_LIB_PATH=$JNI_LIB_PATH JAVA_HOME=$JAVA_HOME \
-                               /usr/bin/time --quiet -f%U -o "$ff.time.out" $TIMEOUT $NITC --no-color $OPT -o "$ffout" "$i" $includes $nocc 2> "$ff.cmp.err" > "$ff.compile.log"
+                               saferun -o "$ff.time.out" $NITC --no-color $OPT -o "$ffout" "$i" $includes $nocc 2> "$ff.cmp.err" > "$ff.compile.log"
                        ERR=$?
                        if [ "x$verbose" = "xtrue" ]; then
                                cat "$ff.compile.log"
@@ -460,11 +532,11 @@ END
                        fi
                fi
                if [ "$engine" = "emscripten" ]; then
-                       echo > "./$ff.bin" "nodejs $ffout \"\$@\""
+                       echo > "$ff.bin" "nodejs $ffout \"\$@\""
                        chmod +x "$ff.bin"
                        if grep "Fatal Error: more than one primitive class" "$ff.compile.log" > /dev/null; then
                                echo " [skip] do no not imports kernel"
-                               echo >>$xml "<testcase classname='$pack' name='$bf' timestamp='`date -Iseconds`'><skipped/></testcase>"
+                               echo >>$xml "<testcase classname='$pack' name='$bf' `timestamp`><skipped/></testcase>"
                                continue
                        fi
                fi
@@ -477,7 +549,7 @@ END
                        echo -n "nocc "
                        > "$ff.res"
                        process_result $bf $bf $pack
-               elif [ -x "./$ff.bin" ]; then
+               elif [ -x "$ff.bin" ]; then
                        if skip_exec "$bf"; then
                                # No exec
                                > "$ff.res"
@@ -489,10 +561,10 @@ END
                        args=""
                        if [ "x$verbose" = "xtrue" ]; then
                                echo ""
-                               echo "NIT_NO_STACK=1 ./$ff.bin" $args
-                       fi      
+                               echo "NIT_NO_STACK=1 $ff.bin" $args
+                       fi
                        NIT_NO_STACK=1 LD_LIBRARY_PATH=$JNI_LIB_PATH \
-                               /usr/bin/time --quiet -f%U -a -o "$ff.time.out" $TIMEOUT "./$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err"
+                               saferun -a -o "$ff.time.out" "$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err"
                        mv $ff.time.out $ff.times.out
                        awk '{ SUM += $1} END { print SUM }' $ff.times.out > $ff.time.out
 
@@ -532,12 +604,12 @@ END
                                        rm -rf "$fff.res" "$fff.err" "$fff.write" 2> /dev/null
                                        if [ "x$verbose" = "xtrue" ]; then
                                                echo ""
-                                               echo "NIT_NO_STACK=1 ./$ff.bin" $args
+                                               echo "NIT_NO_STACK=1 $ff.bin" $args
                                        fi
                                        echo -n "==> $name "
-                                       echo "./$ff.bin $args" > "./$fff.bin"
-                                       chmod +x "./$fff.bin"
-                                       WRITE="$fff.write" /usr/bin/time --quiet -f%U -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $TIMEOUT ./$fff.bin < $ffinputs > $fff.res 2>$fff.err"
+                                       echo "$ff.bin $args" > "$fff.bin"
+                                       chmod +x "$fff.bin"
+                                       WRITE="$fff.write" saferun -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $fff.bin < $ffinputs > $fff.res 2>$fff.err"
                                        if [ "x$verbose" = "xtrue" ]; then
                                                cat "$fff.res"
                                                cat >&2 "$fff.err"
@@ -554,7 +626,7 @@ END
                                        process_result $bff "  $name" $pack
                                done < $fargs
                        fi
-               elif [ -f "./$ff.bin" ]; then
+               elif [ -f "$ff.bin" ]; then
                        #Not executable (platform?)"
                        > "$ff.res"
                        process_result $bf "$bf" $pack
@@ -567,21 +639,23 @@ END
        done
 done
 
-echo "engine: $engine ($enginebinname $OPT)"
-echo "ok: " `echo $ok | wc -w` "/" `echo $ok $nok $nos $todos | wc -w`
+if [ "x$isnode" = "xfalse" ]; then
+       echo "engine: $engine ($enginebinname $OPT)"
+       echo "ok: " `echo $ok | wc -w` "/" `echo $ok $nok $nos $todos | wc -w`
 
-if [ -n "$nok" ]; then
-       echo "fail: $nok"
-       echo "There were $(echo $nok | wc -w) errors ! (see file $ERRLIST)"
-fi
-if [ -n "$nos" ]; then
-       echo "no sav: $nos"
-fi
-if [ -n "$todos" ]; then
-       echo "todo/fixme: $todos"
-fi
-if [ -n "$remains" ]; then
-       echo "sav that remains: $remains"
+       if [ -n "$nok" ]; then
+               echo "fail: $nok"
+               echo "There were $(echo $nok | wc -w) errors ! (see file $ERRLIST)"
+       fi
+       if [ -n "$nos" ]; then
+               echo "no sav: $nos"
+       fi
+       if [ -n "$todos" ]; then
+               echo "todo/fixme: $todos"
+       fi
+       if [ -n "$remains" ]; then
+               echo "sav that remains: $remains"
+       fi
 fi
 
 # write $ERRLIST