Merge: Raise nitc from the dead
authorJean Privat <jean@pryen.org>
Sat, 13 Dec 2014 07:50:17 +0000 (02:50 -0500)
committerJean Privat <jean@pryen.org>
Sat, 13 Dec 2014 07:50:17 +0000 (02:50 -0500)
Raise dead on `nitc`.
It's super effective...

Pull-Request: #1000
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

109 files changed:
contrib/github_merge.nit
contrib/github_search_for_jni/src/github_search_for_jni.nit
contrib/nitcc/examples/minilang.nit
contrib/nitcc/src/autom.nit
contrib/nitcc/src/grammar.nit
contrib/nitcc/src/nitcc_lexer0.nit
contrib/nitcc/src/nitcc_semantic.nit
contrib/nitcc/src/re2nfa.nit
contrib/nitester/src/nitester.nit
examples/mnit_simple/src/test_assets_and_resources.nit
examples/rosettacode/hailstone.nit [new file with mode: 0644]
examples/rosettacode/hamming_number.nit [new file with mode: 0644]
lib/a_star.nit
lib/base64.nit
lib/bucketed_game.nit
lib/buffered_ropes.nit
lib/c.nit
lib/cocoa/app_kit.nit [new file with mode: 0644]
lib/cocoa/cocoa.nit [new file with mode: 0644]
lib/cocoa/examples/cocoa_extern_types.nit [new file with mode: 0644]
lib/cocoa/examples/cocoa_message_box.nit [new file with mode: 0644]
lib/cocoa/examples/hello_cocoa.nit [new file with mode: 0644]
lib/cocoa/foundation.nit [new file with mode: 0644]
lib/combinations.nit
lib/console.nit
lib/crypto.nit
lib/csv.nit [deleted file]
lib/csv/csv.nit [new file with mode: 0644]
lib/csv/test_csv.nit [new file with mode: 0644]
lib/dummy_array.nit
lib/github/github.nit [new file with mode: 0644]
lib/github/github_curl.nit [moved from lib/github_api.nit with 93% similarity]
lib/github/test_github_curl.nit [new file with mode: 0644]
lib/io/push_back_reader.nit
lib/json_serialization.nit
lib/mpd.nit
lib/mpi.nit
lib/pnacl.nit
lib/poset.nit
lib/standard/exec.nit
lib/standard/exec_nit.c
lib/standard/file.nit
lib/standard/file_nit.c
lib/standard/file_nit.h
lib/standard/stream.nit
lib/standard/stream_nit.c [deleted file]
lib/standard/stream_nit.h [deleted file]
lib/standard/string.nit
misc/jenkins/nitester-wrapper.sh
misc/jenkins/unitrun.sh
misc/vim/syntax/nit.vim
share/man/nitpretty.md
share/nitdoc/js/plugins/quicksearch.js
src/doc/doc_model.nit
src/doc/doc_pages.nit
src/doc/doc_templates.nit
src/ffi/ffi.nit
src/ffi/objc.nit [new file with mode: 0644]
src/interpreter/naive_interpreter.nit
src/interpreter/primitive_types.nit [new file with mode: 0644]
src/metrics/mendel_metrics.nit
src/metrics/metrics_base.nit
src/model/mmodule.nit
src/model/model.nit
src/model_utils.nit
src/modelize/modelize_property.nit
src/nitni/nitni_base.nit
src/nitpretty.nit
src/pretty.nit [new file with mode: 0644]
src/rapid_type_analysis.nit
src/testing/testing_suite.nit
src/vm.nit
tests/Linux.skip [new file with mode: 0644]
tests/README.md
tests/base_empty_module2.nit [new file with mode: 0644]
tests/nitg-g.skip
tests/niti.skip
tests/sav/base_as_notnull2.res
tests/sav/base_as_notnull2_alt1.res
tests/sav/base_as_notnull2_alt2.res
tests/sav/base_as_notnull2_alt3.res
tests/sav/base_gen_variance2_alt1.res
tests/sav/base_gen_variance2_alt2.res
tests/sav/base_gen_variance3_alt1.res
tests/sav/base_gen_variance_int_alt1.res
tests/sav/cocoa_extern_types.res [new file with mode: 0644]
tests/sav/cocoa_message_box.res [new file with mode: 0644]
tests/sav/hailstone.res [new file with mode: 0644]
tests/sav/hamming_number.res [new file with mode: 0644]
tests/sav/hello_cocoa.res [new file with mode: 0644]
tests/sav/nitserial_args1.res
tests/sav/nitunit_args1.res
tests/sav/test_c_alt1.res
tests/sav/test_exec.res
tests/sav/test_ffi_java_callbacks.res
tests/sav/test_ffi_java_generics.res
tests/sav/test_ffi_java_string.res
tests/sav/test_ffi_java_use_module.res
tests/sav/test_ffi_objc_types_and_callbacks.res [new file with mode: 0644]
tests/sav/test_file_open_fail.res [new file with mode: 0644]
tests/sav/test_jvm.res
tests/sav/test_sqlite3_nity_alt1.res
tests/sav/test_sqlite3_nity_alt2.res
tests/test_fdstream.nit
tests/test_ffi_objc_types_and_callbacks.nit [new file with mode: 0644]
tests/test_file_open_fail.nit [new file with mode: 0644]
tests/test_stream_poll.nit
tests/tests.sh
tests/turing.skip [new file with mode: 0644]

index 0d5e052..89433b8 100644 (file)
@@ -15,7 +15,7 @@
 # Query the Github PR API to perform a merge
 module github_merge
 
-import github_api
+import github::github_curl
 import template
 
 redef class Object
index 35b19b2..8029b2e 100644 (file)
@@ -17,7 +17,7 @@
 # Script to scan Github for repositories possibly using JNI.
 module github_search_for_jni
 
-import github_api
+import github::github_curl
 
 # The proprieties introduced by this redef are to be used only on a JSON object
 # representing a Github repository.
index 4160f01..e316369 100644 (file)
@@ -1,15 +1,23 @@
 import minilang_test_parser
 
+# An naive recursive stack-based interpreter of the minilang language.
 class Interpretor
        super Visitor
+
+       # A stack of numeric values
        var stack = new Array[Int]
+
+       # A stack of boolean values
        var bstack = new Array[Bool]
+
+       # The current values assigned to each variable
        var vars = new HashMap[String, Int]
 
        redef fun visit(n) do n.accept_calculator(self)
 end
 
 redef class Node
+       # Execution of the node by the interpreter `v`
        fun accept_calculator(v: Interpretor) do visit_children(v)
 end
 
index 8825b01..5ece999 100644 (file)
@@ -23,18 +23,18 @@ class Automaton
        # The start state
        var start: State
 
-       # State that are accect states
+       # State that are accept states
        var accept = new Array[State]
 
        # All states
        var states = new Array[State]
 
-       # Tokens associated on accept states
-       # use `add_tag` to update
+       # Tokens associated on accept states.
+       # Use `add_tag` to update
        var tags = new HashMap[State, Set[Token]]
 
-       # Accept states associated on tokens
-       # use `add_tag` to update
+       # Accept states associated on tokens.
+       # Use `add_tag` to update
        var retrotags = new HashMap[Token, Set[State]]
 
        # Tag all accept states
@@ -66,7 +66,7 @@ class Automaton
                assert retrotags[t].has(s)
        end
 
-       # Remove all occurences of a tag in an automaton
+       # Remove all occurrences of a tag in an automaton
        fun clear_tag(t: Token)
        do
                if not retrotags.has_key(t) then return
@@ -78,7 +78,7 @@ class Automaton
                retrotags.keys.remove(t)
        end
 
-       # Remove tokens from conflicting state according the the inclusion of language
+       # Remove tokens from conflicting state according the inclusion of language.
        # REQUIRE: self isa DFA automaton
        fun solve_token_inclusion
        do
@@ -101,8 +101,8 @@ class Automaton
                end
        end
 
-       # Initialize a new automaton for the empty language
-       # one state, no accept, no transition
+       # Initialize a new automaton for the empty language.
+       # One state, no accept, no transition.
        init empty
        do
                var state = new State
@@ -110,8 +110,8 @@ class Automaton
                states.add state
        end
 
-       # Initialize a new automaton for the empty-string language
-       # one state, is accept, no transition
+       # Initialize a new automaton for the empty-string language.
+       # One state, is accept, no transition.
        init epsilon
        do
                var state = new State
@@ -120,8 +120,8 @@ class Automaton
                states.add state
        end
 
-       # Initialize a new automation for the language that accepts only a single symbol
-       # Two state, the second is accept, one transition on `symbol`
+       # Initialize a new automation for the language that accepts only a single symbol.
+       # Two state, the second is accept, one transition on `symbol`.
        init atom(symbol: Int)
        do
                var s = new State
@@ -148,8 +148,8 @@ class Automaton
                states.add a
        end
 
-       # Contatenate `other` to `self`
-       # other is modified and invalidated.
+       # Concatenate `other` to `self`.
+       # Other is modified and invalidated.
        fun concat(other: Automaton)
        do
                var s2 = other.start
@@ -160,7 +160,7 @@ class Automaton
                states.add_all other.states
        end
 
-       # `self` become the alternation of `self` and `other`
+       # `self` become the alternation of `self` and `other`.
        # `other` is modified and invalidated.
        fun alternate(other: Automaton)
        do
@@ -184,9 +184,9 @@ class Automaton
                states.add_all other.states
        end
 
-       # Return a new automaton that recognize `self` but not `other`
-       # For a theorical POV, this is the substraction of languages.
-       # Note: the implementation use `to_dfa` internally, so the theorical complexity is not cheap.
+       # Return a new automaton that recognize `self` but not `other`.
+       # For a theoretical POV, this is the subtraction of languages.
+       # Note: the implementation use `to_dfa` internally, so the theoretical complexity is not cheap.
        fun except(other: Automaton): Automaton
        do
                var ta = new Token("1")
@@ -209,8 +209,8 @@ class Automaton
                return c
        end
 
-       # `self` absorbs all states, transisions, tags, and acceptations of `other`
-       # An epsilon transition is added between `self.start` and `other.start`
+       # `self` absorbs all states, transitions, tags, and acceptations of `other`.
+       # An epsilon transition is added between `self.start` and `other.start`.
        fun absorb(other: Automaton)
        do
                states.add_all other.states
@@ -494,8 +494,8 @@ class Automaton
                f.close
        end
 
-       # Transform a NFA to a DFA
-       # note: the DFA is not miminized
+       # Transform a NFA to a DFA.
+       # note: the DFA is not minimized.
        fun to_dfa: Automaton
        do
                trim
@@ -581,8 +581,8 @@ class Automaton
                return dfa
        end
 
-       # epsilon-closure on a state of states
-       # used by `to_dfa`
+       # Epsilon-closure on a state of states.
+       # Used by `to_dfa`.
        private fun eclosure(states: Collection[State]): Set[State]
        do
                var res = new ArraySet[State]
@@ -601,8 +601,8 @@ class Automaton
                return res
        end
 
-       # trans on a set of states
-       # Used by `to_dfa`
+       # Trans on a set of states.
+       # Used by `to_dfa`.
        fun trans(states: Collection[State], symbol: Int): Set[State]
        do
                var res = new ArraySet[State]
@@ -621,9 +621,9 @@ class Automaton
                return res
        end
 
-       # Generate the Nit source code of the lexer
-       # `filepath` is the name of the ouptit file
-       # `parser` is the name of the parser module (used to import the token classes)
+       # Generate the Nit source code of the lexer.
+       # `filepath` is the name of the output file.
+       # `parser` is the name of the parser module (used to import the token classes).
        fun gen_to_nit(filepath: String, name: String, parser: nullable String)
        do
                var gen = new DFAGenerator(filepath, name, self, parser)
@@ -638,12 +638,9 @@ private class DFAGenerator
        var automaton: Automaton
        var parser: nullable String
 
-       var out: OStream
-       init(filepath: String, name: String, automaton: Automaton, parser: nullable String) do
-               self.filepath = filepath
-               self.name = name
-               self.automaton = automaton
-               self.parser = parser
+       var out: OStream is noinit
+
+       init do
                self.out = new OFStream.open(filepath)
        end
 
@@ -747,9 +744,9 @@ end
 # A state in a finite automaton
 class State
        # Outgoing transitions
-
        var outs = new Array[Transition]
-       # Ingoing tyransitions
+
+       # Ingoing transitions
        var ins = new Array[Transition]
 
        # Add a transitions to `to` on `symbol` (null means epsilon)
@@ -761,6 +758,8 @@ class State
                return t
        end
 
+       # Get the first state following the transition `i`.
+       # Null if no transition for `i`.
        fun trans(i: Int): nullable State
        do
                for t in outs do
@@ -778,7 +777,12 @@ end
 
 # A range of symbols on a transition
 class TSymbol
+       # The first symbol in the range
        var first: Int
+
+       # The last symbol if any.
+       #
+       # `null` means infinity.
        var last: nullable Int
 
        redef fun to_s
@@ -808,8 +812,8 @@ class Transition
        # The symbol on the transition (null means epsilon)
        var symbol: nullable TSymbol
 
-       # Remove the transition from the automaton
-       # Detash from `from` and `to`
+       # Remove the transition from the automaton.
+       # Detach from `from` and `to`.
        fun delete
        do
                from.outs.remove(self)
index 620b0a0..eea312f 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# A gramar describing a language
+# A grammar describing a language
 class Gram
-       # The productions (non-terminal) of the conctete grammar
+       # The productions (non-terminal) of the concrete grammar
        var prods = new Array[Production]
 
-       # The additionnal abstract productions of the grammar
+       # The additional abstract productions of the grammar
        # TODO clean AST
        var ast_prods = new Array[Production]
 
@@ -35,7 +35,7 @@ class Gram
                                res.append("{p.name} =\n")
                        end
                        var last = null
-                       if not p.alts.is_empty then p.alts.last
+                       if not p.alts.is_empty then last = p.alts.last
                        for a in p.alts do
                                res.append("\t\{{a.name}:\} {a.elems.join(" ")}")
                                if a.codes == null then a.make_codes
@@ -55,7 +55,7 @@ class Gram
                return res.to_s
        end
 
-       # Inline (ie. remove from the conctete grammar) some production
+       # Inline (ie. remove from the concrete grammar) some production
        # REQUIRE: no circular production in `prods`
        fun inline(prods: Collection[Production])
        do
@@ -322,14 +322,14 @@ class Production
        # The alternative of the production
        var alts = new Array[Alternative]
 
-       # Additionnal alternatives in the AST
+       # Additional alternatives in the AST
        var ast_alts = new Array[Alternative]
 
        # Is self the accept production
        var accept = false
 
        # Is self transformed to a other production for the AST
-       # FIXME: cleaup AST
+       # FIXME: cleanup AST
        var spe: nullable Production = null is writable
 
        # Is self contains only a single alternative (then no need for a abstract production class in the AST)
@@ -345,8 +345,10 @@ class Production
 
        # Is the production nullable
        var is_nullable = false
+
        # The first tokens of the production
        var firsts = new HashSet[Item]
+
        # The tokens that may follows the production (as in SLR)
        var afters = new HashSet[Item]
 
@@ -432,7 +434,7 @@ class Alternative
        # Is the alternative unparsable? (ie not in the automaton)
        var phony = false is writable
 
-       # Imitialize codes with the elements
+       # Initialize codes with the elements
        fun make_codes
        do
                if codes != null then return
@@ -445,20 +447,27 @@ class Alternative
        end
 end
 
-# A step in the construction of the AST. used to modelize transformations
+# A step in the construction of the AST.
+# Used to model transformations
 interface Code
 end
+
 # Get a element from the stack
 class CodePop
        super Code
        redef fun to_s do return "pop"
 end
-# Allocate a new AST node for an alternative using the correct number of poped elements
+
+# Allocate a new AST node for an alternative using the correct number of popped elements
 class CodeNew
        super Code
+
+       # The associated alternative
        var alt: Alternative
+
        redef fun to_s do return "New {alt.name}/{alt.elems.length}"
 end
+
 # Get null
 class CodeNull
        super Code
@@ -477,7 +486,7 @@ abstract class Element
        # The mangled name of the element
        fun cname: String do return "N{name.to_cmangle}"
 
-       # the name of the class in the AST
+       # The name of the class in the AST
        fun acname: String do
                var res = acname_cache
                if res == null then
@@ -486,15 +495,17 @@ abstract class Element
                end
                return res
        end
+
+       # The name of the class in the AST
        fun acname=(s: String) do acname_cache = s
 end
 
 # A terminal element
 class Token
        super Element
-       # States of the LR automatio that shift on self
+       # States of the LR automaton that shift on self
        var shifts = new ArraySet[LRState]
-       # States of the LR automatio that reduce on self in the lookahead(1)
+       # States of the LR automaton that reduce on self in the lookahead(1)
        var reduces = new ArraySet[LRState]
 end
 
@@ -872,13 +883,13 @@ end
 
 # A state in a LR automaton
 class LRState
-       # name of the automaton (short part from the start)
+       # Name of the automaton (short part from the start)
        var name: String
 
-       # malglen name
+       # Mangled name
        fun cname: String do return name.to_cmangle
 
-       # number
+       # Number
        var number: Int = -1
 
        # Set of all items
@@ -893,7 +904,7 @@ class LRState
        # Ingoing transitions
        var outs = new Array[LRTransition]
 
-       # trans function
+       # Trans function
        fun trans(e: Element): nullable LRState
        do
                for t in outs do if t.elem == e then return t.to
@@ -915,7 +926,7 @@ class LRState
                return true
        end
 
-       # Recusively extends item outside the core
+       # Recursively extends item outside the core
        fun extends(i: Item)
        do
                var e = i.next
@@ -940,7 +951,7 @@ class LRState
        var gotos = new ArraySet[Production]
        # Reduction guarded by tokens
        var guarded_reduce = new HashMap[Token, Set[Item]]
-       # Shitfs guarded by tokens
+       # Shifts guarded by tokens
        var guarded_shift = new HashMap[Token, Set[Item]]
 
        # Does the state need a guard to perform an action?
@@ -949,7 +960,7 @@ class LRState
        # Is the state LR0?
        fun is_lr0: Bool do return reduces.length <= 1 and shifts.is_empty or reduces.is_empty
 
-       # compute guards and conflicts
+       # Compute guards and conflicts
        fun analysis
        do
                # Extends the core
@@ -1031,7 +1042,7 @@ class LRState
                end
        end
 
-       # Return `i` and all other items of the state that expands, directly or undirectly, to `i`
+       # Return `i` and all other items of the state that expands, directly or indirectly, to `i`
        fun back_expand(i: Item): Set[Item]
        do
                var res = new ArraySet[Item]
@@ -1056,7 +1067,7 @@ end
 class LRTransition
        # The origin state
        var from: LRState
-       # The testination state
+       # The destination state
        var to: LRState
        # The element labeling the transition
        var elem: Element
@@ -1085,14 +1096,14 @@ class Item
                return b.to_s
        end
 
-       # The element thatr follow the dot, null if the fdot is at the end
+       # The element that follows the dot, null if the dot is at the end
        fun next: nullable Element
        do
                if pos >= alt.elems.length then return null
                return alt.elems[pos]
        end
 
-       # SLR loohahead
+       # SLR lookahead
        fun lookahead: Set[Token]
        do
                var res = new HashSet[Token]
index 7fa876f..f3ff58d 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Ad-hoc hand-writen lexer for nitcc
-# This avoid to commit (and relyon ) a generated lexer
-#
+# Ad-hoc hand-written lexer for nitcc
+# This avoid to commit (and rely on) a generated lexer
 module nitcc_lexer0
 
 # Required for the tokens definitions
 import nitcc_parser
 
-# Hand-writen lexer of nitcc
-# Used only for the boostrap of the tool.
+# Hand-written lexer of nitcc.
+# Used only for the bootstrap of the tool.
 class Lexer_nitcc
+       # The text to tokenize
        var text: String
 
-       var iter: Iterator[Char] = "".chars.iterator
+       # The iterator on text
+       private var iter: Iterator[Char] is noinit
+
+       # The current position
        var pos = 0
 
-       var tokens = new Array[NToken]
+       # The tokens currently produced
+       private var tokens = new Array[NToken]
 
+       # Tokenize and returns the tokens
        fun lex: Array[NToken]
        do
                iter = text.chars.iterator
@@ -88,13 +93,13 @@ class Lexer_nitcc
                return tokens
        end
 
-       fun error(c: Char)
+       private fun error(c: Char)
        do
                print "pos {pos}: Lexer error on '{c}'."
                abort
        end
 
-       fun str
+       private fun str
        do
                var b = new FlatBuffer
                b.add('\'')
@@ -121,7 +126,7 @@ class Lexer_nitcc
                abort
        end
 
-       fun id(c: Char)
+       private fun id(c: Char)
        do
                var b = new FlatBuffer
                b.add c
@@ -138,7 +143,7 @@ class Lexer_nitcc
                tokens.add token
        end
 
-       fun kw(c: Char)
+       private fun kw(c: Char)
        do
                var b = new FlatBuffer
                b.add c
@@ -155,7 +160,7 @@ class Lexer_nitcc
                tokens.add token
        end
 
-       fun trim
+       private fun trim
        do
                while iter.is_ok and iter.item <= ' ' do
                        iter.next
index 73baa05..af417d4 100644 (file)
@@ -28,6 +28,7 @@ import re2nfa
 class CollectNameVisitor
        super Visitor
 
+       # All the productions
        var nprods = new Array[Nprod]
 
        # Symbol table to associate things (prods and exprs) with their name
@@ -44,7 +45,7 @@ class CollectNameVisitor
        # The current production, used to initialize priorities
        var prod: nullable Production = null
 
-       # The current priority counter to name tranformed productions
+       # The current priority counter to name transformed productions
        var pricpt: Int = 0
 
        # Run the semantic analysis of the grammar
@@ -53,13 +54,13 @@ class CollectNameVisitor
                # First visit to collect names
                enter_visit(n)
 
-               # Second visit to use collectec names and build rhings
+               # Second visit to use collected names and build things
                var v2 = new CheckNameVisitor(self)
                v2.enter_visit(n)
 
-               # Inline all the ?
+               # Inline all the `?`
                gram.inline(v2.quesizes.values)
-               # Inlile all the prods sufixed by '_imline' #TODO use a real keyword
+               # Inline all the prods suffixed by '_inline' #TODO use a real keyword
                for p in gram.prods do
                        if not p.name.has_suffix("_inline") then continue
                        print "inline {p}"
@@ -102,14 +103,14 @@ private class CheckNameVisitor
        # The collected element names, for the alternative
        var elems_names = new Array[nullable String]
 
-       # The collected elementname, for the nelem
+       # The collected element names, for the nelem
        var elemname: nullable String = null
 
        # Is the alternative transformed, for the alternative
        var trans = false
 
        # The current priority class
-       # Used the check, and tranform the grammar
+       # Used the check, and transform the grammar
        var pri: nullable Npriority = null
 
        # Known ignored tokens
@@ -119,7 +120,7 @@ private class CheckNameVisitor
        var rejecteds = new Array[Element]
 
        # Pool of elements that are modified with + (reuse them!)
-       private var plusizes = new HashMap[Element, Production]
+       var plusizes = new HashMap[Element, Production]
 
        # Create a + version of an element
        fun plusize(e: Element): Production
@@ -136,7 +137,7 @@ private class CheckNameVisitor
        end
 
        # Pool of elements that are modified with ? (reuse them!)
-       private var quesizes = new HashMap[Element, Production]
+       var quesizes = new HashMap[Element, Production]
 
        # Create a ? version of an element
        fun quesize(e: Element): Production
@@ -155,14 +156,13 @@ private class CheckNameVisitor
        end
 
        # The current nexpr, used to track dependency on named expressions (see `Nexpr::precs`)
-       var nexpr: nullable Nexpr
+       var nexpr: nullable Nexpr = null
 
        # The current production, used to initialize alternatives
-       var prod: nullable Production
+       var prod: nullable Production = null
 
        # The main visitor, used to access the grammar of the symbol table
        var v1: CollectNameVisitor
-       init(v1: CollectNameVisitor) do self.v1 = v1
 
        redef fun visit(n) do n.accept_check_name_visitor(self)
 end
@@ -204,7 +204,7 @@ redef class Nexpr
        # The associated NFA (cached, see `build_nfa`)
        private var nfa: nullable Automaton
 
-       # Build the NFA, possibily building the NFA of required expressions
+       # Build the NFA, possibly building the NFA of required expressions
        # Print an error if there is a circular dependency
        # The result is cached
        fun build_nfa: Automaton do
@@ -269,7 +269,7 @@ redef class Nign
                                abort
                        else if e isa Token then
                                # The token was build and registered during the visit
-                               # So, unregister then, the bit Ignred token will be build later
+                               # So, unregister them, the big Ignored token will be build later
                                v.v1.gram.tokens.remove(e)
                        else
                                abort
@@ -298,13 +298,13 @@ redef class Nrej
 end
 
 redef class Nprod
-       # The associated main production
-       # ie the last priority class
+       # The associated main production.
+       # i.e. the last priority class.
        var prod: nullable Production
 
-       # The associated most-priority production
-       # ie the first priority class
-       # If there is no priority then `sub_prod == prod`
+       # The associated most-priority production.
+       # i.e. the first priority class.
+       # If there is no priority then `sub_prod == prod`.
        var sub_prod: nullable Production
 
        redef fun accept_collect_prod(v) do
@@ -376,13 +376,14 @@ redef class Natrans
 end
 
 redef class Npriority
+       # It is the last priority group?
        var is_last = false
 
        # The associated production
        var prod: nullable Production
 
-       # The production in the with the next less priority class
-       # null is there is no priority or if the first priority class
+       # The production in the with the next less priority class.
+       # `null` if there is no priority or if the first priority class.
        var next: nullable Production
 
        redef fun accept_collect_prod(v) do
@@ -409,7 +410,7 @@ redef class Npriority
                v.pri = self
                super
 
-               # Inject a new alternative that goes to the next less prioty class
+               # Inject a new alternative that goes to the next less priority class
                var alt = prod.new_alt2(prod.name + "_" + prod.alts.length.to_s, [next.as(not null)])
                alt.trans = true
                alt.codes = [new CodePop]
@@ -462,6 +463,8 @@ redef class Npriority_unary
 end
 
 redef class Alternative
+       # The short name of the alternative.
+       # Used for errors
        var short_name: nullable String
 end
 
index 6bca660..d0d90ac 100644 (file)
@@ -37,7 +37,6 @@ redef class Nstr
        redef fun make_rfa: Automaton
        do
                var a = new Automaton.epsilon
-               var val
                for c in self.value.chars do
                        var b = new Automaton.atom(c.ascii)
                        a.concat(b)
index 37b5594..3431a86 100644 (file)
@@ -31,6 +31,9 @@ abstract class Processor
        # Controller rank is always 0
        var controller_rank: Rank = 0.rank
 
+       # Rank on this processor
+       fun rank: Rank is abstract
+
        # Where to store data for transfer between nodes
        #
        # Require: `buffer.length % 4 == 0`
@@ -49,7 +52,7 @@ abstract class Processor
        # Tag of a new task packet of size `tasks_per_packet`
        var task_tag: Tag = 0.tag
 
-       # Tag to return a set of `Result` throught `buffer`
+       # Tag to return a set of `Result` thought `buffer`
        var result_tag: Tag = 1.tag
 
        # Tag to notify `Worker` when to quit
@@ -67,7 +70,10 @@ abstract class Processor
        # Run the main logic of this node
        fun run is abstract
 
-       # Engines targetted by this execution
+       # Hash or name of the branch to test
+       var branch_hash: String is noinit
+
+       # Engines targeted by this execution
        var engines: Array[String] is noinit
 
        # All known engines, used to detect errors in `engines`
@@ -86,6 +92,10 @@ abstract class Processor
        fun read_cli_options
        do
                var opt_ctx = new OptionContext
+               var opt_hash = new OptionString(
+                       "Branch to test",
+                       "--hash", "-h")
+               opt_hash.mandatory = true
                var opt_engines = new OptionString(
                        "Engines to test, separated with commas ({all_engines.join(", ")} or all)",
                        "--engine", "-e")
@@ -97,7 +107,7 @@ abstract class Processor
                        "Clean up all nitester files (and do not run tests)",
                        "--cleanup", "-C")
 
-               opt_ctx.add_option(opt_engines, opt_help, opt_verbose, opt_cleanup)
+               opt_ctx.add_option(opt_hash, opt_engines, opt_help, opt_verbose, opt_cleanup)
                opt_ctx.parse args
 
                # --help?
@@ -132,6 +142,9 @@ abstract class Processor
                if rest.is_empty then opt_ctx.usage_error "This tool needs at least one test_program.nit"
                test_programs = rest
 
+               # hash
+               branch_hash = opt_hash.value.as(not null)
+
                # gather and check engines
                var engines_str = opt_engines.value
                var engines
@@ -160,12 +173,30 @@ abstract class Processor
        # All tasks to be performed
        var tasks = new Array[Task]
 
-       # Gather and registar all tasks
+       # Gather and register all tasks
        fun create_tasks
        do
+               # At this point we are in our local nit
+               var skip_path = "tests/turing.skip"
+               var skip
+               if skip_path.file_exists then
+                       var skip_file = new IFStream.open(skip_path)
+                       skip = skip_file.read_lines
+                       skip_file.close
+               else
+                       skip = new Array[String]
+               end
+
                for prog in test_programs do for engine in engines do
+
+                       # Is is blacklisted?
+                       for s in skip do if not s.is_empty and prog.has(s) then
+                               if verbose > 0 and rank == 0 then print "Skipping test '{prog}' because of '{s}' in turing.skip"
+                               continue label
+                       end
+
                        tasks.add new Task(engine, prog)
-               end
+               end label
        end
 end
 
@@ -173,6 +204,8 @@ end
 class Controller
        super Processor
 
+       redef fun rank do return controller_rank
+
        # Id as `Int` of the next task to distribute
        var next_task_id = 0
 
@@ -326,14 +359,11 @@ class Worker
        super Processor
 
        # The `Rank` of `self`
-       var rank: Rank
+       redef var rank: Rank
 
        # Compilation directory
        var comp_dir = "/dev/shm/nit_compile{rank}" is lazy
 
-       # Output file directory
-       var out_dir = "/dev/shm/nit_out{rank}" is lazy
-
        # Directory to store the xml files produced for Jenkins
        var xml_dir = "~/jenkins_xml/"
 
@@ -341,7 +371,10 @@ class Worker
        var tests_sh_out = "/dev/shm/nit_local_out{rank}" is lazy
 
        # Source Nit repository, must be already updated and `make` before execution
-       var nit_source_dir = "~/nit"
+       var local_nit = "/dev/shm/nit{rank}" is lazy
+
+       # Remote Nit repository (actually the local source)
+       var remote_nit = "~/nit/"
 
        # Compiled `Regex` to detect the argument of an execution
        var re_arg: Regex = "arg [0-9]+".to_re
@@ -364,6 +397,26 @@ class Worker
        fun setup
        do
                if verbose > 0 then sys.system "hostname"
+
+               if local_nit.file_exists then local_nit.rmdir
+
+               exec_and_check "git clone {remote_nit} {local_nit}"
+               local_nit.chdir
+               exec_and_check "git config remote.origin.fetch +refs/remotes/origin/pr/*:refs/remotes/origin/pr/*"
+               exec_and_check "git fetch origin --quiet"
+               exec_and_check "git checkout {branch_hash}"
+               exec_and_check "cp {remote_nit}/bin/nitg bin/"
+               exec_and_check "src/git-gen-version.sh"
+               exec_and_check "bin/nitg --dir bin/ src/nit.nit src/nitvm.nit"
+       end
+
+       private fun exec_and_check(cmd: String)
+       do
+               if verbose > 0 then
+                       print "+ {cmd}"
+                       var res = sys.system(cmd)
+                       assert res == 0 else print "Command '{cmd}' failed."
+               end
        end
 
        # Clean up the testing environment
@@ -372,8 +425,8 @@ class Worker
        fun cleanup
        do
                if comp_dir.file_exists then comp_dir.rmdir
-               if out_dir.file_exists then out_dir.rmdir
                if tests_sh_out.file_exists then tests_sh_out.file_delete
+               if local_nit.file_exists then local_nit.file_delete
        end
 
        # Single C `int` to hold the next task id received from the `Controller`
@@ -401,11 +454,12 @@ class Worker
                                        if task_id >= tasks.length then break
                                        var task = tasks[task_id]
 
+                                       "tests".chdir
+
                                        # Command line to execute test
-                                       var cmd = "XMLDIR={xml_dir} ERRLIST={out_dir}/errlist TMPDIR={out_dir} " +
+                                       var cmd = "XMLDIR={xml_dir} " +
                                                "CCACHE_DIR={ccache_dir} CCACHE_TEMPDIR={ccache_dir} CCACHE_BASEDIR={comp_dir} " +
-                                               "./tests.sh --compdir {comp_dir} --outdir {out_dir} " +
-                                               " --node --engine {task.engine} {task.test_program} > {tests_sh_out}"
+                                               "./tests.sh --node --engine {task.engine} {task.test_program} > {tests_sh_out}"
 
                                        # Execute test
                                        sys.system cmd
index b2564bf..c4ffc7f 100644 (file)
@@ -33,14 +33,14 @@ redef class App
        # Testing the assets manager
        fun test_assets
        do
-               assert asset_manager.bitmap("fighter.png") != null
+               assert asset_manager.bitmap("fighter.png").width == 32
        end
 
        # Testing the resources manager
        fun test_resources do
                assert resource_manager.string("string_test") == "string test"
                assert resource_manager.boolean("test_bool") == true
-               assert resource_manager.dimension("test_dimen_1") != null
-               assert resource_manager.dimension("test_dimen_2") != null
+               assert resource_manager.dimension("test_dimen_1") == 25
+               assert resource_manager.dimension("test_dimen_2") == 150
        end
 end
diff --git a/examples/rosettacode/hailstone.nit b/examples/rosettacode/hailstone.nit
new file mode 100644 (file)
index 0000000..c1351e1
--- /dev/null
@@ -0,0 +1,57 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+# Copyright 2014 amineorion <amineorion@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Task: Hailstone sequence
+# SEE: <http://rosettacode.org/wiki/Hailstone_sequence>
+#
+# Not optimize version.
+
+module hailstone
+
+fun hailstone (n: Int): Array[Int]
+do
+       var sequence = [n]
+       while n != 1 do
+               if n.bin_and(0x01) == 0 then
+                       n = n / 2
+               else
+                       n = 3 * n + 1
+               end
+               sequence.add(n)
+       end
+       return sequence
+end
+var sequence27 = hailstone(27)
+var size27 = sequence27.length
+print "Sequence for 27 has {size27} begin with: {sequence27[0]}, " +
+                "{sequence27[1]}, {sequence27[2]}, {sequence27[3]} " +
+                "and end with: {sequence27[size27 - 4]}, {sequence27[size27 - 3]}, " +
+                "{sequence27[size27 - 2]}, {sequence27[size27 - 1]}"
+
+var max: Int = hailstone(1).length
+var max_sequence = hailstone(1)
+var max_element = 1
+
+for i in [2..100000] do
+       var sequence = hailstone(i)
+       if max < sequence.length then
+               max = sequence.length
+               max_element = i
+               max_sequence = sequence
+       end
+end
+print "The number with longest sequence is {max_element} " +
+               "with length of {max_sequence.length}"
diff --git a/examples/rosettacode/hamming_number.nit b/examples/rosettacode/hamming_number.nit
new file mode 100644 (file)
index 0000000..3ff8a44
--- /dev/null
@@ -0,0 +1,129 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+# Copyright 2014 amineorion <amineorion@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Task: Hamming numbers
+# SEE: <http://rosettacode.org/wiki/Hamming_numbers>
+#
+# Version that don't support 1,000,000th position of Hamming number yet.
+
+module hamming_number
+
+class HammingTriple
+       super Comparable
+
+       var a: Int
+       var b: Int
+       var c: Int
+
+       redef type OTHER: HammingTriple
+
+       init(a: Int, b: Int, c: Int)
+       do
+               self.a = a
+               self.b = b
+               self.c = c
+       end
+
+       redef fun >(other: OTHER): Bool
+       do
+               if a > other.a and b > other.b and c > other.c then
+                       return true
+               end
+
+               if a < other.a and b < other.b and c < other.c then
+                       return false
+               end
+
+               if self == other then
+                       return false
+               end
+
+               var log1 = log
+               var log2 = other.log
+
+               if not log1.is_approx(log2, 0.000001) then
+                       return log1 > log2
+               end
+               return false
+       end
+
+       fun log: Float
+       do
+               return a.to_f * 2.0.log + b.to_f * 3.0.log + c.to_f * 5.0.log
+       end
+
+       redef fun ==(o)
+       do
+               return o isa HammingTriple and a == o.a and b == o.b and c == o.c
+       end
+
+       fun calcul: Int
+       do
+               return (2 ** a) * (3 ** b) * (5 ** c)
+       end
+
+       redef fun to_s: String
+       do
+               return "a: {a}, b: {b}, c: {c}"
+       end
+end
+
+class HammingArray
+       super Array[HammingTriple]
+
+       fun get_min: HammingTriple
+       do
+               var min = self[0]
+               var min_index = 0
+               for i in [1..length[ do
+                       if min > self[i] then
+                               min = self[i]
+                               min_index = i
+                       end
+               end
+               remove_at(min_index)
+               return min
+       end
+end
+
+fun hamming(limit_position: Int): Int
+do
+       var hamming_triple = new HammingArray
+       hamming_triple.add(new HammingTriple(0, 0, 0))
+       loop
+               var current = hamming_triple.get_min
+               limit_position -= 1
+               if limit_position == 0 then
+                       return current.calcul
+               end
+
+               if current.a == 0 and current.b == 0 then
+                       hamming_triple.add(new HammingTriple(current.a, current.b, current.c + 1))
+               end
+
+               if current.a == 0 then
+                       hamming_triple.add(new HammingTriple(current.a, current.b + 1, current.c))
+               end
+
+               hamming_triple.add(new HammingTriple(current.a + 1, current.b, current.c))
+       end
+end
+
+for i in [1..20] do
+       print "{i}: {hamming(i)}"
+end
+
+print "1691: {hamming(1691)}"
index 97925ff..0c5ed80 100644 (file)
@@ -116,7 +116,7 @@ class Node
                loop
                        var frontier_node: nullable N = null
 
-                       var bucket_searched: Int = 0
+                       var bucket_searched = 0
 
                        # find next valid node in frontier/buckets
                        loop
index 2d941e9..7b78d78 100644 (file)
@@ -36,6 +36,10 @@ redef class String
        # Encodes the receiver string to base64.
        # By default, uses "=" for padding.
        fun encode_base64 : String do return encode_base64_custom_padding( '=' )
+
+       # Encodes the receiver string to base64 using a custom padding character.
+       #
+       # If using the default padding character `=`, see `encode_base64`.
        fun encode_base64_custom_padding( padding : Char ) : String
        do
                var base64_chars = once base64_chars
@@ -78,6 +82,10 @@ redef class String
        # Decodes the receiver string from base64.
        # By default, uses "=" for padding.
        fun decode_base64 : String do return decode_base64_custom_padding( '=' )
+
+       # Decodes the receiver string to base64 using a custom padding character.
+       #
+       # If using the default padding character `=`, see `decode_base64`.
        fun decode_base64_custom_padding( padding : Char ) : String
        do
                var inverted_base64_chars = once inverted_base64_chars
index 09a4ee8..703ff5c 100644 (file)
@@ -24,6 +24,8 @@ module bucketed_game
 
 # Something acting on the game
 class Turnable[G: Game]
+
+       # Execute `turn` for this instance.
        fun do_turn(turn: GameTurn[G]) is abstract
 end
 
@@ -39,9 +41,11 @@ class Bucketable[G: Game]
        fun cancel_act do act_at = null
 end
 
-# Optiomized organization of `Bucketable` instances
+# Optimized organization of `Bucketable` instances
 class Buckets[G: Game]
        super Turnable[G]
+
+       # Bucket type used in this implementation.
        type BUCKET: HashSet[Bucketable[G]]
 
        private var buckets: Array[BUCKET] is noinit
@@ -59,6 +63,7 @@ class Buckets[G: Game]
                end
        end
 
+       # Add the Bucketable event `e` at `at_tick`.
        fun add_at(e: Bucketable[G], at_tick: Int)
        do
                var at_key = key_for_tick(at_tick)
@@ -103,6 +108,8 @@ end
 
 # Game related event
 interface GameEvent
+
+       # Apply `self` to `game` logic.
        fun apply( game : ThinGame ) is abstract
 end
 
@@ -113,39 +120,43 @@ end
 
 # Game logic on the client
 class ThinGame
+
+       # Game tick when `self` should act.
+       #
+       # Default is 0.
        var tick: Int = 0 is protected writable
 end
 
 # Game turn on the client
 class ThinGameTurn[G: ThinGame]
-       var tick: Int = 0 is protected writable
 
-       var events: List[GameEvent] = new List[GameEvent] is protected writable
+       # Game tick when `self` should act.
+       var tick: Int is protected writable
 
-       init (t: Int) do tick = t
+       # List of game events occured for `self`.
+       var events = new List[GameEvent] is protected writable
 end
 
 # Game turn on the full logic
 class GameTurn[G: Game]
        super ThinGameTurn[G]
+
+       # Game that `self` belongs to.
        var game: G
 
-       init (g: G)
-       do
-               super(g.tick)
-               game = g
+       # Create a new game turn for `game`.
+       init (game: G) is old_style_init do
+               super(game.tick)
+               self.game = game
        end
 
-       fun act_next(e: Bucketable[G])
-       do
-               game.buckets.add_at(e, tick + 1)
-       end
+       # Insert the Bucketable event `e` to be executed at next tick.
+       fun act_next(e: Bucketable[G]) do game.buckets.add_at(e, tick + 1)
 
-       fun act_in(e: Bucketable[G], t: Int)
-       do
-               game.buckets.add_at(e, tick + t)
-       end
+       # Insert the Bucketable event `e` to be executed at tick `t`.
+       fun act_in(e: Bucketable[G], t: Int) do game.buckets.add_at(e, tick + t)
 
+       # Add and `apply` a game `event`.
        fun add_event( event : GameEvent )
        do
                event.apply( game )
@@ -156,8 +167,11 @@ end
 # Full game logic
 class Game
        super ThinGame
+
+       # Game type used in this implementation.
        type G: Game
 
+       # Bucket list in this game.
        var buckets: Buckets[G] = new Buckets[G]
 
        # Last turn executed in this game
@@ -167,6 +181,10 @@ class Game
 
        init do end
 
+       # Execute and return a new GameTurn.
+       #
+       # This method calls `do_pre_turn` before executing the GameTurn
+       # and `do_post_turn` after.
        fun do_turn: GameTurn[G]
        do
                var turn = new GameTurn[G](self)
@@ -182,6 +200,15 @@ class Game
                return turn
        end
 
+       # Execute something before executing the current GameTurn.
+       #
+       # Should be redefined by clients to add a pre-turn behavior.
+       # See `Game::do_turn`.
        fun do_pre_turn(turn: GameTurn[G]) do end
+
+       # Execute something after executing the current GameTurn.
+       #
+       # Should be redefined by clients to add a post-turn behavior.
+       # See `Game::do_turn`.
        fun do_post_turn(turn: GameTurn[G]) do end
 end
index b287871..bbed4ea 100644 (file)
@@ -68,10 +68,13 @@ end
 private class LeafSubstrings
        super IndexedIterator[Text]
 
-       var str: String
+       var leaf: Leaf
+       var str: String is noinit
        var avail = true
 
-       init(l: Leaf) do str = new FlatString.with_infos(l.buf.ns, l.length, 0, l.length - 1)
+       init do
+               str = new FlatString.with_infos(leaf.buf.ns, leaf.length, 0, leaf.length - 1)
+       end
 
        redef fun is_ok do return avail
 
@@ -86,8 +89,8 @@ end
 private class Leaf
        super RopeString
 
-       private var buf: ManualBuffer
-       private var bns: NativeString is noinit
+       var buf: ManualBuffer
+       var bns: NativeString is noinit
        redef var length: Int is noinit
 
        redef fun empty do return new Leaf(new ManualBuffer)
@@ -240,7 +243,6 @@ redef class Concat
 
        redef fun +(o) do
                var s = o.to_s
-               var mlen = length
                var slen = s.length
                if s isa FlatString then
                        var r = right
@@ -286,7 +288,6 @@ redef class FlatString
                        return new Concat(sl + self, s.right)
                else if s isa Leaf then
                        if slen + mlen > maxlen then return new Concat(self, s)
-                       var mits = items
                        var mifrom = index_from
                        var sb = s.buf
                        var b = new ManualBuffer
index 3732085..d94ae8e 100644 (file)
--- a/lib/c.nit
+++ b/lib/c.nit
@@ -29,7 +29,7 @@ abstract class CArray[E]
        # Pointer to the real C array
        var native_array: NATIVE is noinit
 
-       private init(length: Int) do self._length = length
+       private init(length: Int) is old_style_init do self._length = length
 
        redef fun [](index)
        do
@@ -38,6 +38,7 @@ abstract class CArray[E]
                return native_array[index]
        end
 
+       # Set `val` at `index`.
        fun []=(index: Int, val: E)
        do
                assert not destroyed
@@ -45,7 +46,14 @@ abstract class CArray[E]
                native_array[index] = val
        end
 
+       # Was this instance destroyed?
+       #
+       # See `CArray::destroy`.
        var destroyed = false
+
+       # Free used memory used by `native_array`.
+       #
+       # Also set `destroyed` to true.
        fun destroy
        do
                if destroyed then return
@@ -57,9 +65,14 @@ end
 
 # A native C array, as in a pointer to the first element of the array
 extern class NativeCArray `{ void * `}
+
+       # Type of contained elements.
        type E: nullable Object
 
+       # Get element at `index`.
        fun [](index: E): E is abstract
+
+       # Set `val` at `index`.
        fun []=(index: E, val: E) is abstract
 
        # Return pointer to the address to the second element of this array
@@ -73,8 +86,8 @@ class CIntArray
        super CArray[Int]
        redef type NATIVE: NativeCIntArray
 
-       init(size: Int)
-       do
+       # Initialize a new CIntArray of `size` elements.
+       init(size: Int) is old_style_init do
                native_array = new NativeCIntArray(size)
                super size
        end
@@ -95,7 +108,9 @@ extern class NativeCIntArray `{ int* `}
        super NativeCArray
        redef type E: Int
 
+       # Initialize a new NativeCIntArray of `size` elements.
        new(size: Int) `{ return calloc(size, sizeof(int)); `}
+
        redef fun [](index) `{ return recv[index]; `}
        redef fun []=(index, val) `{ recv[index] = val; `}
 
@@ -108,8 +123,7 @@ class CByteArray
        redef type NATIVE: NativeCByteArray
 
        # Allocate a new array of `size`
-       init(size: Int)
-       do
+       init(size: Int) is old_style_init do
                native_array = new NativeCByteArray(size)
                super size
        end
diff --git a/lib/cocoa/app_kit.nit b/lib/cocoa/app_kit.nit
new file mode 100644 (file)
index 0000000..91bc87c
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The Application Kit provides services to create GUI
+module app_kit is c_linker_option "-framework AppKit"
+
+import foundation
+
+in "ObjC Header" `{
+       #import <AppKit/AppKit.h>
+`}
+
+# A simple message box
+extern class NSAlert in "ObjC" `{ NSAlert * `}
+       super NSObject
+
+       # Allocate and instanciate a new `NSAlert`
+       new in "ObjC" `{ return [[NSAlert alloc] init]; `}
+
+       # Set the content of this message box
+       fun message_text=(text: NSString) in "ObjC" `{ [recv setMessageText:text]; `}
+
+       # Show this message box
+       fun run_modal in "ObjC" `{ [recv runModal]; `}
+end
diff --git a/lib/cocoa/cocoa.nit b/lib/cocoa/cocoa.nit
new file mode 100644 (file)
index 0000000..99c6b12
--- /dev/null
@@ -0,0 +1,26 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The Cocoa API is the development layer of OS X
+#
+# This module is only compatible with OS X.
+#
+# This wrapper of the Cocoa API regroups the Foundation Kit and the
+# Application Kit.
+module cocoa is c_linker_option "-framework Cocoa"
+
+import foundation
+import app_kit
diff --git a/lib/cocoa/examples/cocoa_extern_types.nit b/lib/cocoa/examples/cocoa_extern_types.nit
new file mode 100644 (file)
index 0000000..775e6e4
--- /dev/null
@@ -0,0 +1,43 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Test extern classes from the Cocoa framework and extern factories
+module cocoa_extern_types
+
+import cocoa
+
+in "ObjC Header" `{
+       // for `NSString::hello`
+       #import <Foundation/Foundation.h>
+`}
+
+redef extern class NSString
+
+       # Additionnal factory
+       new hello in "ObjC" `{ return @"Factory Hello"; `}
+end
+
+# Print a custom string to the log
+fun nslog(text: NSString) in "ObjC" `{
+       NSLog(text);
+`}
+
+nslog "Hello using to_nsstring".to_nsstring
+nslog new NSString.hello
+
+var msg_box = new NSAlert
+msg_box.message_text = "From Nit".to_nsstring
+if "NIT_TESTING".environ != "true" then msg_box.run_modal
diff --git a/lib/cocoa/examples/cocoa_message_box.nit b/lib/cocoa/examples/cocoa_message_box.nit
new file mode 100644 (file)
index 0000000..72e35d7
--- /dev/null
@@ -0,0 +1,32 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Simple message box using the Cocoa framework
+module cocoa_message_box
+
+import cocoa
+
+in "ObjC" `{
+       #import <Cocoa/Cocoa.h>
+`}
+
+fun dialog in "ObjC" `{
+       NSAlert *alert = [[[NSAlert alloc] init] autorelease];
+       [alert setMessageText:@"Hello world!"];
+       [alert runModal];
+`}
+
+if "NIT_TESTING".environ != "true" then dialog
diff --git a/lib/cocoa/examples/hello_cocoa.nit b/lib/cocoa/examples/hello_cocoa.nit
new file mode 100644 (file)
index 0000000..3a2b168
--- /dev/null
@@ -0,0 +1,33 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Hello world using the Cocoa framework
+module hello_cocoa
+
+import cocoa::foundation
+
+in "ObjC" `{
+       #import <Foundation/Foundation.h>
+`}
+
+# Print `"Hello world!"` to the log
+fun hello_world in "ObjC" `{
+       @autoreleasepool {
+               NSLog(@"Hello World!");
+       }
+`}
+
+hello_world
diff --git a/lib/cocoa/foundation.nit b/lib/cocoa/foundation.nit
new file mode 100644 (file)
index 0000000..ab027fd
--- /dev/null
@@ -0,0 +1,47 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The Foundation Kit provides basic Objective-C classes and structures
+module foundation is c_linker_option "-framework Foundation"
+
+in "ObjC Header" `{
+       #import <Foundation/Foundation.h>
+`}
+
+# Base of the Foundation framework class hierarchy
+extern class NSObject in "ObjC" `{ NSObject * `}
+end
+
+# String of the Foundation Kit
+#
+# Created using `Text::to_nsstring`.
+extern class NSString in "ObjC" `{ NSString * `}
+       super NSObject
+end
+
+redef class NativeString
+       # Get a `NSString` from `self` with the specified `length`
+       fun to_nsstring(length: Int): NSString in "ObjC" `{
+               return [[NSString alloc] initWithBytes:recv
+                       length:length
+                       encoding:NSASCIIStringEncoding];
+       `}
+end
+
+redef class Text
+       # Get a `NSString` from `self`
+       fun to_nsstring: NSString do return to_cstring.to_nsstring(length)
+end
index 4076c03..cb1612a 100644 (file)
@@ -120,7 +120,7 @@ private class CartesianIterator[E]
        var collection: CartesianCollection[E]
 
        # The array of iterations that will be increased in the lexicographic order.
-       private var iterators = new Array[Iterator[E]]
+       var iterators = new Array[Iterator[E]]
 
        init
        do
@@ -314,8 +314,8 @@ private class CombinationIterator[E]
        super Iterator[SequenceRead[E]]
        var product: CombinationCollection[E]
 
-       private var iterators = new Array[Iterator[E]]
-       private var indices = new Array[Int]
+       var iterators = new Array[Iterator[E]]
+       var indices = new Array[Int]
 
        var are_sorted: Bool is noinit
        var are_unique: Bool is noinit
@@ -374,7 +374,7 @@ private class CombinationIterator[E]
                end
        end
 
-       private fun next_free(rank: Int, start: Int): Int
+       fun next_free(rank: Int, start: Int): Int
        do
                loop
                        for i in [0..rank[ do
@@ -388,7 +388,7 @@ private class CombinationIterator[E]
                return start
        end
 
-       private fun reset_iterator(rank: Int): Iterator[E]
+       fun reset_iterator(rank: Int): Iterator[E]
        do
                var it = product.collection.iterator
                iterators[rank] = it
index e922a84..c0a8772 100644 (file)
@@ -44,8 +44,6 @@ end
 class TermMoveUp
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -56,8 +54,6 @@ end
 class TermMoveDown
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -68,8 +64,6 @@ end
 class TermMoveFoward
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -80,8 +74,6 @@ end
 class TermMoveBackward
        super TermDirectionalMove
 
-       init do end
-
        # Move by the specified number of cells.
        init by(magnitude: Int) do self.magnitude = magnitude
 
@@ -102,8 +94,6 @@ class TermMove
        # 1 is the left.
        var column: Int = 1
 
-       init do end
-
        # Move at the specified position.
        #
        # (1, 1) is the top-left corner of the display.
index a84358f..a4abbd1 100644 (file)
@@ -69,7 +69,6 @@ redef class String
                var rot = x % 26
                if rot < 0 then rot += 26
                var d = new FlatBuffer.with_capacity(length)
-               var p = 0
                for i in chars do
                        d.add i.rot(rot)
                end
diff --git a/lib/csv.nit b/lib/csv.nit
deleted file mode 100644 (file)
index 82fbb05..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# CSV output facilities
-module csv
-
-# A CSV document representation
-class CSVDocument
-       super Streamable
-
-       var header: Array[String] = new Array[String] is writable
-       var lines: Array[Array[String]] = new Array[Array[String]]
-
-       fun set_header(values: Object...) do
-               header.clear
-               for value in values do
-                       header.add(value.to_s)
-               end
-       end
-
-       fun add_line(values: Object...) do
-               if values.length != header.length then
-                       print "CSV error: header declares {header.length} columns, line contains {values.length} values"
-                       abort
-               end
-               var line = new Array[String]
-               for value in values do
-                       line.add(value.to_s)
-               end
-               lines.add(line)
-       end
-
-       private fun write_line_to(line: Collection[String], stream: OStream)
-       do
-               var i = line.iterator
-               if i.is_ok then
-                       stream.write(i.item)
-                       i.next
-                       while i.is_ok do
-                               stream.write(";")
-                               stream.write(i.item)
-                               i.next
-                       end
-               end
-               stream.write("\n")
-       end
-
-       redef fun write_to(stream)
-       do
-               write_line_to(header, stream)
-               for line in lines do write_line_to(line, stream)
-       end
-
-       # deprecated alias for `write_to_file`
-       fun save(file: String) do write_to_file(file)
-end
diff --git a/lib/csv/csv.nit b/lib/csv/csv.nit
new file mode 100644 (file)
index 0000000..441f95c
--- /dev/null
@@ -0,0 +1,385 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# CSV document handling.
+module csv
+
+# Specifies a CSV format.
+class CsvFormat
+       # The character that delimits escaped value.
+       #
+       # The delimiter is escaped by doubling it.
+       var delimiter: Char
+
+       # The character that split each cell in a row.
+       var separator: Char
+
+       # The character that ends a row (end of line).
+       var eol: String
+
+       # Escape sequence for the delimiter.
+       private var escaping = "{delimiter}{delimiter}" is lazy
+
+       # Escape the specified cell.
+       private fun escape_cell(cell: String): Text do
+               var result = new RopeBuffer
+               result.add delimiter
+               result.append cell.replace(delimiter, escaping)
+               result.add delimiter
+               return result
+       end
+
+       # Can the specified value be inserted without any escaping?
+       private fun is_value_clean(value: String): Bool do
+               for c in value.chars do
+                       if c == delimiter then return false
+                       if c == separator then return false
+                       if eol.chars.has(c) then return false
+               end
+               return true
+       end
+end
+
+# A CSV document representation.
+class CsvDocument
+       super Streamable
+
+       # The format to use.
+       #
+       # Defaults to `rfc4180`.
+       var format: CsvFormat = rfc4180 is writable
+
+       # The header.
+       #
+       # Contains the name of all fields in this table.
+       var header: Array[String] = new Array[String] is writable
+
+       # The list of the records.
+       #
+       # All records must have the same length than `header`.
+       var records: Array[Array[String]] = new Array[Array[String]]
+
+       # Replace the header by the specified row.
+       fun set_header(values: Object...) do
+               header.clear
+               for value in values do header.add(value.to_s)
+       end
+
+       # Append the specfied record.
+       fun add_record(values: Object...) do
+               assert values.length == header.length else
+                       sys.stderr.write "CSV error: Header declares {header.length} columns, record contains {values.length} values.\n"
+               end
+               var record = new Array[String]
+               for value in values do record.add(value.to_s)
+               records.add(record)
+       end
+
+       redef fun write_to(stream) do
+               var writer = new CsvWriter.with_format(stream, format)
+               writer.write_sequence(header)
+               for record in records do writer.write_sequence(record)
+       end
+
+       # Deprecated alias for `write_to_file`.
+       fun save(file: String) do write_to_file(file)
+
+       # Load from the specified stream.
+       #
+       # Parameters:
+       #
+       # * `stream`: Input stream.
+       # * `has_header`: Is the first row the header?
+       # * `skip_empty`: Do we skip the empty lines?
+       # For details, see `CsvReader.skip_empty`.
+       fun load_from(stream: IStream, has_header: Bool, skip_empty: Bool) do
+               var reader = new CsvReader.with_format(stream, format)
+               reader.skip_empty = skip_empty
+               if has_header then
+                       if reader.is_ok then
+                               header = reader.item
+                       else
+                               header.clear
+                       end
+               end
+               records.clear
+               for record in reader do records.add(record)
+       end
+
+       # Load from the specified file.
+       #
+       # Parameters:
+       #
+       # * `path`: Path of the file.
+       # * `has_header`: Is the first row the header?
+       # * `skip_empty`: Do we skip the empty lines?
+       fun load(path: String, has_header: Bool, skip_empty: Bool) do
+               var istream = new IFStream.open(path)
+               load_from(istream, has_header, skip_empty)
+               istream.close
+       end
+end
+
+# Appends CSV rows to a file.
+#
+# By default, uses the format recommended by RFC 4180 (see `rfc4180`).
+#
+# Note: If a row contains only an empty cell, its representation is
+# undistinguishable from an empty line. This is because the empty values are
+# always written unescaped in order to avoid them to be interpreted as escaped
+# delimiters by some parsers.
+#
+# ~~~nit
+# var out = new StringOStream
+# var writer = new CsvWriter(out)
+# writer.write_row(1, 2.0, "foo\nbar")
+# writer.write_sequence([""])
+# assert out.to_s == """1,2.0,"foo\nbar"\r\n\r\n"""
+# ~~~
+class CsvWriter
+
+       # The output stream.
+       var ostream: OStream
+
+       # The format to use.
+       #
+       # Defaults to `rfc4180`.
+       var format: CsvFormat = rfc4180
+
+       # Do we escape all cells (except empty ones)?
+       #
+       # If `false` (the default), escape only cells that contain a metacharacter
+       # of the format. In all cases, empty cells are not escaped. This option
+       # permits to choose between the optimization of the performances (when
+       # `true`) and optimization of the size of the output (when `false`).
+       #
+       # Note: Escaping may not be correctly recognized by some parsers.
+       var always_escape = false is writable
+
+       # Create a new writer with the specified format.
+       init with_format(ostream:OStream, format: CsvFormat) do
+               self.ostream = ostream
+               self.format = format
+       end
+
+       # Append the specified sequence as a row.
+       #
+       # The representation of each cell is determined by `to_s`.
+       fun write_sequence(row: SequenceRead[Object]) do
+               if not row.is_empty then
+                       var i = row.iterator
+                       var separator = format.separator.to_s
+                       write_cell i.item.to_s
+                       i.next
+                       for cell in i do
+                               ostream.write separator
+                               write_cell cell.to_s
+                       end
+               end
+               ostream.write format.eol
+       end
+
+       # Append the specified row.
+       #
+       # The representation of each cell is determined by `to_s`.
+       fun write_row(row: Object...) do write_sequence(row)
+
+       # Close the output stream.
+       fun close do ostream.close
+
+       private fun write_cell(cell: String) do
+               if cell.is_empty then return
+               if not always_escape and format.is_value_clean(cell) then
+                       ostream.write cell
+               else
+                       ostream.write format.escape_cell(cell)
+               end
+       end
+end
+
+# Reads rows from a CSV file.
+#
+# By default, uses the format recommended by RFC 4180 (see `rfc4180`).
+#
+# ~~~nit
+# var example = new StringIStream("""
+# foo,bar\r
+# "Hello, word!",1234.5 + 42\r
+# "Something\r
+# ""else""\", baz\r
+# """)
+# var reader = new CsvReader(example)
+# var table = new Array[Array[String]]
+#
+# for row in reader do table.add row
+# assert table == [
+#                      ["foo","bar"],
+#                      ["Hello, word!","1234.5 + 42"],
+#                      ["Something\r\n\"else\""," baz"]
+#              ]
+# ~~~
+class CsvReader
+       super Iterator[Array[String]]
+
+       # The input stream.
+       var istream: IStream
+
+       # The format to use.
+       #
+       # Defaults to `rfc4180`.
+       var format: CsvFormat = rfc4180 is lazy
+
+       # Do we skip the empty lines?
+       #
+       # Note: Even if this attribute is `false`, the presence of an line ending at
+       # end of the last row does not change the number of returned rows.
+       # This is because the line endings are processed as terminators, not as
+       # separators. Therefore, when there is more than one line ending at the end
+       # of the file, the additional lines are interpreted as empty rows that
+       # are skipped only if `skip_empty` is set to `true`.
+       #
+       # `false` by default.
+       var skip_empty: Bool = false is writable
+
+       # The last read row.
+       private var row: nullable Array[String] = null
+
+       # Did we read something?
+       private var started = false
+
+       # Create a new reader with the specified format.
+       init with_format(istream:IStream, format: CsvFormat) do
+               self.istream = istream
+               self.format = format
+       end
+
+       # Read the first row, if needed.
+       fun prepare do
+               if not started then
+                       row = read_row
+                       started = true
+               end
+       end
+
+       redef fun next do
+               prepare
+               assert is_ok else
+                       sys.stderr.write "Already at the end of the stream.\n"
+               end
+               row = read_row
+       end
+
+       # Return the last read row.
+       redef fun item do
+               prepare
+               return row.as(not null)
+       end
+
+       redef fun is_ok do
+               prepare
+               return row != null
+       end
+
+       # Free some internal ressources and set `is_ok` to `false`.
+       #
+       # Do not close the input stream.
+       redef fun finish do row = null
+
+       # Close the input stream.
+       fun close do istream.close
+
+       private fun read_row: nullable Array[String] do
+               if istream.eof then return null
+               var row = new Array[String]
+               var value = new RopeBuffer
+
+               # Number of unescaped characters since the last delimiter or separator.
+               var unescaped = 0
+
+               # Do we read the start of a row?
+               var got_row = false
+
+               # Do we found a delimited string in the current cell?
+               var got_delimiter = false
+
+               loop
+                       var i = istream.read_char
+                       var c: Char
+
+                       if i < 0 then
+                               if got_row then
+                                       row.add value.to_s
+                                       return row
+                               else
+                                       return null
+                               end
+                       end
+                       c = i.ascii
+
+                       if c == format.delimiter then
+                               if got_delimiter and unescaped == 0 then
+                                       # Got an escaped delimiter.
+                                       value.add format.delimiter
+                               end
+                               # Read all bytes until the delimiter.
+                               loop
+                                       i = istream.read_char
+                                       assert not_eof: i >= 0 else
+                                               sys.stderr.write "Unexpected end of file before the end of a delimited value.\n"
+                                       end
+                                       c = i.ascii
+                                       if c == format.delimiter then break
+                                       value.add c
+                               end
+                               unescaped = 0
+                               got_row = true
+                               got_delimiter = true
+                       else if c == format.separator then
+                               # Flush the value to the row.
+                               row.add value.to_s
+                               value.clear
+                               unescaped = 0
+                               got_delimiter = false
+                       else
+                               value.add c
+                               unescaped += 1
+                               if unescaped >= format.eol.length and
+                                               value.has_suffix(format.eol) then
+                                       var value_trimed = value.substring(0,
+                                                       value.length - format.eol.length).to_s
+                                       if skip_empty and row.is_empty and
+                                                       value_trimed.is_empty and
+                                                       not got_delimiter then
+                                               # Skip the empty line.
+                                               value.clear
+                                               unescaped = 0
+                                               got_row = false
+                                       else
+                                               row.add value_trimed
+                                               return row
+                                       end
+                               else
+                                       got_row = true
+                               end
+                       end
+               end
+       end
+end
+
+# The CSV format recommended by [RFC 4180](https://tools.ietf.org/html/rfc4180).
+#
+# * `delimiter`: `'"'`
+# * `separator`: `','`
+# * `eol`: `"\r\n"`
+fun rfc4180: CsvFormat do return once new CsvFormat('"', ',', "\r\n")
diff --git a/lib/csv/test_csv.nit b/lib/csv/test_csv.nit
new file mode 100644 (file)
index 0000000..074fced
--- /dev/null
@@ -0,0 +1,202 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT. This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. You can modify it is you want, provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You are allowed to redistribute it and sell it, alone or is a part of
+# another product.
+
+# Tests for `csv`.
+module test_csv is test_suite
+
+import test_suite
+import csv
+
+class TestCsvWriter
+       super TestSuite
+
+       # The custom CSV format used in the tests.
+       private var custom_format = new CsvFormat('/', ':', "#")
+
+       # Expect to write `row` as `expected_rfc4180` and as `expected_custom`.
+       #
+       # Parameters:
+       #
+       # * `always_escape`: value of the `always_escape` option.
+       # * `row`: row to write.
+       # * `expected_rfc4180`: expected result in RFC 4180.
+       # * `expected_custom`: expected result in the custom CSV format.
+       private fun expect(always_escape: Bool, row: SequenceRead[String],
+                       expected_rfc4180: String,
+                       expected_custom: String) do
+               var out = new StringOStream
+               var writer = new CsvWriter(out)
+
+               writer.always_escape = always_escape
+               writer.write_sequence(row)
+               assert out.to_s == expected_rfc4180 else
+                       sys.stderr.write "\nFormat: RFC 4180\n"
+                       sys.stderr.write "Expecting: \"{expected_rfc4180.escape_to_nit}\"\n"
+                       sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
+               end
+               writer.close
+
+               out = new StringOStream
+               writer = new CsvWriter.with_format(out, custom_format)
+               writer.always_escape = always_escape
+               writer.write_sequence(row)
+               assert out.to_s == expected_custom else
+                       sys.stderr.write "\nFormat: {custom_format.delimiter}"
+                       sys.stderr.write " {custom_format.separator}"
+                       sys.stderr.write " {custom_format.eol.escape_to_nit}\n"
+                       sys.stderr.write "Expecting: \"{expected_custom.escape_to_nit}\"\n"
+                       sys.stderr.write "Got: \"{out.to_s.escape_to_nit}\"\n"
+               end
+               writer.close
+       end
+
+       fun test_empty do expect(true, new Array[String], "\r\n", "#")
+
+       fun test_one_cell do expect(true, ["foo/\"\r\n,"],
+                       "\"foo/\"\"\r\n,\"\r\n",
+                       "/foo//\"\r\n,/#")
+
+       fun test_optimize_size_escaped do expect(false, ["foo/\"\r\n,"],
+                       "\"foo/\"\"\r\n,\"\r\n",
+                       "/foo//\"\r\n,/#")
+
+       fun test_optimize_size_eol do expect(false, ["foo\r#\n"],
+                       "\"foo\r#\n\"\r\n",
+                       "/foo\r#\n/#")
+
+       fun test_optimize_size_unescaped do expect(false, ["foo"],
+                       "foo\r\n",
+                       "foo#")
+
+       fun test_multiple_cells do expect(true, ["1", "", "/"],
+                       "\"1\",,\"/\"\r\n",
+                       "/1/::////#")
+
+       fun test_multiple_cells_optimize_size do expect(false, ["1", "", "/"],
+                       "1,,/\r\n",
+                       "1::////#")
+end
+
+class TestCsvReader
+       super TestSuite
+
+       # The custom CSV format used in the tests.
+       private var custom_format = new CsvFormat('/', ':', "#")
+
+       # Expect to read `expected`.
+       #
+       # Parameters:
+       #
+       # * `skip_empty`: value of the `skip_empty` option.
+       # * `modal_escaping`: value of the `modal_escaping` option.
+       # * `input_rfc4180`: input in the RFC 4180 format.
+       # * `input_custom`: input in the custom CSV format.
+       # * `expected`: expected resulting table.
+       private fun expect(skip_empty: Bool,
+                       input_rfc4180: String,
+                       input_custom: String,
+                       expected: SequenceRead[SequenceRead[String]]) do
+               var istream: IStream
+               var reader: CsvReader
+               var i = 0
+
+               istream = new StringIStream(input_rfc4180)
+               reader = new CsvReader(istream)
+               reader.skip_empty = skip_empty
+               assert_table_equals("RFC 4180", reader, expected.iterator)
+
+               istream = new StringIStream(input_custom)
+               reader = new CsvReader.with_format(istream, custom_format)
+               reader.skip_empty = skip_empty
+               assert_table_equals("{custom_format.delimiter} " +
+                               "{custom_format.separator} " +
+                               "{custom_format.eol.escape_to_nit}", reader, expected.iterator)
+       end
+
+       # Check if tables are equal.
+       private fun assert_table_equals(format: String,
+                       actual: Iterator[SequenceRead[String]],
+                       expected: Iterator[SequenceRead[String]]) do
+               var i = 0
+
+               for actual_row in actual do
+                       assert expected.is_ok else fail(format,"Too many rows.")
+                       var expected_row = expected.item
+                       assert_row_equals(format, i, actual_row, expected_row)
+                       expected.next
+                       i += 1
+               end
+               assert not expected.is_ok else fail(format, "Not enough rows.")
+               expected.finish
+       end
+
+       # Check if rows are equal.
+       private fun assert_row_equals(format: String,
+                       row_index: Int,
+                       actual: SequenceRead[String],
+                       expected: SequenceRead[String]) do
+               assert actual == expected else
+                       fail(format, """
+At row {{{row_index}}}.
+Expecting: {{{expected.join("|")}}}
+Got: {{{actual.join("|")}}}""")
+               end
+       end
+
+       # Output an error message with an indication of the format used.
+       private fun fail(format: Text, message: Text) do
+               sys.stderr.write "\nFormat: {format}\n"
+               sys.stderr.write message
+               sys.stderr.write "\n"
+       end
+
+       fun test_empty do expect(false, "", "", new Array[Array[String]])
+
+       fun test_empty_eol do expect(false, "\r\n", "#", [[""]])
+
+       fun test_empty_skip do expect(true, "", "", new Array[Array[String]])
+
+       fun test_empty_skip1 do expect(true, "\r\n", "#", new Array[Array[String]])
+
+       fun test_empty_skip2 do expect(true, "\r\n\r\n", "##", new Array[Array[String]])
+
+       fun test_escaped do expect(false, "\"foo/\"\"\r\n,\"\r\n",
+                       "/foo//\"\r\n,/#", [["foo/\"\r\n,"]])
+
+       fun test_unescaped do expect(false, "foo bar\r\n",
+                       "foo bar#", [["foo bar"]])
+
+       fun test_escaped_no_eol do expect(false, "\"foo/\"\"\r\n,\"",
+                       "/foo//\"\r\n,/", [["foo/\"\r\n,"]])
+
+       fun test_unescaped_no_eol do expect(false, "foo bar",
+                       "foo bar", [["foo bar"]])
+
+       fun test_multiple_cells do expect(false, "\"1\",,\"/\"\r\n",
+                       "/1/::////#", [["1", "", "/"]])
+
+       fun test_multiple_cells_unescaped do expect(false, "1,,/\r\n",
+                       "1::////#", [["1", "", "/"]])
+
+       fun test_modal_escaping do expect(false, """a"b""/c","d"e""",
+                       """/ab"///c:d/e/""", [["""ab"/c""", "de"]])
+
+       fun test_skip_start do expect(true, "\r\n1,,/\r\n",
+                       "#1::////#", [["1", "", "/"]])
+
+       fun test_dont_skip_empty_delimited do expect(true, "\"\"\r\n",
+                       "//#", [[""]])
+
+       fun test_dont_skip_multiple_empty_cells do expect(true, ",\r\n",
+                       ":#", [["",""]])
+
+       fun test_mutiple_rows do expect(false, "\"a\r\nb#\",c\r\nd,\r\n,e\r\n",
+                       "/a\r\nb#/:c#d:#:e#", [["a\r\nb#", "c"], ["d", ""], ["", "e"]])
+end
index 88e7008..9061e53 100644 (file)
@@ -1,6 +1,7 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
+# Copyright 2014 Alexandre Terrasa <alexandre@moz-code.org>
 #
 # This file is free software, which comes along with NIT.  This software is
 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
@@ -10,6 +11,7 @@
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
+# A `Set` that contains only integers.
 class DummyArray
        super Set[Int]
        private var capacity: Int
@@ -75,14 +77,15 @@ class DummyArray
                return _values[pos]
        end
 
-       init(capacity: Int)
-       do
+       # initialize a new DummyArray with `capacity`.
+       init(capacity: Int) is old_style_init do
                _capacity = capacity
                _keys = new NativeArray[Int](capacity)
                _values = new NativeArray[Int](capacity)
        end
 end
 
+# An iterator over a `DummyArray`.
 class DummyIterator
        super Iterator[Int]
        private var array: DummyArray
@@ -101,8 +104,8 @@ class DummyIterator
 
        redef fun next do _pos = _pos + 1 end
 
-       init(array: DummyArray)
-       do
+       # Initialize an iterator for `array`.
+       init(array: DummyArray) is old_style_init do
                _pos = 0
                _array = array
        end
diff --git a/lib/github/github.nit b/lib/github/github.nit
new file mode 100644 (file)
index 0000000..759b921
--- /dev/null
@@ -0,0 +1,18 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Github API related features.
+module github
+
+import github_curl
similarity index 93%
rename from lib/github_api.nit
rename to lib/github/github_curl.nit
index 39fca84..5e50474 100644 (file)
@@ -14,7 +14,7 @@
 
 # Curl extention to access the Github API
 # See https://developer.github.com/v3/ for details
-module github_api
+module github_curl
 
 import curl
 import json::static
@@ -24,7 +24,7 @@ class GithubCurl
        super Curl
 
        # Headers to use on all requests
-       var header: HeaderMap
+       var header: HeaderMap is noinit
 
        # OAuth token
        var auth: String
@@ -33,12 +33,7 @@ class GithubCurl
        # Eg. "Awesome-Octocat-App"
        var user_agent: String
 
-       init(auth: String, user_agent: String)
-       do
-               super
-               self.auth = auth
-               self.user_agent = user_agent
-
+       init do
                header = new HeaderMap
                header["Authorization"] = "token {auth}"
        end
@@ -83,4 +78,3 @@ do
        p.close
        return token.trim
 end
-
diff --git a/lib/github/test_github_curl.nit b/lib/github/test_github_curl.nit
new file mode 100644 (file)
index 0000000..e15f684
--- /dev/null
@@ -0,0 +1,48 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_github_curl is test_suite
+
+import github::github_curl
+import test_suite
+
+class TestGithubCurl
+       super TestSuite
+
+       var auth: String = get_github_oauth
+       var user_agent: String = "nit"
+       var testee: GithubCurl is noinit
+
+       redef fun before_test do
+               testee = new GithubCurl(auth, user_agent)
+       end
+
+       fun test_get_repo do
+               var uri = "https://api.github.com/repos/privat/nit"
+               var res = testee.get_and_check(uri)
+
+               assert res isa JsonObject
+               assert res["name"] == "nit"
+               assert res["owner"] isa JsonObject
+       end
+
+       fun test_get_user do
+               var uri = "https://api.github.com/users/Morriar"
+               var res = testee.get_and_check(uri)
+
+               assert res isa JsonObject
+               assert res["login"] == "Morriar"
+               assert res["html_url"] == "https://github.com/Morriar"
+       end
+end
index 257c662..5453901 100644 (file)
@@ -12,7 +12,7 @@
 module io::push_back_reader
 
 # Input stream that permits to push bytes back to the stream.
-interface PushBackReader
+class PushBackReader
        super IStream
 
        # Push the specified byte back to the stream.
index f090b24..c41426d 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Handles serialization and deserialization of objects to/from Json.
 module json_serialization
 
 import serialization
 import json::static
 
+# Serializer of Nit objects to Json string.
 class JsonSerializer
        super Serializer
 
        # Target writing stream
        var stream: OStream
 
-       init(stream: OStream) do self.stream = stream
-
        redef fun serialize(object)
        do
                if object == null then
@@ -52,9 +52,10 @@ class JsonSerializer
                end
        end
 
-       # Map of references to already serialized objects
+       # Map of references to already serialized objects.
        var refs_map = new HashMap[Serializable,Int]
 
+       # Get the internal serialized reference for this `object`.
        private fun ref_id_for(object: Serializable): Int
        do
                if refs_map.keys.has(object) then
@@ -67,18 +68,28 @@ class JsonSerializer
        end
 end
 
-# Deserializer from a Json string
+# Deserializer from a Json string.
 class JsonDeserializer
        super Deserializer
 
-       var root: nullable Jsonable
+       # Json text to deserialize from.
+       private var text: Text
+
+       # Root json object parsed from input text.
+       var root: nullable Jsonable is noinit
+
+       # Depth-first path in the serialized object tree.
        var path = new Array[JsonObject]
+
+       # Map of refenrences to already deserialized objects.
        var id_to_object = new HashMap[Int, Object]
 
+       # Last encountered object reference id.
+       #
+       # See `id_to_object`.
        var just_opened_id: nullable Int = null
 
-       init(text: Text)
-       do
+       init do
                var root = text.parse_json
                if root isa JsonObject then path.add(root)
                self.root = root
@@ -246,6 +257,7 @@ redef class Array[E]
                end
        end
 
+       # Instanciate a new `Array` from its serialized representation.
        init from_deserializer(v: Deserializer)
        do
                if v isa JsonDeserializer then
index 9a56b95..97c38f2 100644 (file)
@@ -21,12 +21,20 @@ import socket
 
 # Connection to a MPD server
 class MPDConnection
+
+       # Socket connection to server.
        var socket: nullable Socket = null
 
+       # Server hostname.
        var host: String
+
+       # Server port.
        var port: Int
+
+       # Server password.
        var password: nullable String
 
+       # Last occured error.
        var error: nullable String = null
 
        # Connect to the MPD server
@@ -130,6 +138,7 @@ class MPDConnection
                error = "Cannot get volume"
        end
 
+       # Set MPD server volume.
        fun volume=(vol: Int) do write_and_check("setvol {vol}\n")
 
        # Pause music playing on the MPD server
@@ -179,6 +188,7 @@ class MPDConnection
                end
        end
 
+       # Load playlist named `name`.
        fun load_playlist(name: String)
        do
                write_and_check "load \"{name}\""
@@ -187,23 +197,44 @@ end
 
 # MPD song info
 class SongInfo
+       # Song album.
        var album: String
+
+       # Song artist.
        var artist: String
+
+       # Song title.
        var title: String
+
+       # Song total duration.
        var time: Int
 end
 
 # MPD server status
 class ServerStatus
+
+       # MPD server volume.
        var volume: nullable Int
 
+       # Playback state (play/stop/pause).
        var state: String
+
+       # Is MPD server playing?
        fun playing: Bool do return state == "play"
+
+       # Is MPD server stopped?
        fun stopped: Bool do return state == "stop"
 
+       # Time elapsed within the current song.
        var elapsed: nullable Int
+
+       # TODO comment
        var time_at: nullable Int
+
+       # Total time of the current song.
        var time_total: nullable Int
+
+       # Get the cursor position on the song duration.
        fun time_ratio: nullable Float do
                if time_at == null or time_total == null then return null
                return time_at.to_f / time_total.to_f
index 1d8c406..01137f7 100644 (file)
@@ -117,7 +117,7 @@ class MPI
                # Deserialize message
                var deserializer = new JsonDeserializer(buffer)
                var deserialized = deserializer.deserialize
-               
+
                if deserialized == null then print "|{buffer}|{buffer.chars.join("-")}| {buffer.length}"
 
                return deserialized
@@ -191,19 +191,46 @@ end
 
 # An MPI data type
 extern class DataType `{ MPI_Datatype `}
+       # Get a MPI char.
        new char `{ return MPI_CHAR; `}
+
+       # Get a MPI short.
        new short `{ return MPI_SHORT; `}
+
+       # Get a MPI int.
        new int `{ return MPI_INT; `}
+
+       # Get a MPI long.
        new long `{ return MPI_LONG; `}
+
+       # Get a MPI long long.
        new long_long `{ return MPI_LONG_LONG; `}
+
+       # Get a MPI unsigned char.
        new unsigned_char `{ return MPI_UNSIGNED_CHAR; `}
+
+       # Get a MPI unsigned short.
        new unsigned_short `{ return MPI_UNSIGNED_SHORT; `}
+
+       # Get a MPI unsigned int.
        new unsigned `{ return MPI_UNSIGNED; `}
+
+       # Get a MPI unsigned long.
        new unsigned_long `{ return MPI_UNSIGNED_LONG; `}
+
+       # Get a MPI unsigned long long.
        new unsigned_long_long `{ return MPI_UNSIGNED_LONG_LONG; `}
+
+       # Get a MPI float.
        new float `{ return MPI_FLOAT; `}
+
+       # Get a MPI double.
        new double `{ return MPI_DOUBLE; `}
+
+       # Get a MPI long double.
        new long_double `{ return MPI_LONG_DOUBLE; `}
+
+       # Get a MPI byte.
        new byte `{ return MPI_BYTE; `}
 end
 
@@ -235,21 +262,60 @@ end
 
 # An MPI operation
 #
-# Used with the `reduce` method
+# Used with the `reduce` method.
+#
+# See <http://www.mpi-forum.org/docs/mpi-1.1/mpi-11-html/node78.html>
 extern class Op `{ MPI_Op `}
+       # Get a MPI null operation.
        new op_null `{ return MPI_OP_NULL; `}
+
+       # Get a MPI maximum operation.
        new max `{ return MPI_MAX; `}
+
+       # Get a MPI minimum operation.
        new min `{ return MPI_MIN; `}
+
+       # Get a MPI sum operation.
        new sum `{ return MPI_SUM; `}
+
+       # Get a MPI product operation.
        new prod `{ return MPI_PROD; `}
+
+       # Get a MPI logical and operation.
        new land `{ return MPI_LAND; `}
+
+       # Get a MPI bit-wise and operation.
        new band `{ return MPI_BAND; `}
+
+       # Get a MPI logical or operation.
        new lor `{ return MPI_LOR; `}
+
+       # Get a MPI bit-wise or operation.
        new bor `{ return MPI_BOR; `}
+
+       # Get a MPI logical xor operation.
        new lxor `{ return MPI_LXOR; `}
+
+       # Get a MPI bit-wise xor operation.
        new bxor `{ return MPI_BXOR; `}
+
+       # Get a MPI minloc operation.
+       #
+       # Used to compute a global minimum and also an index attached
+       # to the minimum value.
+       #
+       # See <http://www.mpi-forum.org/docs/mpi-1.1/mpi-11-html/node79.html#Node79>
        new minloc `{ return MPI_MINLOC; `}
+
+       # Get a MPI maxloc operation.
+       #
+       # Used to compute a global maximum and also an index attached
+       # to the maximum value.
+       #
+       # See <http://www.mpi-forum.org/docs/mpi-1.1/mpi-11-html/node79.html#Node79>
        new maxloc `{ return MPI_MAXLOC; `}
+
+       # Get a MPI replace operation.
        new replace `{ return MPI_REPLACE; `}
 end
 
index 4008422..70b8e7c 100644 (file)
@@ -44,6 +44,7 @@ in "C Header" `{
        #include <string.h>
        #include <stdlib.h>
        #include <pthread.h>
+       #include <poll.h>
 
        #define MAX_DICTIONARY_QUEUE_SIZE 200
        #define MAX_MESSAGE_QUEUE_SIZE 10
@@ -358,7 +359,7 @@ in "C Header" `{
        }
 
        /* Hack in order to avoid the problem with file. */
-       int poll(void *fds, int nfds, int timeout) { return 0; }
+       int poll(struct pollfd* fds, nfds_t nfds, int timeout) { return 0; }
 `}
 
 # Nit class representing a Pepper C API PP_Var typed as a Dictionary.
index e8f7e73..a5425dd 100644 (file)
@@ -226,21 +226,26 @@ class POSet[E: Object]
 
        # Write the POSet as a graphviz digraph.
        #
-       # Nodes are identified with their `to_s`.
+       # Nodes are labeled with their `to_s` so homonymous nodes may appear.
        # Edges are unlabeled.
        fun write_dot(f: OStream)
        do
                f.write "digraph \{\n"
+               var ids = new HashMap[E, Int]
+               for x in elements.keys do
+                       ids[x] = ids.length
+               end
                for x in elements.keys do
                        var xstr = x.to_s.escape_to_dot
-                       f.write "\"{xstr}\";\n"
+                       var nx = "n{ids[x]}"
+                       f.write "{nx}[label=\"{xstr}\"];\n"
                        var xe = self.elements[x]
                        for y in xe.dtos do
-                               var ystr = y.to_s.escape_to_dot
+                               var ny = "n{ids[y]}"
                                if self.has_edge(y,x) then
-                                       f.write "\"{xstr}\" -> \"{ystr}\"[dir=both];\n"
+                                       f.write "{nx} -> {ny}[dir=both];\n"
                                else
-                                       f.write "\"{xstr}\" -> \"{ystr}\";\n"
+                                       f.write "{nx} -> {ny};\n"
                                end
                        end
                end
@@ -254,7 +259,6 @@ class POSet[E: Object]
        fun show_dot
        do
                var f = new OProcess("dot", "-Txlib")
-               f.write "\}\n"
                write_dot(f)
                f.close
                f.wait
index 2495800..79f9bfd 100644 (file)
@@ -15,7 +15,7 @@
 # Standard input and output can be handled through streams.
 module exec
 
-import stream
+import file
 
 # Simple sub-process
 class Process
@@ -93,7 +93,7 @@ class IProcess
        super IStream
 
        # File Descriptor used for the input.
-       var stream_in: FDIStream is noinit
+       var stream_in: IFStream is noinit
 
        redef fun close do stream_in.close
 
@@ -106,7 +106,7 @@ class IProcess
        redef fun execute
        do
                super
-               stream_in = new FDIStream(data.out_fd)
+               stream_in = new IFStream.from_fd(data.out_fd)
        end
 end
 
@@ -129,7 +129,7 @@ class OProcess
        redef fun execute
        do
                super
-               stream_out = new FDOStream(data.in_fd)
+               stream_out = new OFStream.from_fd(data.in_fd)
        end
 end
 
index c808105..c69ba64 100644 (file)
@@ -26,22 +26,19 @@ se_exec_data_t* exec_Process_Process_basic_exec_execute_4(void *s, char *prog, c
        if (pipeflag & 1) {
                int res = pipe(in_fd);
                if ( res == -1 ) {
-                       fprintf( stderr, "Pipe init failed in Process:basic_exec_execute: %s\n", strerror( errno ) );
-                       exit(1);
+                       return NULL;
                }
        }
        if (pipeflag & 2) {
                int res = pipe(out_fd);
                if ( res == -1 ) {
-                       fprintf( stderr, "Pipe init failed in Process:basic_exec_execute: %s\n", strerror( errno ) );
-                       exit(1);
+                       return NULL;
                }
        }
        if (pipeflag & 4) {
                int res = pipe(err_fd);
                if ( res == -1 ) {
-                       fprintf( stderr, "Pipe init failed in Process:basic_exec_execute: %s\n", strerror( errno ) );
-                       exit(1);
+                       return NULL;
                }
        }
                                        
index 88fb3c8..a7c48db 100644 (file)
@@ -27,6 +27,8 @@ in "C Header" `{
        #include <sys/stat.h>
        #include <unistd.h>
        #include <stdio.h>
+       #include <poll.h>
+       #include <errno.h>
 `}
 
 # File Abstract Stream
@@ -43,6 +45,21 @@ abstract class FStream
 
        # File descriptor of this file
        fun fd: Int do return _file.fileno
+
+       # Sets the buffering mode for the current FStream
+       #
+       # If the buf_size is <= 0, its value will be 512 by default
+       #
+       # The mode is any of the buffer_mode enumeration in `Sys`:
+       #       - buffer_mode_full
+       #       - buffer_mode_line
+       #       - buffer_mode_none
+       fun set_buffering_mode(buf_size, mode: Int) do
+               if buf_size <= 0 then buf_size = 512
+               if _file.set_buffering_type(buf_size, mode) != 0 then
+                       last_error = new IOError("Error while changing buffering type for FStream, returned error {sys.errno.strerror}")
+               end
+       end
 end
 
 # File input stream
@@ -56,8 +73,14 @@ class IFStream
        # The original path is reused, therefore the reopened file can be a different file.
        fun reopen
        do
-               if not eof then close
+               if not eof and not _file.address_is_null then close
+               last_error = null
                _file = new NativeFile.io_open_read(path.to_cstring)
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Opening file at '{path.as(not null)}' failed with '{sys.errno.strerror}'")
+                       end_reached = true
+                       return
+               end
                end_reached = false
                _buffer_pos = 0
                _buffer.clear
@@ -65,7 +88,8 @@ class IFStream
 
        redef fun close
        do
-               _file.io_close
+               if _file.address_is_null then return
+               var i = _file.io_close
                _buffer.clear
                end_reached = true
        end
@@ -80,7 +104,7 @@ class IFStream
                _buffer.length = nb
                _buffer_pos = 0
        end
-       
+
        # End of file?
        redef var end_reached: Bool = false
 
@@ -90,11 +114,21 @@ class IFStream
                self.path = path
                prepare_buffer(10)
                _file = new NativeFile.io_open_read(path.to_cstring)
-               assert not _file.address_is_null else
-                       print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'"
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'")
+                       end_reached = true
                end
        end
 
+       init from_fd(fd: Int) do
+               self.path = ""
+               prepare_buffer(1)
+               _file = fd.fd_to_stream(read_only)
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Converting fd {fd} to stream failed with '{sys.errno.strerror}'")
+                       end_reached = true
+               end
+       end
 end
 
 # File output stream
@@ -104,30 +138,52 @@ class OFStream
        
        redef fun write(s)
        do
-               assert _is_writable
+               if last_error != null then return
+               if not _is_writable then
+                       last_error = new IOError("Cannot write to non-writable stream")
+                       return
+               end
                if s isa FlatText then
                        write_native(s.to_cstring, s.length)
                else
                        for i in s.substrings do write_native(i.to_cstring, i.length)
                end
+               _file.flush
        end
 
        redef fun close
        do
-               _file.io_close
+               if _file.address_is_null then
+                       if last_error != null then return
+                       last_error = new IOError("Cannot close unopened write stream")
+                       _is_writable = false
+                       return
+               end
+               var i = _file.io_close
+               if i != 0 then
+                       last_error = new IOError("Close failed due to error {sys.errno.strerror}")
+               end
                _is_writable = false
        end
-
        redef var is_writable = false
        
        # Write `len` bytes from `native`.
        private fun write_native(native: NativeString, len: Int)
        do
-               assert _is_writable
+               if last_error != null then return
+               if not _is_writable then
+                       last_error = new IOError("Cannot write to non-writable stream")
+                       return
+               end
+               if _file.address_is_null then
+                       last_error = new IOError("Writing on a null stream")
+                       _is_writable = false
+                       return
+               end
                var err = _file.io_write(native, len)
                if err != len then
                        # Big problem
-                       printn("Problem in writing : ", err, " ", len, "\n")
+                       last_error = new IOError("Problem in writing : {err} {len} \n")
                end
        end
        
@@ -135,14 +191,43 @@ class OFStream
        init open(path: String)
        do
                _file = new NativeFile.io_open_write(path.to_cstring)
-               assert not _file.address_is_null else
-                       print "Error: Opening file at '{path}' failed with '{sys.errno.strerror}'"
-               end
                self.path = path
                _is_writable = true
+               if _file.address_is_null then
+                       last_error = new IOError("Error: Opening file at '{path}' failed with '{sys.errno.strerror}'")
+                       is_writable = false
+               end
+       end
+
+       # Creates a new File stream from a file descriptor
+       init from_fd(fd: Int) do
+               self.path = ""
+               _file = fd.fd_to_stream(wipe_write)
+               _is_writable = true
+                if _file.address_is_null then
+                        last_error = new IOError("Error: Opening stream from file descriptor {fd} failed with '{sys.errno.strerror}'")
+                       _is_writable = false
+               end
        end
 end
 
+redef class Int
+       # Creates a file stream from a file descriptor `fd` using the file access `mode`.
+       #
+       # NOTE: The `mode` specified must be compatible with the one used in the file descriptor.
+       private fun fd_to_stream(mode: NativeString): NativeFile is extern "file_int_fdtostream"
+end
+
+# Constant for read-only file streams
+private fun read_only: NativeString do return "r".to_cstring
+
+# Constant for write-only file streams
+#
+# If a stream is opened on a file with this method,
+# it will wipe the previous file if any.
+# Else, it will create the file.
+private fun wipe_write: NativeString do return "w".to_cstring
+
 ###############################################################################
 
 # Standard input stream.
@@ -617,6 +702,10 @@ private extern class NativeFile `{ FILE* `}
        fun io_close: Int is extern "file_NativeFile_NativeFile_io_close_0"
        fun file_stat: FileStat is extern "file_NativeFile_NativeFile_file_stat_0"
        fun fileno: Int `{ return fileno(recv); `}
+       # Flushes the buffer, forcing the write operation
+       fun flush: Int is extern "fflush"
+       # Used to specify how the buffering will be handled for the current stream.
+       fun set_buffering_type(buf_length: Int, mode: Int): Int is extern "file_NativeFile_NativeFile_set_buffering_type_0"
 
        new io_open_read(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_read_1"
        new io_open_write(path: NativeString) is extern "file_NativeFileCapable_NativeFileCapable_io_open_write_1"
@@ -627,6 +716,10 @@ end
 
 redef class Sys
 
+       init do
+               if stdout isa FStream then stdout.as(FStream).set_buffering_mode(256, buffer_mode_line)
+       end
+
        # Standard input
        var stdin: PollableIStream = new Stdin is protected writable
 
@@ -636,6 +729,89 @@ redef class Sys
        # Standard output for errors
        var stderr: OStream = new Stderr is protected writable
 
+       # Enumeration for buffer mode full (flushes when buffer is full)
+       fun buffer_mode_full: Int is extern "file_Sys_Sys_buffer_mode_full_0"
+       # Enumeration for buffer mode line (flushes when a `\n` is encountered)
+       fun buffer_mode_line: Int is extern "file_Sys_Sys_buffer_mode_line_0"
+       # Enumeration for buffer mode none (flushes ASAP when something is written)
+       fun buffer_mode_none: Int is extern "file_Sys_Sys_buffer_mode_none_0"
+
+       # returns first available stream to read or write to
+       # return null on interruption (possibly a signal)
+       protected fun poll( streams : Sequence[FStream] ) : nullable FStream
+       do
+               var in_fds = new Array[Int]
+               var out_fds = new Array[Int]
+               var fd_to_stream = new HashMap[Int,FStream]
+               for s in streams do
+                       var fd = s.fd
+                       if s isa IFStream then in_fds.add( fd )
+                       if s isa OFStream then out_fds.add( fd )
+
+                       fd_to_stream[fd] = s
+               end
+
+               var polled_fd = intern_poll( in_fds, out_fds )
+
+               if polled_fd == null then
+                       return null
+               else
+                       return fd_to_stream[polled_fd]
+               end
+       end
+
+       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]) : nullable Int is extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{
+               int in_len, out_len, total_len;
+               struct pollfd *c_fds;
+               sigset_t sigmask;
+               int i;
+               int first_polled_fd = -1;
+               int result;
+
+               in_len = Array_of_Int_length( in_fds );
+               out_len = Array_of_Int_length( out_fds );
+               total_len = in_len + out_len;
+               c_fds = malloc( sizeof(struct pollfd) * total_len );
+
+               /* input streams */
+               for ( i=0; i<in_len; i ++ ) {
+                       int fd;
+                       fd = Array_of_Int__index( in_fds, i );
+
+                       c_fds[i].fd = fd;
+                       c_fds[i].events = POLLIN;
+               }
+
+               /* output streams */
+               for ( i=0; i<out_len; i ++ ) {
+                       int fd;
+                       fd = Array_of_Int__index( out_fds, i );
+
+                       c_fds[i].fd = fd;
+                       c_fds[i].events = POLLOUT;
+               }
+
+               /* poll all fds, unlimited timeout */
+               result = poll( c_fds, total_len, -1 );
+
+               if ( result > 0 ) {
+                       /* analyse results */
+                       for ( i=0; i<total_len; i++ )
+                               if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
+                                        c_fds[i].revents & POLLHUP ) /* closed */
+                               {
+                                       first_polled_fd = c_fds[i].fd;
+                                       break;
+                               }
+
+                       return Int_as_nullable( first_polled_fd );
+               }
+               else if ( result < 0 )
+                       fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
+
+               return null_Int();
+       `}
+
 end
 
 # Print `objects` on the standard output (`stdout`).
index 06f5e2e..f6a6987 100644 (file)
@@ -65,3 +65,11 @@ int file_stdin_poll_in_(void) {
        }
        return res > 0;
 }
+
+FILE* file_int_fdtostream(int fd, char* mode){
+       return fdopen(fd, mode);
+}
+
+int file_NativeFile_NativeFile_set_buffering_type_0(FILE* f, int buf_sz, int mode){
+       return setvbuf(f, NULL, mode, buf_sz);
+}
index 1f5b183..d8d9b4b 100644 (file)
@@ -24,6 +24,8 @@ extern int string_NativeString_NativeString_file_exists_0(char *f);
 extern void *string_NativeString_NativeString_file_stat_0(char *f);
 extern void *file_NativeFile_NativeFile_file_stat_0(FILE *f);
 extern int string_NativeString_NativeString_file_delete_0(char *f);
+FILE* file_int_fdtostream(int fd, char* mode);
+int file_NativeFile_NativeFile_set_buffering_type_0(FILE* f, int buf_sz, int mode);
 
 #define file_NativeFile_NativeFile_io_read_2(p, b, l) fread((b), 1, (l), (FILE*)(p))
 #define file_NativeFile_NativeFile_io_write_2(p, b, l) fwrite((b), 1, (l), (FILE*)(p))
@@ -40,6 +42,9 @@ extern int string_NativeString_NativeString_file_delete_0(char *f);
 #define file_FileStat_FileStat_ctime_0(self) (((struct stat*)self)->st_ctime)
 #define file_FileStat_FileStat_mtime_0(self) (((struct stat*)self)->st_mtime)
 #define file_FileStat_FileStat_size_0(self) (((struct stat*)self)->st_size)
+#define file_Sys_Sys_buffer_mode_full_0(self) _IOFBF
+#define file_Sys_Sys_buffer_mode_line_0(self) _IOLBF
+#define file_Sys_Sys_buffer_mode_none_0(self) _IONBF
 
 #define string_NativeString_NativeString_file_mkdir_0(p) (mkdir(p, 0777))
 #define string_NativeString_NativeString_file_getcwd_0(p) (getcwd(NULL, 0))
index 4139e45..933523a 100644 (file)
 module stream
 
 intrude import ropes
+import error
 
 in "C" `{
        #include <unistd.h>
-       #include <poll.h>
-       #include <errno.h>
        #include <string.h>
        #include <signal.h>
 `}
 
+# Any kind of error that could be produced by an operation on Streams
+class IOError
+       super Error
+end
+
 # Abstract stream class
-interface IOS
+abstract class IOS
+       # Error produced by the file stream
+       #
+       #     var ifs = new IFStream.open("donotmakethisfile.binx")
+       #     ifs.read_all
+       #     ifs.close
+       #     assert ifs.last_error != null
+       var last_error: nullable IOError = null
+
        # close the stream
        fun close is abstract
 end
 
 # Abstract input streams
-interface IStream
+abstract class IStream
        super IOS
        # Read a character. Return its ASCII value, -1 on EOF or timeout
        fun read_char: Int is abstract
@@ -36,6 +48,7 @@ interface IStream
        # Read at most i bytes
        fun read(i: Int): String
        do
+               if last_error != null then return ""
                var s = new FlatBuffer.with_capacity(i)
                while i > 0 and not eof do
                        var c = read_char
@@ -74,6 +87,7 @@ interface IStream
        # NOTE: Only LINE FEED (`\n`) is considered to delimit the end of lines.
        fun read_line: String
        do
+               if last_error != null then return ""
                assert not eof
                var s = new FlatBuffer
                append_line_to(s)
@@ -106,6 +120,7 @@ interface IStream
        # Read all the stream until the eof.
        fun read_all: String
        do
+               if last_error != null then return ""
                var s = new FlatBuffer
                while not eof do
                        var c = read_char
@@ -119,6 +134,7 @@ interface IStream
        # SEE: `read_line` for details.
        fun append_line_to(s: Buffer)
        do
+               if last_error != null then return
                loop
                        var x = read_char
                        if x == -1 then
@@ -137,7 +153,7 @@ interface IStream
 end
 
 # IStream capable of declaring if readable without blocking
-interface PollableIStream
+abstract class PollableIStream
        super IStream
 
        # Is there something to read? (without blocking)
@@ -146,7 +162,7 @@ interface PollableIStream
 end
 
 # Abstract output stream
-interface OStream
+abstract class OStream
        super IOS
        # write a string
        fun write(s: Text) is abstract
@@ -189,7 +205,8 @@ abstract class BufferedIStream
        super IStream
        redef fun read_char
        do
-               assert not eof
+               if last_error != null then return 0
+               if eof then last_error = new IOError("Stream has reached eof")
                if _buffer_pos >= _buffer.length then
                        fill_buffer
                end
@@ -203,6 +220,7 @@ abstract class BufferedIStream
 
        redef fun read(i)
        do
+               if last_error != null then return ""
                if _buffer.length == _buffer_pos then
                        if not eof then
                                fill_buffer
@@ -221,6 +239,7 @@ abstract class BufferedIStream
 
        redef fun read_all
        do
+               if last_error != null then return ""
                var s = new FlatBuffer
                while not eof do
                        var j = _buffer_pos
@@ -294,140 +313,11 @@ abstract class BufferedIStream
 end
 
 # An Input/Output Stream
-interface IOStream
+abstract class IOStream
        super IStream
        super OStream
 end
 
-##############################################################"
-
-# A File Descriptor Stream.
-abstract class FDStream
-       super IOS
-       # File description
-       var fd: Int
-
-       redef fun close do native_close(fd)
-
-       private fun native_close(i: Int): Int is extern "stream_FDStream_FDStream_native_close_1"
-       private fun native_read_char(i: Int): Int is extern "stream_FDStream_FDStream_native_read_char_1"
-       private fun native_read(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_read_3"
-       private fun native_write(i: Int, buf: NativeString, len: Int): Int is extern "stream_FDStream_FDStream_native_write_3"
-       private fun native_write_char(i: Int, c: Char): Int is extern "stream_FDStream_FDStream_native_write_char_2"
-end
-
-# An Input File Descriptor Stream.
-class FDIStream
-       super FDStream
-       super IStream
-       redef var eof: Bool = false
-       
-       redef fun read_char
-       do
-               var nb = native_read_char(fd)
-               if nb == -1 then eof = true
-               return nb
-       end
-end
-
-# An Output File Descriptor Stream.
-class FDOStream
-       super FDStream
-       super OStream
-       redef var is_writable = true
-
-       redef fun write(s)
-       do
-               var nb = native_write(fd, s.to_cstring, s.length)
-               if nb < s.length then is_writable = false
-       end
-end
-
-# An Input/Output File Descriptor Stream.
-class FDIOStream
-       super FDIStream
-       super FDOStream
-       super IOStream
-end
-
-redef interface Object
-       # returns first available stream to read or write to
-       # return null on interruption (possibly a signal)
-       protected fun poll( streams : Sequence[FDStream] ) : nullable FDStream
-       do
-               var in_fds = new Array[Int]
-               var out_fds = new Array[Int]
-               var fd_to_stream = new HashMap[Int,FDStream]
-               for s in streams do
-                       var fd = s.fd
-                       if s isa FDIStream then in_fds.add( fd )
-                       if s isa FDOStream then out_fds.add( fd )
-
-                       fd_to_stream[fd] = s
-               end
-
-               var polled_fd = intern_poll( in_fds, out_fds )
-
-               if polled_fd == null then
-                       return null
-               else
-                       return fd_to_stream[polled_fd]
-               end
-       end
-
-       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]) : nullable Int is extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{
-               int in_len, out_len, total_len;
-               struct pollfd *c_fds;
-               sigset_t sigmask;
-               int i;
-               int first_polled_fd = -1;
-               int result;
-
-               in_len = Array_of_Int_length( in_fds );
-               out_len = Array_of_Int_length( out_fds );
-               total_len = in_len + out_len;
-               c_fds = malloc( sizeof(struct pollfd) * total_len );
-
-               /* input streams */
-               for ( i=0; i<in_len; i ++ ) {
-                       int fd;
-                       fd = Array_of_Int__index( in_fds, i );
-
-                       c_fds[i].fd = fd;
-                       c_fds[i].events = POLLIN;
-               }
-
-               /* output streams */
-               for ( i=0; i<out_len; i ++ ) {
-                       int fd;
-                       fd = Array_of_Int__index( out_fds, i );
-
-                       c_fds[i].fd = fd;
-                       c_fds[i].events = POLLOUT;
-               }
-
-               /* poll all fds, unlimited timeout */
-               result = poll( c_fds, total_len, -1 );
-
-               if ( result > 0 ) {
-                       /* analyse results */
-                       for ( i=0; i<total_len; i++ )
-                               if ( c_fds[i].revents & c_fds[i].events || /* awaited event */
-                                        c_fds[i].revents & POLLHUP ) /* closed */
-                               {
-                                       first_polled_fd = c_fds[i].fd;
-                                       break;
-                               }
-
-                       return Int_as_nullable( first_polled_fd );
-               }
-               else if ( result < 0 )
-                       fprintf( stderr, "Error in Stream:poll: %s\n", strerror( errno ) );
-
-               return null_Int();
-       `}
-end
-
 # Stream to a String.
 #
 # Mainly used for compatibility with OStream type and tests.
diff --git a/lib/standard/stream_nit.c b/lib/standard/stream_nit.c
deleted file mode 100644 (file)
index 14bfece..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* This file is part of NIT ( http://www.nitlanguage.org ).
- *
- * Copyright 2004-2008 Jean Privat <jean@pryen.org>
- *
- * This file is free software, which comes along with NIT.  This software is
- * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
- * PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
- * is kept unaltered, and a notification of the changes is added.
- * You  are  allowed  to  redistribute it and sell it, alone or is a part of
- * another product.
- */
-
-#include "stream_nit.h"
-
-int stream_FDStream_FDStream_native_read_char_1(void *s, int fd) {
-       int result;
-       char buf;
-       ssize_t r = read(fd, &buf, 1);
-       if (r == 0)
-               result = -1;
-       else
-               result = buf;
-       return result;
-}
diff --git a/lib/standard/stream_nit.h b/lib/standard/stream_nit.h
deleted file mode 100644 (file)
index 3a4d51e..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef __STREAM_NIT_H
-#define __STREAM_NIT_H
-/* This file is part of NIT ( http://www.nitlanguage.org ).
- *
- * Copyright 2004-2008 Jean Privat <jean@pryen.org>
- *
- * This file is free software, which comes along with NIT.  This software is
- * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without  even  the implied warranty of  MERCHANTABILITY or  FITNESS FOR A 
- * PARTICULAR PURPOSE.  You can modify it is you want,  provided this header
- * is kept unaltered, and a notification of the changes is added.
- * You  are  allowed  to  redistribute it and sell it, alone or is a part of
- * another product.
- */
-
-#include <unistd.h>
-
-int stream_FDStream_FDStream_native_read_char_1(void *s, int fd);
-
-#define stream_FDStream_FDStream_native_close_1(self, p0) (close(p0))
-#define stream_FDStream_FDStream_native_read_3(s, i, b, l) read((i), ((b)), ((l)))
-#define stream_FDStream_FDStream_native_write_3(s, i, b, l) write((i), ((b)), ((l)))
-#define stream_FDStream_FDStream_native_write_char_2(s, i, c) write((i), (char[]){(c)}, 1 )
-
-#endif
index 604386f..def85f4 100644 (file)
@@ -394,7 +394,7 @@ abstract class Text
        # This method is mainly used to remove the LINE_FEED character from lines of text.
        fun chomp: SELFTYPE
        do
-               if self.chars.last != '\n' then return self
+               if self.is_empty or self.chars.last != '\n' then return self
                return substring(0, length-1)
        end
 
index 2f9e20f..c82f883 100755 (executable)
 hash=$1
 shift
 
-set +x
+set -x
 
 local_repo=nit/
-remote_repo=privat
-
 tools_dir=misc/jenkins/
 
 cd $local_repo
 git clean -fdxq .
+git fetch origin
 
-git fetch $remote_repo
-git checkout $hash
+if ! git checkout $hash; then
+       exit 1
+fi
 
-# Make nitg and tools
-$tools_dir/unitrun.sh "run-make-0initial_make" make
+# Make nitg (quickly)
+$tools_dir/unitrun.sh "run-make-csrc" make -C c_src
+$tools_dir/unitrun.sh "run-make-version" src/git-gen-version.sh
+$tools_dir/unitrun.sh "run-make-nitg_0" c_src/nitg -o bin/nitg_0 src/nitg.nit
+$tools_dir/unitrun.sh "run-make-nitg" bin/nitg_0 -o bin/nitg src/nitg.nit
 
 # Make nitester
 $tools_dir/unitrun.sh "run-make-nitester" make -C contrib/nitester/
index 10dfd38..ed4e2f7 100755 (executable)
@@ -28,7 +28,7 @@ shift
 if env time --quiet -f%U true 2>/dev/null; then
        TIME="env time --quiet -f%U -o '${name}.t.out'"
 elif env time -f%U true 2>/dev/null; then
-       TIME="env time -f%U -o '${name}.t.out'"
+       TIME="env time -f%U -o ${name}.t.out"
 else
        TIME=
 fi
index 737b4a1..18a579a 100644 (file)
@@ -131,25 +131,31 @@ hi def link NITFFIDelimiters              Keyword
 " FFI Python
 syntax include @FFIPython syntax/python.vim
 unlet b:current_syntax
-syn match NITFFILanguage       '"Python"' nextgroup=NITFFIBlockPython skipwhite
+syn match NITFFILanguage       /\c"Python"/ nextgroup=NITFFIBlockPython skipwhite
 syn region NITFFIBlockPython matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIPython
 
 " FFI Java
 syntax include @FFIJava syntax/java.vim
 unlet b:current_syntax
-syn match NITFFILanguage       '"Java"' nextgroup=NITFFIBlockJava skipwhite
+syn match NITFFILanguage       /\c"Java\(\| inner\)"/ nextgroup=NITFFIBlockJava skipwhite
 syn region NITFFIBlockJava matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIJava
 
 " FFI C++
 syntax include @FFICpp syntax/cpp.vim
 unlet b:current_syntax
-syn match NITFFILanguage       '"C++"' nextgroup=NITFFIBlockCpp skipwhite
+syn match NITFFILanguage       /\c"C++\(\| header\| body\)"/ nextgroup=NITFFIBlockCpp skipwhite
 syn region NITFFIBlockCpp matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFICpp
 
+" FFI Objective-C
+syntax include @FFIObjC syntax/objc.vim
+unlet b:current_syntax
+syn match NITFFILanguage       /\c"ObjC\(\| Header\| Body\)"/ nextgroup=NITFFIBlockObjC skipwhite
+syn region NITFFIBlockObjC matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIObjC
+
 " FFI C (the last one is the default)
 syntax include @FFIC syntax/c.vim
 unlet b:current_syntax
-syn match NITFFILanguage               '"C\(\| header\| body\)"'       nextgroup=NITFFIBlockC skipwhite
+syn match NITFFILanguage               /\c"C\(\| header\| body\)"/     nextgroup=NITFFIBlockC skipwhite
 syn region NITFFIBlockC matchgroup=NITFFI start='`{' matchgroup=NITFFI end='`}' keepend fold contains=@FFIC
 
 hi def link NITFFILanguage             Define
index 5fac6a4..0864629 100644 (file)
@@ -25,6 +25,178 @@ nitpretty [*options*]... FILE
 `--check`
 :   Check format of Nit source files
 
+# SPECIFICATION
+
+The specification of the pretty printing is described here.
+
+* Default indentation level is one `'\t'` character and is increased by one for
+  each indentation level.
+* Default line max-size is 80.
+
+### COMMENTS
+
+There is many categories of comments:
+
+`Licence comments` are attached to the top of the file no blank line before,
+one after.
+
+~~~nitish
+# This is a licence comment
+
+# Documentation for module `foo`
+module foo
+~~~
+
+`ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
+
+They are printed before the definition with a blank line before and no after
+at the same indentation level than the definition.
+
+~~~nitish
+# Documentation for module `foo`
+module foo
+
+# Documentation for class `Bar`
+class Bar
+     # Documentation for method `baz`
+     fun baz do end
+end
+~~~
+
+`Block comments` are comments composed of one or more line rattached to nothing.
+They are displayed with one blank line before and after at current indent level.
+
+~~~nitish
+<blank>
+# block
+# comment
+<blank>
+~~~
+
+`Attached comments` are comments attached to a production.
+They are printed as this.
+
+~~~nitish
+fun foo do # attached comment
+end
+~~~
+
+`nitpretty` automatically remove multiple blanks between comments:
+
+~~~nitish
+# Licence
+# ...
+<blank>
+# Block comment
+~~~
+
+### INLINING
+
+Productions are automatically inlined when possible.
+
+Conditions:
+
+* The production must be syntactically inlinable
+* The inlined production length is less than `PrettyPrinterVisitor::max-size`
+* The production do not contains any comments
+
+### MODULES
+
+* There is a blank between the module declaration and its imports
+* There is no blank between imports and only one after
+* There is a blank between each extern block definition
+* There is a blank between each class definition
+* There is no blank line at the end of the module
+
+~~~nitish
+# Documentation for module `foo`
+module foo
+
+import a
+import b
+import c
+
+# Documentation for class `Bar`
+class Bar end
+
+class Baz end # not a `ADoc` comment
+~~~
+
+### CLASSES
+
+* There is no blank between the class definition and its super-classes declarations
+* There is no blank between two inlined property definition
+* There is a blank between each block definition
+* There no blank line at the end of the class definition
+
+~~~nitish
+# Documentation for class `Bar`
+class Bar end
+
+class Baz
+    super Bar
+
+    fun a is abstract
+    private fun b do end
+
+    fun c do
+        # ...
+    end
+end
+~~~
+
+Generic types have no space after or before brackets and are separated by a comma and a space:
+
+~~~nitish
+class A[E: Type1, F: Type1] end
+~~~
+
+### BLOCKS
+
+* Inlined productions have no blank lines between them
+* Block productions have a blank before and after
+
+~~~nitish
+var a = 10
+var b = 0
+
+if a > b then
+     is positive
+     print "positive"
+end
+
+print "end"
+~~~
+
+### CALLS AND BINARY OPS
+
+Arguments are always printed separated with a comma and a space:
+
+~~~nitish
+foo(a, b, c)
+~~~
+
+Binary ops are always printed wrapped with spaces:
+
+~~~nitish
+var c = 1 + 2
+~~~
+
+Calls and binary ops can be splitted to fit the `max-size` constraint.
+Breaking priority is given to arguments declaration after the comma.
+
+~~~nitish
+return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
+    "cccccccccccccccccccccccccc")
+~~~
+
+Binary ops can also be broken to fit the `max-size` limit:
+
+~~~nitish
+return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
+    "cccccccccccccccccccccccccc"
+~~~
+
 # SEE ALSO
 
 The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
index 13f500c..df006f9 100644 (file)
@@ -52,17 +52,21 @@ define([
                },
 
                _create: function() {
-                       this.element
-                               .attr(this.options.fieldAttrs)
-                               .keydown($.proxy(this._doKeyDown, this))
-                               .keyup($.proxy(this._doKeyUp, this))
-
+                       // set widget options
+                       this.element.attr(this.options.fieldAttrs);
+                       // event dispatch
+                       this._on(this.element, {
+                               "keydown": this._doKeyDown,
+                               "keyup": this._doKeyUp,
+                               "input": this._doInput
+                       });
+                       // add result table element once
                        this._table = $("<table/>")
                                .attr("id", this.options.tableID)
                                .css(this.options.tableCSS)
                                .css("min-width", this.element.outerWidth());
                        $("body").append(this._table);
-
+                       // make table disappear when a click occurs outside
                        $(document).click($.proxy(this.closeTable, this));
                },
 
@@ -94,11 +98,14 @@ define([
                                        this.closeTable();
                                        return true;
                                default: // Other keys
-                                       utils.delayEvent($.proxy(this.search, this));
                                        return true;
                        }
                },
 
+               _doInput: function(event) {
+                       utils.delayEvent($.proxy(this.search, this));
+               },
+
                /* Result lookup */
 
                _getResults: function(query) {
index 9794beb..09883db 100644 (file)
@@ -237,7 +237,7 @@ redef class MModule
        redef fun tpl_declaration do
                var tpl = new Template
                tpl.add "<span>module "
-               tpl.add tpl_link
+               tpl.add tpl_namespace
                tpl.add "</span>"
                return tpl
        end
index 1780167..248a8ee 100644 (file)
@@ -905,7 +905,7 @@ class NitdocModule
 
                # Graph
                var mmodules = new HashSet[MModule]
-               mmodules.add_all mmodule.in_nesting.direct_greaters
+               mmodules.add_all mmodule.nested_mmodules
                mmodules.add_all imports
                if clients.length < 10 then mmodules.add_all clients
                mmodules.add mmodule
@@ -1498,7 +1498,12 @@ class NitdocProperty
 
        private fun tpl_properties(parent: TplSection) do
                # intro title
-               var section = new TplSection.with_title("intro", "Introduction")
+               var ns = mproperty.intro.mclassdef.mmodule.tpl_namespace
+               var section = new TplSection("intro")
+               var title = new Template
+               title.add "Introduction in "
+               title.add ns
+               section.title = title
                section.summary_title = "Introduction"
                section.add_child tpl_mpropdef_article(mproperty.intro)
                parent.add_child section
@@ -1516,7 +1521,7 @@ class NitdocProperty
                                parent.add_child new TplSection(mentity.nitdoc_id)
                        else if mentity isa MModule then
                                var ssection = new TplSection(mentity.nitdoc_id)
-                               var title = new Template
+                               title = new Template
                                title.add "in "
                                title.add mentity.tpl_namespace
                                ssection.title = title
index 353ecdf..7624626 100644 (file)
@@ -60,28 +60,28 @@ class TplPage
                var css = (self.shareurl / "css").html_escape
                var vendors = (self.shareurl / "vendors").html_escape
 
-               add "<!DOCTYPE html>"
-               add "<head>"
-               add " <meta charset='utf-8'/>"
-               add " <!--link rel='stylesheet' href='{css}/Nitdoc.UI.css' type='text/css'/-->"
-               add " <link rel='stylesheet' href='{vendors}/bootstrap/css/bootstrap.min.css'/>"
-               add " <link rel='stylesheet' href='{css}/nitdoc.bootstrap.css'/>"
-               add " <link rel='stylesheet' href='{css}/nitdoc.css'/>"
-               add " <link rel='stylesheet' href='{css}/Nitdoc.QuickSearch.css'/>"
-               add " <link rel='stylesheet' href='{css}/Nitdoc.ModalBox.css'/>"
-               add " <link rel='stylesheet' href='{css}/Nitdoc.GitHub.css'/>"
-               add " <title>{title}</title>"
-               add "</head>"
+               addn "<!DOCTYPE html>"
+               addn "<head>"
+               addn " <meta charset='utf-8'/>"
+               addn " <!--link rel='stylesheet' href='{css}/Nitdoc.UI.css' type='text/css'/-->"
+               addn " <link rel='stylesheet' href='{vendors}/bootstrap/css/bootstrap.min.css'/>"
+               addn " <link rel='stylesheet' href='{css}/nitdoc.bootstrap.css'/>"
+               addn " <link rel='stylesheet' href='{css}/nitdoc.css'/>"
+               addn " <link rel='stylesheet' href='{css}/Nitdoc.QuickSearch.css'/>"
+               addn " <link rel='stylesheet' href='{css}/Nitdoc.ModalBox.css'/>"
+               addn " <link rel='stylesheet' href='{css}/Nitdoc.GitHub.css'/>"
+               addn " <title>{title}</title>"
+               addn "</head>"
                add "<body"
                for attr in body_attrs do add attr
-               add ">"
+               addn ">"
        end
 
        # Render the topmenu template
        private fun render_topmenu do
-               add " <div class='row'>"
+               addn " <div class='row'>"
                add topmenu
-               add " </div>"
+               addn " </div>"
        end
 
        # Render the sidebar
@@ -99,9 +99,9 @@ class TplPage
        private fun render_content do
                for section in sections do add section
                if footer != null then
-                       add "<div class='well footer'>"
+                       addn "<div class='well footer'>"
                        add footer.as(not null)
-                       add "</div>"
+                       addn "</div>"
                end
        end
 
@@ -110,41 +110,41 @@ class TplPage
                var vendors = (self.shareurl / "vendors").html_escape
                var js = (self.shareurl / "js").html_escape
 
-               add "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
-               add "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
-               add "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
-               add "<script data-main='{js}/nitdoc' src='{js}/lib/require.js'></script>"
+               addn "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
+               addn "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
+               addn "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
+               addn "<script data-main='{js}/nitdoc' src='{js}/lib/require.js'></script>"
                for script in scripts do add script
-               add """<script>
+               addn """<script>
                        $(function () {
                                $("[data-toggle='tooltip']").tooltip();
                                $("[data-toggle='popover']").popover();
                        });
                </script>"""
-               add "</body>"
-               add "</html>"
+               addn "</body>"
+               addn "</html>"
        end
 
        # Render the whole page
        redef fun rendering do
                render_head
-               add "<div class='container-fluid'>"
+               addn "<div class='container-fluid'>"
                render_topmenu
-               add " <div class='row' id='content'>"
+               addn " <div class='row' id='content'>"
                if sidebar != null then
-                       add "<div class='col col-xs-3 col-lg-2'>"
+                       addn "<div class='col col-xs-3 col-lg-2'>"
                        render_sidebar
-                       add "</div>"
-                       add "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
+                       addn "</div>"
+                       addn "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
                        render_content
-                       add "</div>"
+                       addn "</div>"
                else
-                       add "<div class='col col-xs-12'>"
+                       addn "<div class='col col-xs-12'>"
                        render_content
-                       add "</div>"
+                       addn "</div>"
                end
-               add " </div>"
-               add "</div>"
+               addn " </div>"
+               addn "</div>"
                render_footer
        end
 end
@@ -182,7 +182,7 @@ class TplTopMenu
                end
                tpl.add ">"
                tpl.add content
-               tpl.add "</li>"
+               tpl.addn "</li>"
                add_raw(tpl)
        end
 
@@ -193,27 +193,27 @@ class TplTopMenu
 
        redef fun rendering do
                if brand == null and elts.is_empty then return
-               add "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
-               add " <div class='container-fluid'>"
-               add "  <div class='navbar-header'>"
+               addn "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
+               addn " <div class='container-fluid'>"
+               addn "  <div class='navbar-header'>"
                add "   <button type='button' class='navbar-toggle' "
-               add "       data-toggle='collapse' data-target='#topmenu-collapse'>"
-               add "    <span class='sr-only'>Toggle menu</span>"
-               add "    <span class='icon-bar'></span>"
-               add "    <span class='icon-bar'></span>"
-               add "    <span class='icon-bar'></span>"
-               add "   </button>"
+               addn "       data-toggle='collapse' data-target='#topmenu-collapse'>"
+               addn "    <span class='sr-only'>Toggle menu</span>"
+               addn "    <span class='icon-bar'></span>"
+               addn "    <span class='icon-bar'></span>"
+               addn "    <span class='icon-bar'></span>"
+               addn "   </button>"
                if brand != null then add brand.as(not null)
-               add "  </div>"
-               add "  <div class='collapse navbar-collapse' id='topmenu-collapse'>"
+               addn "  </div>"
+               addn "  <div class='collapse navbar-collapse' id='topmenu-collapse'>"
                if not elts.is_empty then
-                       add "<ul class='nav navbar-nav'>"
+                       addn "<ul class='nav navbar-nav'>"
                        for elt in elts do add elt
-                       add "</ul>"
+                       addn "</ul>"
                end
-               add "  </div>"
-               add " </div>"
-               add "</nav>"
+               addn "  </div>"
+               addn " </div>"
+               addn "</nav>"
        end
 end
 
@@ -233,9 +233,9 @@ class TplSidebar
        redef fun rendering do
                if boxes.is_empty then return
                order_boxes
-               add "<div id='sidebar'>"
+               addn "<div id='sidebar'>"
                for box in boxes do add box
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -296,16 +296,16 @@ class TplSideBox
                if content == null then return
                var open = ""
                if is_open then open = "in"
-               add "<div class='panel'>"
-               add " <div class='panel-heading'>"
+               addn "<div class='panel'>"
+               addn " <div class='panel-heading'>"
                add "  <a data-toggle='collapse' data-parent='#sidebar' data-target='#box_{id}' href='#'>"
                add title
-               add "  </a>"
-               add " </div>"
-               add " <div id='box_{id}' class='panel-body collapse {open}'>"
+               addn "  </a>"
+               addn " </div>"
+               addn " <div id='box_{id}' class='panel-body collapse {open}'>"
                add content.as(not null)
-               add " </div>"
-               add "</div>"
+               addn " </div>"
+               addn "</div>"
        end
 end
 
@@ -332,18 +332,18 @@ class TplSummary
 
        redef fun rendering do
                if children.is_empty then return
-               add "<div class='panel'>"
-               add " <div class='panel-heading'>"
+               addn "<div class='panel'>"
+               addn " <div class='panel-heading'>"
                add "  <a data-toggle='collapse' data-parent='#sidebar' data-target='#box-sum' href='#'>"
                add "Summary"
-               add "  </a>"
-               add " </div>"
-               add " <div id='box-sum' class='summary collapse in'>"
-               add " <ul class='nav'>"
+               addn "  </a>"
+               addn " </div>"
+               addn " <div id='box-sum' class='summary collapse in'>"
+               addn " <ul class='nav'>"
                for entry in children do add entry
-               add " </ul>"
-               add " </div>"
-               add "</div>"
+               addn " </ul>"
+               addn " </div>"
+               addn "</div>"
        end
 end
 
@@ -364,11 +364,11 @@ class TplSummaryEntry
                add "<li>"
                add text
                if not children.is_empty then
-                       add "<ul class='nav'>"
+                       addn "\n<ul class='nav'>"
                        for entry in children do add entry
-                       add "</ul>"
+                       addn "</ul>"
                end
-               add "</li>"
+               addn  "</li>"
        end
 end
 
@@ -444,23 +444,23 @@ class TplSection
        super TplSectionElt
 
        redef fun rendering do
-               add "<section id='{id}' class='{css_classes.join(" ")}'>"
+               addn "<section id='{id}' class='{css_classes.join(" ")}'>"
                if title != null then
                        var lvl = hlvl
                        if lvl == 2 then title_classes.add "well well-sm"
-                       add "<h{lvl} class='{title_classes.join(" ")}'>"
-                       add title.as(not null)
-                       add "</h{lvl}>"
+                       addn "<h{lvl} class='{title_classes.join(" ")}'>"
+                       addn title.as(not null)
+                       addn "</h{lvl}>"
                end
                if subtitle != null then
-                       add "<div class='info subtitle'>"
-                       add subtitle.as(not null)
-                       add "</div>"
+                       addn "<div class='info subtitle'>"
+                       addn subtitle.as(not null)
+                       addn "</div>"
                end
                for child in children do
                        add child
                end
-               add "</section>"
+               addn "</section>"
        end
 end
 
@@ -488,23 +488,23 @@ class TplArticle
 
        redef fun rendering do
                if is_empty then return
-               add "<article id='{id}' class='{css_classes.join(" ")}'>"
+               addn "<article id='{id}' class='{css_classes.join(" ")}'>"
                if source_link != null then
                        add "<div class='source-link'>"
                        add source_link.as(not null)
-                       add "</div>"
+                       addn "</div>"
                end
                if title != null then
                        var lvl = hlvl
                        if lvl == 2 then title_classes.add "well well-sm"
                        add "<h{lvl} class='{title_classes.join(" ")}'>"
                        add title.as(not null)
-                       add "</h{lvl}>"
+                       addn "</h{lvl}>"
                end
                if subtitle != null then
                        add "<div class='info subtitle'>"
                        add subtitle.as(not null)
-                       add "</div>"
+                       addn "</div>"
                end
                if content != null then
                        add content.as(not null)
@@ -512,7 +512,7 @@ class TplArticle
                for child in children do
                        add child
                end
-               add """</article>"""
+               addn """</article>"""
        end
 
        redef fun is_empty: Bool do
@@ -534,7 +534,7 @@ class TplDefinition
        var location: nullable Streamable = null is writable
 
        private fun render_info do
-               add "<div class='info text-right'>"
+               addn "<div class='info text-right'>"
                if namespace != null then
                        if comment == null then
                                add "<span class=\"noComment\">no comment for </span>"
@@ -545,7 +545,7 @@ class TplDefinition
                        add " "
                        add location.as(not null)
                end
-               add "</div>"
+               addn "</div>"
        end
 
        private fun render_comment do
@@ -553,10 +553,10 @@ class TplDefinition
        end
 
        redef fun rendering do
-               add "<div class='definition'>"
+               addn "<div class='definition'>"
                render_comment
                render_info
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -570,20 +570,20 @@ class TplClassDefinition
        init do end
 
        redef fun rendering do
-               add "<div class='definition'>"
+               addn "<div class='definition'>"
                render_comment
                render_info
                render_list("Introduces", intros)
                render_list("Redefines", redefs)
-               add "</div>"
+               addn "</div>"
        end
 
        private fun render_list(name: String, elts: Array[TplListElt]) do
                if elts.is_empty then return
-               add "<h5>{name.html_escape}</h5>"
-               add "<ul class='list-unstyled list-definition'>"
+               addn "<h5>{name.html_escape}</h5>"
+               addn "<ul class='list-unstyled list-definition'>"
                for elt in elts do add elt
-               add "</ul>"
+               addn "</ul>"
        end
 end
 
@@ -597,47 +597,47 @@ class TplSearchPage
 
        redef fun rendering do
                var title = self.title
-               if title != null then add "<h1>{title.to_s.html_escape}</h1>"
-               add "<div class='container-fluid'>"
-               add " <div class='row'>"
+               if title != null then addn "<h1>{title.to_s.html_escape}</h1>"
+               addn "<div class='container-fluid'>"
+               addn " <div class='row'>"
                if not modules.is_empty then
-                       add "<div class='col-xs-4'>"
-                       add "<h3>Modules</h3>"
-                       add "<ul>"
+                       addn "<div class='col-xs-4'>"
+                       addn "<h3>Modules</h3>"
+                       addn "<ul>"
                        for m in modules do
                                add "<li>"
                                add m
-                               add "</li>"
+                               addn "</li>"
                        end
-                       add "</ul>"
-                       add "</div>"
+                       addn "</ul>"
+                       addn "</div>"
                end
                if not classes.is_empty then
-                       add "<div class='col-xs-4'>"
-                       add "<h3>Classes</h3>"
-                       add "<ul>"
+                       addn "<div class='col-xs-4'>"
+                       addn "<h3>Classes</h3>"
+                       addn "<ul>"
                        for c in classes do
                                add "<li>"
                                add c
-                               add "</li>"
+                               addn "</li>"
                        end
-                       add "</ul>"
-                       add "</div>"
+                       addn "</ul>"
+                       addn "</div>"
                end
                if not props.is_empty then
-                       add "<div class='col-xs-4'>"
-                       add "<h3>Properties</h3>"
-                       add "<ul>"
+                       addn "<div class='col-xs-4'>"
+                       addn "<h3>Properties</h3>"
+                       addn "<ul>"
                        for p in props do
                                add "<li>"
                                add p
-                               add "</li>"
+                               addn "</li>"
                        end
-                       add "</ul>"
-                       add "</div>"
+                       addn "</ul>"
+                       addn "</div>"
                end
-               add " </div>"
-               add "</div>"
+               addn " </div>"
+               addn "</div>"
        end
 end
 
@@ -700,9 +700,9 @@ class TplList
 
        redef fun rendering do
                if elts.is_empty then return
-               add "<ul class='{css_classes.join(" ")}'>"
+               addn "<ul class='{css_classes.join(" ")}'>"
                for elt in elts do add elt
-               add "</ul>"
+               addn "</ul>"
        end
 end
 
@@ -737,7 +737,7 @@ class TplListItem
        redef fun rendering do
                add "<li class='{css_classes.join(" ")}'>"
                add content
-               add "</li>"
+               addn "</li>"
        end
 end
 
@@ -755,9 +755,9 @@ class TplTab
        var css_classes = new Array[String]
 
        redef fun rendering do
-               add "<div class='tab-content'>"
+               addn "<div class='tab-content'>"
                for panel in panels do add panel
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -796,9 +796,9 @@ class TplTabPanel
        redef fun rendering do
                add "<div class='tab-pane {css_classes.join(" ")}"
                if is_active then add "active"
-               add "' id='{id}'>"
+               addn "' id='{id}'>"
                if content != null then add content.as(not null)
-               add "</div>"
+               addn "</div>"
        end
 end
 
@@ -886,9 +886,9 @@ class TplScript
        redef fun rendering do
                add "<script"
                for attr in attrs do add attr
-               add ">"
+               addn ">"
                render_content
-               add "</script>"
+               addn "</script>"
        end
 end
 
@@ -905,17 +905,17 @@ class TplPiwikScript
                if tracker_url.chars.last != '/' then tracker_url += "/"
                tracker_url = "://{tracker_url}".to_json
 
-               add "<!-- Piwik -->"
-               add "var _paq = _paq || [];"
-               add " _paq.push([\"trackPageView\"]);"
-               add " _paq.push([\"enableLinkTracking\"]);"
-               add "(function() \{"
-               add " var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + {tracker_url};"
-               add " _paq.push([\"setTrackerUrl\", u+\"piwik.php\"]);"
-               add " _paq.push([\"setSiteId\", {site_id}]);"
-               add " var d=document, g=d.createElement(\"script\"), s=d.getElementsByTagName(\"script\")[0]; g.type=\"text/javascript\";"
-               add " g.defer=true; g.async=true; g.src=u+\"piwik.js\"; s.parentNode.insertBefore(g,s);"
-               add "\})();"
+               addn "<!-- Piwik -->"
+               addn "var _paq = _paq || [];"
+               addn " _paq.push([\"trackPageView\"]);"
+               addn " _paq.push([\"enableLinkTracking\"]);"
+               addn "(function() \{"
+               addn " var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + {tracker_url};"
+               addn " _paq.push([\"setTrackerUrl\", u+\"piwik.php\"]);"
+               addn " _paq.push([\"setSiteId\", {site_id}]);"
+               addn " var d=document, g=d.createElement(\"script\"), s=d.getElementsByTagName(\"script\")[0]; g.type=\"text/javascript\";"
+               addn " g.defer=true; g.async=true; g.src=u+\"piwik.js\"; s.parentNode.insertBefore(g,s);"
+               addn "\})();"
        end
 end
 
index a2dda26..019d201 100644 (file)
@@ -32,6 +32,7 @@ import c
 import cpp
 import java
 import extra_java_files
+import objc
 
 redef class MModule
        # Does this module uses the FFI?
diff --git a/src/ffi/objc.nit b/src/ffi/objc.nit
new file mode 100644 (file)
index 0000000..9ab6f7f
--- /dev/null
@@ -0,0 +1,220 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# FFI support for Objective-C
+#
+# Compiles all Objective-C code with clang. The user module must define
+# the framework used.
+#
+# This module is heavily based on the C++ FFI.
+module objc
+
+import extern_classes
+import c
+
+redef class FFILanguageAssignationPhase
+       # The Objective-C language visitor
+       var objc_language: FFILanguage = new ObjCLanguage(self)
+end
+
+redef class MModule
+       private var objc_file: nullable ObjCCompilationUnit = null
+end
+
+# The Objective-C langugage visitor
+class ObjCLanguage
+       super FFILanguage
+
+       redef fun identify_language(n) do return n.is_objc
+
+       redef fun compile_module_block(block, ecc, mmodule)
+       do
+               if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
+
+               if block.is_objc_header then
+                       mmodule.objc_file.header_custom.add block.location.as_line_pragma
+                       mmodule.objc_file.header_custom.add block.code
+               else if block.is_objc_body then
+                       mmodule.objc_file.body_custom.add block.location.as_line_pragma
+                       mmodule.objc_file.body_custom.add block.code
+               end
+       end
+
+       redef fun compile_extern_method(block, m, ecc, mmodule)
+       do
+               if mmodule.objc_file == null then mmodule.objc_file = new ObjCCompilationUnit
+
+               var mpropdef = m.mpropdef
+               var recv_mtype = mpropdef.mclassdef.bound_mtype
+               var csignature = mpropdef.mproperty.build_csignature(
+                       recv_mtype, mmodule, "___impl", long_signature, from_objc_call_context)
+
+               var fc = new CFunction(csignature)
+               fc.decls.add block.location.as_line_pragma
+               fc.exprs.add block.code
+               mmodule.objc_file.add_exported_function fc
+       end
+
+       redef fun compile_extern_class(block, m, ecc, mmodule) do end
+
+       redef fun get_ftype(block, m) do return new ForeignObjCType(block.code)
+
+       redef fun compile_to_files(mmodule, compdir)
+       do
+               var objc_file = mmodule.objc_file
+               assert objc_file != null
+
+               # write .m and _m.h file
+               mmodule.objc_file.header_c_types.add """
+       #include "{{{mmodule.cname}}}._ffi.h"
+"""
+
+               var file = objc_file.write_to_files(mmodule, compdir)
+
+               # add compilation to makefile
+               mmodule.ffi_files.add file
+       end
+
+       redef fun compile_callback(callback, mmodule, mainmodule, ecc)
+       do
+               callback.compile_callback_to_objc(mmodule, mainmodule)
+       end
+end
+
+redef class AExternCodeBlock
+       # Is this Objective-C code?
+       fun is_objc : Bool do return language_name != null and
+               (language_name_lowered == "objc" or language_name_lowered.has_prefix("objc "))
+
+       # Is this Objective-C code for the body file?
+       fun is_objc_body : Bool do return language_name != null and
+               (language_name_lowered == "objc" or language_name_lowered == "objc body")
+
+       # Is this Objective-C code for the header file?
+       fun is_objc_header : Bool do return language_name != null and
+               (language_name_lowered == "objc header")
+end
+
+private class ObjCCompilationUnit
+       super CCompilationUnit
+
+       # Write this compilation unit to Objective-C source files
+       fun write_to_files(mmodule: MModule, compdir: String): ExternObjCFile
+       do
+               var base_name = "{mmodule.cname}._ffi"
+
+               var h_file = "{base_name}_m.h"
+               var guard = "{mmodule.cname.to_s.to_upper}_NIT_OBJC_H"
+               write_header_to_file(mmodule, compdir/h_file, new Array[String], guard)
+
+               var c_file = "{base_name}.m"
+               write_body_to_file(mmodule, compdir/c_file, ["\"{h_file}\""])
+
+               files.add compdir/c_file
+
+               mmodule.c_linker_options = "{mmodule.c_linker_options} -lobjc"
+
+               return new ExternObjCFile(compdir/c_file, mmodule)
+       end
+end
+
+# A Objective-C file
+class ExternObjCFile
+       super ExternFile
+
+       # Associated `MModule`
+       var mmodule: MModule
+
+       redef fun makefile_rule_name do return "{filename.basename(".m")}_m.o"
+       redef fun makefile_rule_content do
+               return "clang $(CFLAGS) -c {filename.basename("")} -o {makefile_rule_name}"
+       end
+       redef fun compiles_to_o_file do return true
+end
+
+# An Objective-C type
+class ForeignObjCType
+       super ForeignType
+
+       # Type name
+       var objc_type: String
+end
+
+redef class NitniCallback
+       # Compile this callback to be callable from Objective-C
+       fun compile_callback_to_objc(mmodule: MModule, mainmodule: MModule) do end
+end
+
+redef class MExplicitCall
+       redef fun compile_callback_to_objc(mmodule, mainmodule)
+       do
+               var mproperty = mproperty
+               assert mproperty isa MMethod
+
+               var objc_signature = mproperty.build_csignature(recv_mtype, mainmodule, null, short_signature, from_objc_call_context)
+               var ccall = mproperty.build_ccall(recv_mtype, mainmodule, null, long_signature, from_objc_call_context, null)
+               var fc = new CFunction(objc_signature)
+               fc.exprs.add ccall
+               mmodule.objc_file.add_local_function fc
+       end
+end
+
+# Calls withing Objective-C code
+private fun objc_call_context: ObjCCallContext do return once new ObjCCallContext
+
+# Calls from C to Objective-C
+private fun to_objc_call_context: ToObjCCallContext do return once new ToObjCCallContext
+
+# Calls from Objective-C to C
+private fun from_objc_call_context: FromObjCCallContext do return once new FromObjCCallContext
+
+private class ObjCCallContext
+       super CallContext
+
+       redef fun name_mtype(mtype)
+       do
+               if mtype isa MClassType then
+                       var ftype = mtype.mclass.ftype
+                       if ftype isa ForeignObjCType then
+                               return ftype.objc_type
+                       end
+               end
+
+               return mtype.cname
+       end
+end
+
+private class ToObjCCallContext
+       super ObjCCallContext
+
+       redef fun cast_to(mtype, name)
+       do
+               if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
+                       return "(void*)({name})"
+               else return name
+       end
+end
+
+private class FromObjCCallContext
+       super ObjCCallContext
+
+       redef fun cast_from(mtype, name)
+       do
+               if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
+                       return "({name_mtype(mtype)})({name})"
+               else return name
+       end
+end
index e3cc1ca..5d24e6c 100644 (file)
@@ -21,6 +21,7 @@ import literal
 import semantize
 private import parser::tables
 import mixin
+import primitive_types
 
 redef class ToolContext
        # --discover-call-trace
@@ -71,10 +72,12 @@ class NaiveInterpreter
 
        init
        do
-               self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
-               init_instance_primitive(self.true_instance)
-               self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
-               init_instance_primitive(self.false_instance)
+               if mainmodule.model.get_mclasses_by_name("Bool") != null then
+                       self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
+                       init_instance_primitive(self.true_instance)
+                       self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
+                       init_instance_primitive(self.false_instance)
+               end
                self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null)
        end
 
@@ -796,6 +799,12 @@ redef class AMethPropdef
                else if pname == "exit" then
                        exit(args[1].to_i)
                        abort
+               else if pname == "buffer_mode_full" then
+                       return v.int_instance(sys.buffer_mode_full)
+               else if pname == "buffer_mode_line" then
+                       return v.int_instance(sys.buffer_mode_line)
+               else if pname == "buffer_mode_none" then
+                       return v.int_instance(sys.buffer_mode_none)
                else if pname == "sys" then
                        return v.mainobj
                else if cname == "Int" then
@@ -1013,43 +1022,51 @@ redef class AMethPropdef
                        end
                else if cname == "NativeFile" then
                        if pname == "native_stdout" then
-                               var instance = new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdout)
+                               var inst = new PrimitiveNativeFile.native_stdout
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "native_stdin" then
-                               var instance = new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, sys.stdin)
+                               var inst = new PrimitiveNativeFile.native_stdin
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "native_stderr" then
-                               var instance = new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, sys.stderr)
+                               var inst = new PrimitiveNativeFile.native_stderr
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "io_open_read" then
                                var a1 = args[1].val.as(Buffer)
-                               var instance = new PrimitiveInstance[IStream](mpropdef.mclassdef.mclass.mclass_type, new IFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_read(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        else if pname == "io_open_write" then
                                var a1 = args[1].val.as(Buffer)
-                               var instance = new PrimitiveInstance[OStream](mpropdef.mclassdef.mclass.mclass_type, new OFStream.open(a1.to_s))
+                               var inst = new PrimitiveNativeFile.io_open_write(a1.to_s)
+                               var instance = new PrimitiveInstance[PrimitiveNativeFile](mpropdef.mclassdef.mclass.mclass_type, inst)
                                v.init_instance_primitive(instance)
                                return instance
                        end
                        var recvval = args.first.val
                        if pname == "io_write" then
                                var a1 = args[1].val.as(Buffer)
-                               recvval.as(OStream).write(a1.substring(0, args[2].to_i).to_s)
-                               return args[2]
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_write(a1.to_cstring, args[2].to_i))
                        else if pname == "io_read" then
-                               var str = recvval.as(IStream).read(args[2].to_i)
                                var a1 = args[1].val.as(Buffer)
-                               new FlatBuffer.from(str).copy(0, str.length, a1.as(FlatBuffer), 0)
-                               return v.int_instance(str.length)
+                               var ns = new NativeString(a1.length)
+                               var len = recvval.as(PrimitiveNativeFile).io_read(ns, args[2].to_i)
+                               a1.clear
+                               a1.append(ns.to_s_with_length(len))
+                               return v.int_instance(len)
+                       else if pname == "flush" then
+                               recvval.as(PrimitiveNativeFile).flush
+                               return null
                        else if pname == "io_close" then
-                               recvval.as(IOS).close
-                               return v.int_instance(0)
-                       else if pname == "address_is_null" then
-                               return v.false_instance
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).io_close)
+                       else if pname == "set_buffering_type" then
+                               return v.int_instance(recvval.as(PrimitiveNativeFile).set_buffering_type(args[1].to_i, args[2].to_i))
                        end
                else if pname == "calloc_array" then
                        var recvtype = args.first.mtype.as(MClassType)
@@ -1095,6 +1112,10 @@ redef class AMethPropdef
                else if pname == "errno" then
                        return v.int_instance(sys.errno)
                else if pname == "address_is_null" then
+                       var recv = args[0]
+                       if recv isa PrimitiveInstance[PrimitiveNativeFile] then
+                               return v.bool_instance(recv.val.address_is_null)
+                       end
                        return v.false_instance
                end
                return v.error_instance
diff --git a/src/interpreter/primitive_types.nit b/src/interpreter/primitive_types.nit
new file mode 100644 (file)
index 0000000..8fe483d
--- /dev/null
@@ -0,0 +1,61 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT. This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. You can modify it is you want, provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You are allowed to redistribute it and sell it, alone or is a part of
+# another product.
+
+# Encapsulates all primitive data types of nit
+#
+# Ensures that the use in the interpreter is independant of the
+# underlying implementation and that the services are semantically correct.
+module primitive_types
+
+intrude import standard::file
+
+# Wrapper for `NativeFile`
+class PrimitiveNativeFile
+
+       var file: FStream
+
+       init native_stdin do
+               file = new IFStream.from_fd(0)
+       end
+
+       init native_stdout do
+               file = new OFStream.from_fd(1)
+       end
+
+       init native_stderr do
+               file = new OFStream.from_fd(2)
+       end
+
+       init io_open_read(path: String) do
+               file = new IFStream.open(path.to_s)
+       end
+
+       init io_open_write(path: String) do
+               file = new OFStream.open(path.to_s)
+       end
+
+       fun address_is_null: Bool do return file._file.address_is_null
+
+       fun io_read(buf: NativeString, len: Int): Int do return file._file.io_read(buf, len)
+
+       fun io_write(buf: NativeString, len: Int): Int do return file._file.io_write(buf, len)
+
+       fun io_close: Int do return file._file.io_close
+
+       fun file_stat: FileStat do return file._file.file_stat
+
+       fun fileno: Int do return file._file.fileno
+
+       fun flush: Int do return file._file.flush
+
+       fun set_buffering_type(size, mode: Int): Int do
+               return file._file.set_buffering_type(size, mode)
+       end
+end
index 6c63925..1ee1db2 100644 (file)
@@ -102,7 +102,8 @@ private class MendelMetricsPhase
                end
 
                if csv then
-                       var csvh = new CSVDocument
+                       var csvh = new CsvDocument
+                       csvh.format = new CsvFormat('"', ';', "\n")
                        csvh.header = ["povr", "ovr", "pext", "ext", "pspe", "spe", "prep", "rep", "eq"]
                        for mclass in mclasses do
                                var povr = mclass.is_pure_overrider(vis).object_id
@@ -114,7 +115,7 @@ private class MendelMetricsPhase
                                var prep = mclass.is_pure_replacer(vis).object_id
                                var rep = mclass.is_replacer(vis).object_id
                                var eq = mclass.is_equal(vis).object_id
-                               csvh.add_line(povr, ovr, pext, ext, pspe, spe, prep, rep, eq)
+                               csvh.add_record(povr, ovr, pext, ext, pspe, spe, prep, rep, eq)
                        end
                        csvh.save("{out}/inheritance_behaviour.csv")
                end
index b55e1b8..c51e656 100644 (file)
@@ -367,8 +367,10 @@ class MetricSet
        end
 
        # Export the metric set in CSV format
-       fun to_csv: CSVDocument do
-               var csv = new CSVDocument
+       fun to_csv: CsvDocument do
+               var csv = new CsvDocument
+
+               csv.format = new CsvFormat('"', ';', "\n")
 
                # set csv headers
                csv.header.add("entry")
@@ -390,7 +392,7 @@ class MetricSet
                                        line.add("n/a")
                                end
                        end
-                       csv.lines.add(line)
+                       csv.records.add(line)
                end
                return csv
        end
index a92d065..c634815 100644 (file)
@@ -27,12 +27,6 @@ redef class Model
        # All known modules
        var mmodules = new Array[MModule]
 
-       # placebo for old module nesting hierarchy.
-       # where mainmodule < mainmodule::nestedmodule
-       #
-       # TODO REMOVE, rely on mgroup instead
-       var mmodule_nesting_hierarchy = new POSet[MModule]
-
        # Full module importation hierarchy including private or nested links.
        var mmodule_importation_hierarchy = new POSet[MModule]
 
@@ -91,12 +85,6 @@ class MModule
        # Alias for `name`
        redef fun to_s do return self.name
 
-       # placebo for old module nesting hierarchy
-       # The view of the module in the `model.mmodule_nesting_hierarchy`
-       #
-       # TODO REMOVE, rely on mgroup instead
-       var in_nesting: POSetElement[MModule] is noinit
-
        # The view of the module in the `model.mmodule_importation_hierarchy`
        var in_importation: POSetElement[MModule] is noinit
 
@@ -117,7 +105,6 @@ class MModule
        do
                model.mmodules_by_name.add_one(name, self)
                model.mmodules.add(self)
-               self.in_nesting = model.mmodule_nesting_hierarchy.add_node(self)
                if mgroup != null then
                        mgroup.mmodules.add(self)
                        if mgroup.name == name then
@@ -127,17 +114,9 @@ class MModule
                        # placebo for old module nesting hierarchy
                        var direct_owner = mgroup.default_mmodule
                        if direct_owner == self then
-                               # The module is the new owner of its own group, thus adopt the other modules
-                               for m in mgroup.mmodules do
-                                       if m == self then continue
-                                       model.mmodule_nesting_hierarchy.add_edge(self, m)
-                               end
                                # The potential owner is the default_mmodule of the parent group
                                if mgroup.parent != null then direct_owner = mgroup.parent.default_mmodule
                        end
-                       if direct_owner != self and direct_owner != null then
-                               model.mmodule_nesting_hierarchy.add_edge(direct_owner, self)
-                       end
                end
                self.in_importation = model.mmodule_importation_hierarchy.add_node(self)
        end
index 899f023..9c7a877 100644 (file)
@@ -251,9 +251,13 @@ redef class MModule
        do
                var cla = self.model.get_mclasses_by_name(name)
                if cla == null then
-                       if name == "Bool" then
+                       if name == "Bool" and self.model.get_mclasses_by_name("Object") != null then
+                               # Bool is injected because it is needed by engine to code the result
+                               # of the implicit casts.
                                var c = new MClass(self, name, null, enum_kind, public_visibility)
                                var cladef = new MClassDef(self, c.mclass_type, new Location(null, 0,0,0,0))
+                               cladef.set_supertypes([object_type])
+                               cladef.add_in_hierarchy
                                return c
                        end
                        print("Fatal Error: no primitive class {name}")
index 7af444d..7302619 100644 (file)
@@ -163,44 +163,6 @@ redef class MModule
                return mclasses
        end
 
-       fun in_nesting_intro_mclasses(min_visibility: MVisibility): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mmodule in in_nesting.greaters do
-                       for mclass in mmodule.filter_intro_mclasses(min_visibility) do
-                               if mclass.visibility < min_visibility then continue
-                               res.add mclass
-                       end
-               end
-               return res
-       end
-
-       fun in_nesting_redef_mclasses(min_visibility: MVisibility): Set[MClass] do
-               var res = new HashSet[MClass]
-               for mmodule in self.in_nesting.greaters do
-                       for mclass in mmodule.filter_redef_mclasses(min_visibility) do
-                               if mclass.visibility < min_visibility then continue
-                               res.add mclass
-                       end
-               end
-               return res
-       end
-
-       fun in_nesting_intro_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
-               var res = new HashSet[MClassDef]
-               for mmodule in in_nesting.greaters do
-                       res.add_all mmodule.intro_mclassdefs(min_visibility)
-               end
-               return res
-       end
-
-       fun in_nesting_redef_mclassdefs(min_visibility: MVisibility): Set[MClassDef] do
-               var res = new HashSet[MClassDef]
-               for mmodule in self.in_nesting.greaters do
-                       res.add_all mmodule.redef_mclassdefs(min_visibility)
-               end
-               return res
-       end
-
        redef fun concern_rank is cached do
                var max = 0
                for p in in_importation.direct_greaters do
@@ -209,6 +171,23 @@ redef class MModule
                end
                return max + 1
        end
+
+       # Find all mmodules nested in `self` if `self` is the default module of a `MGroup`.
+       fun nested_mmodules: Array[MModule] do
+               var res = new Array[MModule]
+               var mgroup = mgroup
+               if mgroup == null or self != mgroup.default_mmodule then return res
+               for mmodule in mgroup.mmodules do
+                       if mmodule == self then continue
+                       res.add mmodule
+               end
+               for nested in mgroup.in_nesting.direct_smallers do
+                       var default = nested.default_mmodule
+                       if default == null then continue
+                       res.add default
+               end
+               return res
+       end
 end
 
 redef class MClass
index 07b7470..c996a0c 100644 (file)
@@ -708,6 +708,9 @@ redef class AMethPropdef
                        msignature = mpropdef.mproperty.intro.msignature
                        if msignature == null then return # Skip error
 
+                       # The local signature is adapted to use the local formal types, if any.
+                       msignature = msignature.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+
                        # Check inherited signature arity
                        if param_names.length != msignature.arity then
                                var node: ANode
@@ -983,7 +986,11 @@ redef class AAttrPropdef
                        var msignature = mreadpropdef.mproperty.intro.msignature
                        if msignature == null then return # Error, thus skipped
                        inherited_type = msignature.return_mtype
-                       if mtype == null then mtype = inherited_type
+                       if inherited_type != null then
+                               # The inherited type is adapted to use the local formal types, if any.
+                               inherited_type = inherited_type.resolve_for(mclassdef.mclass.mclass_type, mclassdef.bound_mtype, mmodule, false)
+                               if mtype == null then mtype = inherited_type
+                       end
                end
 
                var nexpr = self.n_expr
index cc0ffa2..4f31aaf 100644 (file)
@@ -22,6 +22,7 @@ module nitni_base
 
 import parser
 import modelbuilder # builder only for externcalls
+private import compiler::abstract_compiler
 
 redef class MMethod
        # Short name of this method in C (without the class name)
@@ -53,7 +54,8 @@ end
 
 redef class MModule
        # Mangled name of this module in C
-       fun cname: String do return name
+       fun cname: String do return c_name # FIXME this is a hack to keep the internal FFI
+       # API independent of the compilers while still using the `MModule::c_name` service.
 end
 
 redef class MMethodDef
index 141ef1b..7ff0b16 100644 (file)
 
 # `nitpretty` is a tool able to pretty print Nit files.
 #
-# Usage:
-#
-#      nitpretty source.nit
-#
-# Main options:
-#
-# * `-o res.nit` output result into `res.nit`
-# * `--diff` show diff between `source` and `res`
-# * `--meld` open diff with `meld`
-# * `--check` check the format of multiple source files
-# * `--check --meld` perform `--check` and open `meld` for each difference
-#
-# ## Specification
-#
-# The specification of the pretty printing is described here.
-#
-# * Default indentation level is one `'\t'` character and
-# is increased by one for each indentation level.
-# * Default line max-size is 80.
-#
-# ### Comments
-#
-# There is many categories of comments:
-#
-# `Licence comments` are attached to the top of the file
-# no blank line before, one after.
-#
-# ~~~nitish
-# # This is a licence comment
-#
-# # Documentation for module `foo`
-# module foo
-# ~~~
-#
-# `ADoc` are documentation comments attached to a `AModule`, `AClassdef`, `APropdef`.
-#
-# They are printed before the definition with a blank line before and no after
-# at the same indentation level than the definition.
-#
-# ~~~nitish
-# # Documentation for module `foo`
-# module foo
-#
-# # Documentation for class `Bar`
-# class Bar
-#      # Documentation for method `baz`
-#      fun baz do end
-# end
-# ~~~
-#
-# `Block comments` are comments composed of one or more line rattached to nothing.
-# They are displayed with one blank line before and after at current indent level.
-#
-# ~~~nitish
-# <blank>
-# # block
-# # comment
-# <blank>
-# ~~~
-#
-# `Attached comments` are comments attached to a production.
-# They are printed as this.
-#
-# ~~~nitish
-# fun foo do # attached comment
-# end
-# ~~~
-#
-# `nitpretty` automatically remove multiple blanks between comments:
-#
-# ~~~nitish
-# # Licence
-# # ...
-# <blank>
-# # Block comment
-# ~~~
-#
-# ### Inlining
-#
-# Productions are automatically inlined when possible.
-#
-# Conditions:
-#
-# * the production must be syntactically inlinable
-# * the inlined production length is less than `PrettyPrinterVisitor::max-size`
-# * the production do not contains any comments
-#
-# ### Modules
-#
-# * There is a blank between the module declaration and its imports
-# * There is no blank between imports and only one after
-# * There is a blank between each extern block definition
-# * There is a blank between each class definition
-# * There is no blank line at the end of the module
-#
-# ~~~nitish
-# # Documentation for module `foo`
-# module foo
-#
-# import a
-# # import b
-# import c
-#
-# # Documentation for class `Bar`
-# class Bar end
-#
-# class Baz end # not a `ADoc` comment
-# ~~~
-#
-#
-# ### Classes
-#
-# * There is no blank between the class definition and its super-classes declarations
-# * There is no blank between two inlined property definition
-# * There is a blank between each block definition
-# * There no blank line at the end of the class definition
-#
-# ~~~nitish
-# # Documentation for class `Bar`
-# class Bar end
-#
-# class Baz
-#     super Bar
-#
-#      fun a is abstract
-#      private fun b do end
-#
-#      fun c do
-#           # ...
-#      end
-# end
-# ~~~
-#
-# Generic types have no space after or before brackets and are separated by a comma and a space:
-#
-# ~~~nitish
-# class A[E: Type1, F: Type1] end
-# ~~~
-#
-# ### Blocks
-#
-# * Inlined productions have no blank lines between them
-# * Block productions have a blank before and after
-#
-# ~~~nitish
-# var a = 10
-# var b = 0
-#
-# if a > b then
-#      # is positive
-#      print "positive"
-# end
-#
-# print "end"
-# ~~~
-#
-# ### Calls and Binary Ops
-#
-# Arguments are always printed separated with a comma and a space:
-#
-# ~~~nitish
-# foo(a, b, c)
-# ~~~
-#
-# Binary ops are always printed wrapped with spaces:
-#
-# ~~~nitish
-# var c = 1 + 2
-# ~~~
-#
-# Calls and binary ops can be splitted to fit the `max-size` constraint.
-# Breaking priority is given to arguments declaration after the comma.
-#
-# ~~~nitish
-# return foo("aaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbb",
-#     "cccccccccccccccccccccccccc")
-# ~~~
-#
-# Binary ops can also be broken to fit the `max-size` limit:
-#
-# ~~~nitish
-# return "aaaaaaaaaaaaaaaaaaaaaaaaaa" + "bbbbbbbbbbbbbbbbbbbbbbbbbbb" +
-#     "cccccccccccccccccccccccccc"
-# ~~~
-module nitpretty
-
-import template
-import toolcontext
-import modelbuilder
-import astutil
-
-# The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
-#
-# The main method here is `visit` that performs the pretty printing of a `ANode`.
-#
-# Because some tokens like `TComment` are not stored in the AST,
-# we visit the AST like traditionnal visitor and also maintain a
-# pointer to the `current_token` (actually the next one to be printed).
-#
-# Visited productions are in charges to move the token pointer using methods such as:
-#
-# * `consume`: print `current_token` and move to the next one
-# * `skip`: move to the next token without printing the current one
-# * `skip_to`: move to a specified token skipping all the tokens before
-# * `catch_up`: consume all the tokens between `current_token` and a specified token
-# * `finish_line`: consume all the tokens between `current_token` and the end of line
-class PrettyPrinterVisitor
-       # Pretty print `n`.
-       fun pretty(n: ANode): Template do
-               clear
-               n.parentize_tokens
-
-               if n isa Prod then
-                       current_token = n.first_token
-                       visit n
-               else if n isa Token then
-                       var p = n.parent
-
-                       while p != null and not p isa Prod do
-                               p = p.parent
-                       end
-
-                       current_token = p.first_token
-                       visit p
-               end
-
-               return tpl.as(not null)
-       end
-
-       # Pretty print the whole `nmodule` with comments before and after.
-       fun pretty_nmodule(nmodule: AModule): Template do
-               clear
-               nmodule.parentize_tokens
-               current_token = nmodule.location.file.first_token
-               visit nmodule
-               catch_up nmodule.location.file.last_token
-               tpl.add "\n"
-               return tpl.as(not null)
-       end
-
-       # Prepare `self` for a new visit.
-       private fun clear do
-               tpl = new Template
-               current_token = null
-               indent = 0
-               current_length = 0
-               previous_length = 0
-               wait_addn = 0
-       end
-
-       # Visit `n` if not null.
-       fun visit(n: nullable ANode) do
-               if n == null then return
-               n.accept_pretty_printer self
-       end
-
-       # Visit a list of `Anode`.
-       fun visit_list(n: nullable ANodes[ANode]) do
-               if n == null then return
-               n.accept_pretty_printer self
-       end
-
-       # Is the node inlinable and can fit on the line.
-       fun can_inline(n: nullable ANode): Bool do
-               if n == null then return true
-               if n.must_be_inline then return true
-               if n.must_be_block then return false
-               # check length
-               if n.collect_length + current_length > max_size then return false
-               # check block is inlinable
-               return n.is_inlinable
-       end
-
-       # Collect all `TComment` between `from` and `to`.
-       fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
-               var res = new Array[TComment]
-               if from isa Prod then from = from.first_token
-               if to isa Prod then to = to.first_token
-               if from == null or to == null then return res
-
-               while from != to do
-                       if from isa TComment then res.add from
-                       from = from.as(Token).next_token
-               end
-
-               return res
-       end
-
-       # Token under cursor.
-       #
-       # This is the next token to visit.
-       var current_token: nullable Token = null
-
-       # Skip the `current_token`.
-       fun skip do current_token = current_token.next_token
-
-       # Skip `current_token` until the end of line.
-       fun skip_line do current_token = current_token.last_real_token_in_line
-
-       # Skip `current_token` until `target` is reached.
-       fun skip_to(target: nullable Token) do
-               if target == null then return
-               while current_token != target do skip
-       end
-
-       # Visit `current_token`.
-       fun consume(token: String) do
-               assert current_token.text == token
-               visit current_token
-       end
-
-       # Is there token to visit between `current_token` and `target`?
-       fun need_catch_up(target: nullable Token): Bool do
-               if target == null then return false
-               return current_token != target
-       end
-
-       # Visit all tokens between `current_token` and `target`.
-       fun catch_up(target: nullable ANode) do
-               if target == null then return
-               if current_token == null then return
-               var token: Token
-               if target isa Token then
-                       token = target
-               else if target isa Prod then
-                       token = target.first_token.as(not null)
-               else
-                       abort
-               end
-               assert current_token.location <= token.location
-               while current_token != token do visit current_token
-       end
-
-       # Visit all tokens between `current_token` and the end of line.
-       fun finish_line do
-               if current_token isa TComment then
-                       adds
-                       visit current_token
-               end
-
-               while current_token isa TEol do skip
-       end
-
-       # The template under construction.
-       private var tpl: nullable Template = null
-
-       # Current indent level.
-       var indent = 0
-
-       # Size of a tabulation in spaces.
-       var tab_size = 8
-
-       # Max line size.
-       var max_size = 80
-
-       # Length of the current line.
-       var current_length = 0
-
-       # Length of the previous line.
-       var previous_length = 0
-
-       # Is the last line a blank line?
-       fun last_line_is_blank: Bool do return previous_length == 0
-
-       # Add `t` to current template.
-       fun add(t: String) do
-               if t.is_empty then return
-               while wait_addn > 0 do
-                       tpl.add "\n"
-                       wait_addn -= 1
-               end
-               tpl.add t
-               current_length += t.length
-       end
-
-       # Add a `'\n'`.
-       fun addn do
-               if current_length == 0 and last_line_is_blank then return
-               previous_length = current_length
-               current_length = 0
-               wait_addn += 1
-       end
-
-       # End of line chars are stored until another char is added.
-       # This avoid empty files with only a '`\n`'.
-       private var wait_addn = 0
-
-       # Add `'\t' * indent`.
-       fun addt do add "\t" * indent
-
-       # Add a space.
-       fun adds do add " "
-
-       fun visit_recv(n_expr: AExpr) do
-               if not n_expr isa AImplicitSelfExpr then
-                       visit n_expr
-                       consume "."
-               end
-       end
-end
-
-# Base framework redefs
-
-redef class ANodes[E]
-       private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
-               for e in self do
-                       var e_can_inline = v.can_inline(e)
-
-                       if e != first then
-                               if not e_can_inline then
-                                       v.add ","
-                                       v.addn
-                                       v.addt
-                                       v.addt
-                               else
-                                       v.add ", "
-                               end
-                       end
-
-                       v.visit e
-               end
-       end
-end
-
-redef class ANode
-       # Start visit of `self` using a `PrettyPrinterVisitor`
-       private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract
-
-       # Collect the length (in `Char`) of the node.
-       private fun collect_length: Int is abstract
-
-       # Is `self` printable in one line?
-       private fun is_inlinable: Bool do return true
-
-       # Force `self` to be rendered as a block.
-       private var force_block = false
-
-       # Does `self` have to be rendered as a block?
-       private fun must_be_block: Bool do return force_block
-
-       # Force `self` to be rendered as a line.
-       private var force_inline = false
-
-       # Does `self` have be rendered as a line?
-       private fun must_be_inline: Bool do
-               if parent != null and parent.must_be_inline then return true
-               return force_inline
-       end
-
-       # Does `self` was written in one line before transformation?
-       private fun was_inline: Bool is abstract
-end
-
-redef class Token
-       redef fun accept_pretty_printer(v) do
-               v.add text.trim
-               v.current_token = next_token
-       end
-
-       redef fun collect_length do return text.length
-       redef fun is_inlinable do return true
-       redef fun was_inline do return true
-end
-
-redef class Prod
-       redef fun accept_pretty_printer(v) do v.visit first_token
-
-       # The token where the production really start (skipping ADoc).
-       private fun start_token: nullable Token do return first_token
-
-       # Collect all `TComment` contained in the production
-       # (between `start_token` and `end_token`).
-       private fun collect_comments: Array[TComment] do
-               var res = new Array[TComment]
-               if start_token == null or last_token == null then return res
-               var token = start_token
-
-               while token != last_token do
-                       if token isa TComment then res.add token
-                       token = token.next_token
-               end
-
-               return res
-       end
-
-       redef fun collect_length do
-               var res = 0
-               if start_token == null or last_token == null then return res
-               var token = start_token
-
-               while token != last_token do
-                       res += token.text.length
-                       token = token.next_token
-               end
-
-               res += token.text.length
-               return res
-       end
-
-       redef fun was_inline do
-               return first_token.location.line_start == last_token.location.line_end
-       end
-end
-
-# Comments
-
-redef class TComment
-       redef fun accept_pretty_printer(v) do
-               if is_adoc then
-                       v.addt
-                       super
-                       v.addn
-                       return
-               end
-
-               if is_licence then
-                       super
-                       v.addn
-                       if is_last_in_group then v.addn
-                       return
-               end
-
-               if is_orphan then
-                       v.addn
-                       v.addt
-                       super
-                       v.addn
-                       v.addn
-                       return
-               end
-
-               if is_inline then
-                       if next_token isa TComment and is_first_in_group then v.addn
-                       v.addt
-                       super
-                       v.addn
-                       var prev_token = self.prev_token
-                       if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
-                       return
-               end
-
-               super
-       end
-
-       # Is `self` part of an `ADoc`?
-       private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null
-
-       # Is `self` part of a licence?
-       private fun is_licence: Bool do
-               var prev_token = self.prev_token
-
-               if prev_token == null then
-                       return true
-               else if prev_token isa TComment then
-                       return prev_token.is_licence
-               else
-                       return false
-               end
-       end
-
-       # Is `self` starts and ends its line?
-       private fun is_inline: Bool do
-               return self == first_real_token_in_line and self == last_real_token_in_line
-       end
-
-       # Is `self` an orphan line (blank before and after)?
-       private fun is_orphan: Bool do
-               return prev_token isa TEol and
-                  (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
-                  next_token isa TEol
-       end
-
-       # Is `self` the first comment of a group of comment?
-       private fun is_first_in_group: Bool do return not prev_token isa TComment
-
-       # Is `self` the last comment of a group of comments?
-       private fun is_last_in_group: Bool do return not next_token isa TComment
-end
-
-redef class ADoc
-       redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
-       redef fun is_inlinable do return n_comment.length <= 1
-end
-
-# Annotations
-
-redef class AAnnotations
-       redef fun accept_pretty_printer(v) do
-               v.adds
-               v.consume "is"
-
-               if v.can_inline(self) then
-                       v.adds
-                       for n_item in n_items do
-                               v.visit n_item
-                               if n_item != n_items.last then
-                                       v.add ", "
-                               end
-                       end
-                       v.finish_line
-               else if n_items.length > 1 then
-                       v.addn
-                       v.indent += 1
-
-                       for n_item in n_items do
-                               v.addt
-                               v.visit n_item
-                               v.finish_line
-                               v.addn
-                       end
-
-                       v.indent -= 1
-               end
-               if not was_inline and v.current_token isa TKwend then v.skip
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               for annot in n_items do if not annot.is_inlinable then return false
-               return true
-       end
-end
-
-redef class AAnnotation
-       redef fun accept_pretty_printer(v) do
-               v.visit n_atid
-               if not n_args.is_empty then
-                       if n_opar == null then
-                               v.adds
-                       else
-                               v.visit n_opar
-                       end
-                       v.visit_list n_args
-                       v.visit n_cpar
-               end
-       end
-end
-
-redef class ATypeExpr
-       redef fun accept_pretty_printer(v) do v.visit n_type
-end
-
-# Modules
-
-redef class AModule
-       redef fun accept_pretty_printer(v) do
-               v.catch_up start_token
-               v.visit n_moduledecl
-
-               if not n_imports.is_empty then
-                       v.addn
-
-                       for n_import in n_imports do
-                               v.catch_up n_import
-                               v.visit n_import
-                       end
-               end
-
-               if not n_extern_code_blocks.is_empty then
-                       v.addn
-
-                       for n_extern_block in n_extern_code_blocks do
-                               v.catch_up n_extern_block
-                               v.visit n_extern_block
-                               v.addn
-                               if n_extern_block != n_extern_code_blocks.last then v.addn
-                       end
-
-                       if not n_classdefs.is_empty then v.addn
-               end
-
-               if not n_classdefs.is_empty then
-                       v.addn
-
-                       for n_classdef in n_classdefs do
-                               v.catch_up n_classdef
-                               v.visit n_classdef
-                               if n_classdef != n_classdefs.last then v.addn
-                       end
-               end
-
-               assert v.indent == 0
-       end
-
-       # Skip doc if any.
-       redef fun start_token do
-               if n_moduledecl != null then return n_moduledecl.first_token
-               if not n_imports.is_empty then return n_imports.first.first_token
-               if not n_classdefs.is_empty then return n_classdefs.first.first_token
-               return first_token
-       end
-
-       redef fun is_inlinable do return false
-end
-
-redef class AModuledecl
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               v.visit n_kwmodule
-               v.adds
-               v.visit n_name
-
-               if n_annotations != null then
-                       var annot_inline = v.can_inline(n_annotations)
-                       v.visit n_annotations
-
-                       if not annot_inline then
-                               if v.current_token isa TKwend then
-                                       v.consume "end"
-                                       v.finish_line
-                               else
-                                       v.add "end"
-                               end
-                       end
-               end
-
-               v.finish_line
-               v.addn
-       end
-end
-
-redef class AModuleName
-       redef fun accept_pretty_printer(v) do
-               for path in n_path do
-                       v.visit path
-                       v.add "::"
-               end
-
-               v.visit n_id
-       end
-end
-
-redef class ANoImport
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwimport
-               v.adds
-               v.visit n_kwend
-               v.finish_line
-               v.addn
-       end
-end
-
-redef class AStdImport
-       redef fun accept_pretty_printer(v) do
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               v.visit n_kwimport
-               v.adds
-               v.visit n_name
-               v.finish_line
-               v.addn
-       end
-end
-
-# Classes
-
-redef class AClassdef
-       redef fun accept_pretty_printer(v) do
-               for n_propdef in n_propdefs do
-                       v.catch_up n_propdef
-
-                       if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
-                               if n_propdef != n_propdefs.first then v.addn
-                               v.visit n_propdef
-                               if n_propdef != n_propdefs.last then v.addn
-                       else
-                               v.visit n_propdef
-                       end
-               end
-       end
-end
-
-redef class AStdClassdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               var can_inline = v.can_inline(self)
-
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               if n_kwredef != null then
-                       v.visit n_kwredef
-                       v.adds
-               end
-
-               v.visit n_classkind
-               v.adds
-               v.visit n_id
-
-               if not n_formaldefs.is_empty then
-                       v.consume "["
-                       v.visit_list n_formaldefs
-                       v.consume "]"
-               end
-
-               if n_extern_code_block != null then
-                       v.adds
-                       v.visit n_extern_code_block
-               end
-
-               if can_inline then
-                       v.adds
-
-                       if not n_superclasses.is_empty then
-                               for n_superclass in n_superclasses do
-                                       v.visit n_superclass
-                                       v.adds
-                               end
-                       end
-               else
-                       v.finish_line
-                       v.addn
-                       v.indent += 1
-
-                       for n_superclass in n_superclasses do
-                               v.catch_up n_superclass
-                               v.addt
-                               v.visit n_superclass
-                               v.finish_line
-                               v.addn
-                       end
-
-                       if not n_superclasses.is_empty and not n_propdefs.is_empty then
-                               v.addn
-                       end
-
-                       super
-                       v.catch_up n_kwend
-                       v.indent -= 1
-               end
-
-               v.visit n_kwend
-               v.finish_line
-               v.addn
-               assert v.indent == 0
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if not n_propdefs.is_empty then return false
-               if n_superclasses.length > 1 then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-
-       redef fun start_token do
-               if not n_visibility isa APublicVisibility then return n_visibility.first_token
-               if n_kwredef != null then return n_kwredef
-               return n_classkind.first_token
-       end
-end
-
-redef class AAbstractClasskind
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwabstract
-               v.adds
-               v.visit n_kwclass
-       end
-end
-
-redef class AExternClasskind
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwextern
-               v.adds
-               v.visit n_kwclass
-       end
-end
-
-redef class AFormaldef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-       end
-end
-
-redef class AType
-       redef fun accept_pretty_printer(v) do
-               if n_kwnullable != null then
-                       v.visit n_kwnullable
-                       v.adds
-               end
-
-               v.visit n_id
-
-               if not n_types.is_empty then
-                       v.consume "["
-                       v.visit_list n_types
-                       v.consume "]"
-               end
-       end
-end
-
-redef class ASuperclass
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwsuper
-               v.adds
-               v.visit n_type
-       end
-end
-
-# Properties
-
-redef class APropdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_doc
-               v.addt
-
-               if not n_visibility isa APublicVisibility then
-                       v.visit n_visibility
-                       v.adds
-               end
-
-               if n_kwredef != null then
-                       v.visit n_kwredef
-                       v.adds
-               end
-       end
-
-       redef fun start_token do
-               if n_doc == null then return super
-               return n_doc.last_token.next_token
-       end
-end
-
-redef class AAttrPropdef
-       redef fun accept_pretty_printer(v) do
-               super
-               v.visit n_kwvar
-               v.adds
-               v.visit n_id2
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_expr != null then
-                       v.adds
-                       v.consume "="
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_annotations != null then v.visit n_annotations
-               v.finish_line
-               v.addn
-       end
-
-       redef fun first_token do
-               if n_doc != null then return n_doc.first_token
-               if not n_visibility isa APublicVisibility then return n_visibility.first_token
-               if n_kwredef != null then return n_kwredef
-               return n_kwvar
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class ATypePropdef
-       redef fun accept_pretty_printer(v) do
-               super
-               v.visit n_kwtype
-               v.adds
-               v.visit n_id
-               v.consume ":"
-               v.adds
-               v.visit n_type
-               v.finish_line
-               v.addn
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AMethPropdef
-       redef fun accept_pretty_printer(v) do
-               #  TODO: Handle extern annotations
-
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-               super
-               if n_kwinit != null then v.visit n_kwinit
-               if n_kwmeth != null then v.visit n_kwmeth
-               if n_kwnew != null then v.visit n_kwnew
-
-               if not n_methid == null then
-                       v.adds
-                       v.visit n_methid
-               end
-
-               v.visit n_signature
-
-               if n_annotations != null then
-                       v.visit n_annotations
-               else
-                       v.adds
-               end
-
-               if n_extern_calls != null or n_extern_code_block != null then
-                       if n_annotations != null then v.adds
-                       if n_extern_calls != null then v.visit n_extern_calls
-                       if n_extern_code_block != null then v.visit n_extern_code_block
-               end
-
-               var n_block = self.n_block
-
-               if n_block != null then
-                       while not v.current_token isa TKwdo do v.skip
-                       if n_annotations != null then
-                               if v.can_inline(n_annotations) then
-                                       v.adds
-                               else
-                                       v.addt
-                               end
-                       end
-                       v.consume "do"
-
-                       if can_inline then
-                               v.adds
-
-                               if n_block isa ABlockExpr then
-                                       if n_block.n_expr.is_empty then
-                                               v.visit n_block.n_kwend
-                                       else
-                                               v.visit n_block.n_expr.first
-                                               v.current_token = n_block.n_kwend
-                                               v.skip
-                                       end
-                               else
-                                       v.visit n_block
-                                       if v.current_token isa TKwend then v.skip
-                               end
-                       else
-                               v.finish_line
-                               v.addn
-                               v.indent += 1
-
-                               if n_block isa ABlockExpr then
-                                       n_block.force_block = true
-                                       v.visit n_block
-                                       v.catch_up n_block.n_kwend
-                               else
-                                       v.addt
-                                       v.visit n_block
-                                       v.addn
-                               end
-
-                               v.indent -= 1
-                               v.addt
-                               if n_block isa ABlockExpr then
-                                       v.visit n_block.n_kwend
-                               else
-                                       v.add "end"
-                               end
-                       end
-               end
-
-               v.finish_line
-               v.addn
-               assert v.indent == before
-       end
-
-       # Can be inlined if:
-       # * block is empty or can be inlined
-       # * contains no comments
-       redef fun is_inlinable do
-               if not super then return false
-               if n_annotations != null and not n_annotations.is_inlinable then return false
-               if n_block != null and not n_block.is_inlinable then return false
-               if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
-               if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-end
-
-redef class AMainMethPropdef
-       redef fun accept_pretty_printer(v) do
-               v.visit n_block
-               v.addn
-       end
-end
-
-redef class ASignature
-       redef fun accept_pretty_printer(v) do
-               if not n_params.is_empty then
-                       v.consume "("
-                       v.visit_list n_params
-                       v.consume ")"
-               end
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-       end
-end
-
-redef class AParam
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_dotdotdot != null then v.visit n_dotdotdot
-       end
-end
-
-# Extern
-
-redef class AExternCalls
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwimport
-
-               if can_inline then
-                       v.adds
-                       v.visit_list n_extern_calls
-               else
-                       v.addn
-                       v.addt
-                       v.addt
-                       v.visit_list n_extern_calls
-               end
-
-               v.adds
-       end
-end
-
-redef class AFullPropExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.visit n_dot
-               v.visit n_methid
-       end
-end
-
-redef class ALocalPropExternCall
-       redef fun accept_pretty_printer(v) do v.visit n_methid
-end
-
-redef class AInitPropExternCall
-       redef fun accept_pretty_printer(v) do v.visit n_type
-end
-
-redef class ACastAsExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_from_type
-               v.visit n_dot
-               v.visit n_kwas
-               v.consume "("
-               v.visit n_to_type
-               v.consume ")"
-       end
-end
-
-redef class AAsNullableExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.consume "."
-               v.visit n_kwas
-               v.adds
-               v.visit n_kwnullable
-       end
-end
-
-redef class AAsNotNullableExternCall
-       redef fun accept_pretty_printer(v) do
-               v.visit n_type
-               v.consume "."
-               v.visit n_kwas
-               v.adds
-               v.visit n_kwnot
-               v.adds
-               v.visit n_kwnullable
-       end
-end
-
-redef class AExternCodeBlock
-       redef fun accept_pretty_printer(v) do
-               if n_in_language != null then
-                       v.visit n_in_language
-                       v.adds
-               end
-
-               v.visit n_extern_code_segment
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               return n_extern_code_segment.is_inlinable
-       end
-end
-
-redef class AInLanguage
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwin
-               v.adds
-               v.visit n_string
-       end
-end
-
-redef class TExternCodeSegment
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if can_inline then
-                       super
-               else
-                       var text = text.substring(2, text.length - 4)
-                       var lines = text.r_trim.split("\n")
-
-                       if text.is_empty then
-                               v.add "`\{`\}"
-                       else
-                               v.add "`\{"
-
-                               if not lines.first.trim.is_empty then
-                                       v.addn
-                                       lines.first.l_trim
-                                       v.indent += 1
-                                       v.addt
-                                       v.indent -= 1
-                               end
-
-                               for line in lines do
-                                       v.add line.r_trim
-                                       v.addn
-                               end
-
-                               v.addt
-                               v.add "`\}"
-                       end
-
-                       v.current_token = next_token
-               end
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               return location.line_start == location.line_end
-       end
-end
-
-# Blocks
-
-redef class ABlockExpr
-       redef fun accept_pretty_printer(v) do
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-
-               if can_inline and not n_expr.is_empty then
-                       v.visit n_expr.first
-                       v.finish_line
-               else
-                       for nexpr in n_expr do
-                               var expr_inline = v.can_inline(nexpr)
-                               if not expr_inline and nexpr != n_expr.first then v.addn
-                               v.catch_up nexpr
-                               v.addt
-                               v.visit nexpr
-                               v.finish_line
-                               v.addn
-                               if not expr_inline and nexpr != n_expr.last then v.addn
-                       end
-               end
-
-               assert v.indent == before
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if not collect_comments.is_empty then return false
-
-               if not n_expr.is_empty then
-                       if n_expr.length > 1 then return false
-                       if not n_expr.first.is_inlinable then return false
-               end
-
-               return true
-       end
-end
-
-redef class AIfExpr
-       redef fun accept_pretty_printer(v) do
-               var before = v.indent
-               var can_inline = v.can_inline(self)
-               v.visit n_kwif
-               v.adds
-
-               if v.can_inline(n_expr) then
-                       v.visit n_expr
-                       v.adds
-               else
-                       v.visit n_expr
-                       v.addn
-                       v.addt
-               end
-
-               # skip comments before `then` token
-               while not v.current_token isa TKwthen do v.skip
-               v.consume "then"
-               var n_else = self.n_else
-
-               if can_inline then
-                       v.adds
-                       if n_then != null then v.visit n_then
-
-                       if has_else(v) then
-                               n_else.force_inline = true
-                               v.adds
-                               v.consume "else"
-                               v.adds
-                               v.visit n_else
-                       else if n_then == null then
-                               v.add "end"
-                       end
-
-                       v.skip_to last_token.last_real_token_in_line
-               else
-                       v.finish_line
-                       v.addn
-                       v.indent += 1
-
-                       if n_then != null then
-                               if n_then isa ABlockExpr then
-                                       n_then.force_block = true
-                                       v.visit n_then
-                               else
-                                       v.addt
-                                       v.visit n_then
-                                       v.addn
-                               end
-                       end
-
-                       if has_else(v) then
-                               while not v.current_token isa TKwelse do
-                                       v.consume v.current_token.text
-                               end
-
-                               v.indent -= 1
-                               v.addt
-                               v.consume "else"
-
-                               if n_else isa AIfExpr then
-                                       n_else.force_block = true
-                                       v.adds
-                                       v.visit n_else
-                               else
-                                       v.finish_line
-                                       v.addn
-                                       v.indent += 1
-
-                                       if n_else isa ABlockExpr then
-                                               n_else.force_block = true
-                                               v.visit n_else
-                                       else
-                                               v.addt
-                                               v.visit n_else
-                                               v.addn
-                                       end
-
-                                       if last_token isa TKwend then
-                                               v.catch_up last_token
-                                               v.indent -= 1
-                                               v.addt
-                                               v.consume "end"
-                                       else
-                                               v.indent -= 1
-                                               v.addt
-                                               v.add "end"
-                                       end
-                               end
-                       else
-                               if last_token.location >= v.current_token.location then v.catch_up last_token
-                               v.indent -= 1
-                               v.addt
-                               v.add "end"
-                               if v.current_token isa TKwend then v.skip
-                       end
-               end
-
-               assert v.indent == before
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if n_then != null and not n_then.is_inlinable then return false
-               var n_else = self.n_else
-               if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
-                  (not n_else isa ABlockExpr and n_else != null) then
-                       return false
-               end
-               if not collect_comments.is_empty then return false
-               return true
-       end
-
-       # Does this `if` statement contains a `else` part?
-       private fun has_else(v: PrettyPrinterVisitor): Bool do
-               var n_else = n_else
-               if n_else == null then return false
-               var n_kwelse = collect_kwelse
-               if n_kwelse == null then return false
-
-               if n_else isa ABlockExpr then
-                       var comments: Array[TComment]
-
-                       if n_then == null then
-                               comments = v.collect_comments(n_expr.last_token, n_else.last_token)
-                       else
-                               comments = v.collect_comments(n_then.last_token, n_else.last_token)
-                       end
-
-                       if not comments.is_empty then return true
-                       return not n_else.n_expr.is_empty
-               end
-
-               return true
-       end
-
-       # Lookup for `else` token in `self`.
-       private fun collect_kwelse: nullable TKwelse do
-               var token = first_token
-
-               while token != last_token do
-                       if token isa TKwelse then return token
-                       token = token.next_token
-               end
-
-               return null
-       end
-end
-
-# Used to factorize work on loops.
-private class ALoopHelper
-       super AExpr
-
-       fun loop_block: nullable ANode is abstract
-       fun loop_label: nullable ANode is abstract
-
-       fun visit_loop_block(v: PrettyPrinterVisitor) do
-               var n_block = loop_block
-               v.finish_line
-               v.addn
-               v.indent += 1
-
-               if n_block isa ABlockExpr then
-                       n_block.force_block = true
-                       v.visit n_block
-                       v.catch_up n_block.n_kwend
-                       v.indent -= 1
-                       v.addt
-                       v.visit n_block.n_kwend
-               else
-                       v.addt
-                       v.visit n_block
-                       v.addn
-                       v.indent -= 1
-                       v.addt
-                       v.add "end"
-               end
-
-               if loop_label != null then
-                       v.adds
-                       v.visit loop_label
-               end
-       end
-
-       fun visit_loop_inline(v: PrettyPrinterVisitor) do
-               var n_block = loop_block
-               v.adds
-
-               if n_block isa ABlockExpr then
-                       if n_block.n_expr.is_empty then
-                               v.visit n_block.n_kwend
-                       else
-                               v.visit n_block.n_expr.first
-                               v.current_token = n_block.n_kwend
-                               v.skip
-                       end
-               else
-                       v.visit n_block
-                       if v.current_token isa TKwend then v.skip
-               end
-
-               if loop_label != null then
-                       v.adds
-                       v.visit loop_label
-               end
-       end
-
-       redef fun is_inlinable do
-               var n_block = loop_block
-               if not super then return false
-               if n_block isa ABlockExpr and not n_block.is_inlinable then return false
-               if not collect_comments.is_empty then return false
-               return true
-       end
-end
-
-redef class ALoopExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwloop
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class AWhileExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwwhile
-               v.adds
-               v.visit n_expr
-               v.adds
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class ADoExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class AForExpr
-       super ALoopHelper
-
-       redef fun loop_block do return n_block
-       redef fun loop_label do return n_label
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwfor
-               v.adds
-
-               for n_id in n_ids do
-                       v.visit n_id
-                       if n_id != n_ids.last then v.add ", "
-               end
-
-               v.adds
-               v.consume "in"
-               v.adds
-               v.visit n_expr
-               v.adds
-               v.visit n_kwdo
-               if can_inline then visit_loop_inline v else visit_loop_block v
-       end
-end
-
-redef class ABreakExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwbreak
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_label != null then
-                       v.adds
-                       v.visit n_label
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AContinueExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwcontinue
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-
-               if n_label != null then
-                       v.adds
-                       v.visit n_label
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Calls
-
-redef class ASendExpr
-       redef fun is_inlinable do return true
-end
-
-redef class ACallExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit_recv n_expr
-
-               if not n_expr isa AImplicitSelfExpr and not can_inline then
-                       v.addn
-                       v.addt
-                       v.addt
-               end
-
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       if is_stmt and n_args.n_exprs.length == 1 then
-                               v.adds
-                               if v.current_token isa TOpar then v.skip
-                               v.visit n_args.n_exprs.first
-                               if v.current_token isa TCpar then v.skip
-                       else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
-                       end
-               end
-       end
-
-       # Is the call alone on its line?
-       fun is_stmt: Bool do return parent isa ABlockExpr
-end
-
-redef class ACallAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-
-               v.adds
-               v.consume "="
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ACallReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ABraExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-       end
-end
-
-redef class ABraAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class ABraReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "["
-                       v.visit_list n_args.n_exprs
-                       v.consume "]"
-               end
-
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAssignMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-               v.visit n_assign
-       end
-end
-
-redef class ABraMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_cbra
-       end
-end
-
-redef class ABraassignMethid
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_cbra
-               v.visit n_assign
-       end
-end
-
-redef class AInitExpr
-       redef fun accept_pretty_printer(v) do
-               if not n_expr isa AImplicitSelfExpr then
-                       v.visit n_expr
-                       v.consume "."
-               end
-
-               v.visit n_kwinit
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-       end
-end
-
-redef class ANewExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwnew
-               v.adds
-               v.visit n_type
-
-               if n_id != null then
-                       v.consume "."
-
-                       if not can_inline then
-                               v.addn
-                               v.addt
-                               v.addt
-                       end
-
-                       v.visit n_id
-               end
-
-               if not n_args.n_exprs.is_empty then
-                       v.consume "("
-                       v.visit_list n_args.n_exprs
-                       v.consume ")"
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Attributes
-
-redef class AAttrExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AAttrAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAttrReassignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit_recv n_expr
-               v.visit n_id
-               v.adds
-               v.visit n_assign_op
-               v.adds
-               v.visit n_value
-       end
-end
-
-# Exprs
-
-redef class AVardeclExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwvar
-               v.adds
-               v.visit n_id
-
-               if n_type != null then
-                       v.consume ":"
-                       v.adds
-                       v.visit n_type
-               end
-
-               if n_expr != null then
-                       v.adds
-                       v.consume "="
-                       v.adds
-                       v.visit n_expr
-               end
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AVarAssignExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_id
-               v.adds
-               v.visit n_assign
-               v.adds
-               v.visit n_value
-       end
-end
-
-redef class AAssertExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-               v.visit n_kwassert
-
-               if n_id != null then
-                       v.adds
-                       v.visit n_id
-                       v.consume ":"
-               end
-
-               v.adds
-               v.visit n_expr
-               var n_else = self.n_else
-
-               if n_else != null then
-                       v.adds
-                       v.consume "else"
-
-                       if can_inline then
-                               v.adds
-                               v.visit n_else
-                       else
-                               v.addn
-
-                               if n_else isa ABlockExpr then
-                                       v.indent += 1
-                                       n_else.force_block = true
-                                       v.visit n_else
-                                       v.indent -= 1
-                                       v.addt
-                                       v.visit n_else.n_kwend
-                               else
-                                       v.indent += 1
-                                       v.addt
-                                       v.visit n_else
-                                       v.addn
-                                       v.indent -= 1
-                                       v.addt
-                                       v.add "end"
-                               end
-                       end
-               end
-       end
-
-       redef fun is_inlinable do
-               if not super then return false
-               if n_else != null and not n_else.is_inlinable then return false
-               return true
-       end
-end
-
-redef class AReturnExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwreturn
-
-               if n_expr != null then
-                       v.adds
-                       v.visit n_expr
-               end
-       end
-end
-
-redef class ASuperExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwsuper
-
-               if not n_args.n_exprs.is_empty then
-                       if is_stmt and n_args.n_exprs.length == 1 then
-                               v.adds
-                               if v.current_token isa TOpar then v.skip
-                               v.visit n_args.n_exprs.first
-                               if v.current_token isa TCpar then v.skip
-                       else
-                               if v.current_token isa TOpar then
-                                       v.consume "("
-                               else
-                                       v.adds
-                               end
-
-                               v.visit_list n_args.n_exprs
-                               if v.current_token isa TCpar then v.consume ")"
-                       end
-               end
-       end
-
-       # Is the call alone on its line?
-       fun is_stmt: Bool do return self.first_token.is_starting_line
-
-       redef fun is_inlinable do return true
-end
-
-redef class AOnceExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwonce
-               v.adds
-               v.visit n_expr
-       end
-
-       redef fun is_inlinable do return true
-end
-
-redef class AAbortExpr
-       redef fun accept_pretty_printer(v) do v.visit n_kwabort
-       redef fun is_inlinable do return true
-end
-
-redef class ANotExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_kwnot
-               v.adds
-               v.visit n_expr
-       end
-end
-
-redef class AAsCastExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.consume "."
-               v.visit n_kwas
-               v.visit n_opar
-               v.visit n_type
-               v.visit n_cpar
-       end
-end
-
-redef class AAsNotnullExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.consume "."
-               v.visit n_kwas
-               v.visit n_opar
-               v.visit n_kwnot
-               v.adds
-               v.visit n_kwnull
-               v.visit n_cpar
-       end
-end
-
-# Binops
-
-# Used to factorize work on Or, And, Implies and Binop expressions.
-private class ABinOpHelper
-       super AExpr
-
-       fun bin_expr1: AExpr is abstract
-       fun bin_expr2: AExpr is abstract
-
-       # Operator string
-       fun bin_op: String is abstract
-
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if not can_inline then
-                       if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
-                          (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
-                          (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
-                       then
-                               bin_expr1.force_block = true
-                       end
-               end
-
-               v.visit bin_expr1
-               v.adds
-               v.consume bin_op
-
-               if can_inline then
-                       v.adds
-                       v.visit bin_expr2
-               else
-                       v.addn
-                       v.addt
-                       v.addt
-                       v.visit bin_expr2
-               end
-       end
-end
-
-redef class AAndExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "and"
-end
-
-redef class AOrExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "or"
-end
-
-redef class AImpliesExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-       redef fun bin_op do return "implies"
-end
-
-redef class ABinopExpr
-       super ABinOpHelper
-
-       redef fun bin_expr1 do return n_expr
-       redef fun bin_expr2 do return n_expr2
-end
-
-redef class AEqExpr
-       redef fun bin_op do return "=="
-end
-
-redef class AGeExpr
-       redef fun bin_op do return ">="
-end
-
-redef class AGgExpr
-       redef fun bin_op do return ">>"
-end
-
-redef class AGtExpr
-       redef fun bin_op do return ">"
-end
-
-redef class ALeExpr
-       redef fun bin_op do return "<="
-end
-
-redef class ALlExpr
-       redef fun bin_op do return "<<"
-end
-
-redef class ALtExpr
-       redef fun bin_op do return "<"
-end
-
-redef class AMinusExpr
-       redef fun bin_op do return "-"
-end
-
-redef class ANeExpr
-       redef fun bin_op do return "!="
-end
-
-redef class APercentExpr
-       redef fun bin_op do return "%"
-end
-
-redef class APlusExpr
-       redef fun bin_op do return "+"
-end
-
-redef class ASlashExpr
-       redef fun bin_op do return "/"
-end
-
-redef class AStarExpr
-       redef fun bin_op do return "*"
-end
-
-redef class AStarstarExpr
-       redef fun bin_op do return "**"
-end
-
-redef class AStarshipExpr
-       redef fun bin_op do return "<=>"
-end
-
-redef class AIsaExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.adds
-               v.consume "isa"
-               v.adds
-               v.visit n_type
-       end
-end
-
-redef class AOrElseExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_expr
-               v.adds
-               v.consume "or"
-               v.adds
-               v.consume "else"
-               v.adds
-               v.visit n_expr2
-       end
-
-       redef fun is_inlinable do return true
-end
-
-# Syntax
-
-redef class AUminusExpr
-       redef fun accept_pretty_printer(v) do
-               v.consume "-"
-               v.visit n_expr
-       end
-end
-
-redef class ANullExpr
-       redef fun accept_pretty_printer(v) do v.visit n_kwnull
-       redef fun is_inlinable do return true
-end
-
-redef class AParExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_opar
-               v.visit n_expr
-               v.visit n_cpar
-       end
-end
-
-redef class AArrayExpr
-       redef fun accept_pretty_printer(v) do
-               v.consume "["
-               v.visit_list n_exprs.n_exprs
-               v.consume "]"
-       end
-end
-
-redef class ACrangeExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_expr
-               v.consume ".."
-               v.visit n_expr2
-               v.visit n_cbra
-       end
-end
-
-redef class AOrangeExpr
-       redef fun accept_pretty_printer(v) do
-               v.visit n_obra
-               v.visit n_expr
-               v.consume ".."
-               v.visit n_expr2
-               v.visit n_cbra
-       end
-end
-
-# Strings
-
-redef class AStringFormExpr
-       redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if can_inline then
-                       v.visit n_string
-               else
-                       var text = n_string.text
-                       var i = 0
-
-                       while i < text.length do
-                               v.add text[i].to_s
-
-                               if v.current_length >= v.max_size and i <= text.length - 3 then
-                                       v.add "\" +"
-                                       v.addn
-                                       v.indent += 1
-                                       v.addt
-                                       v.indent -= 1
-                                       v.add "\""
-                               end
-
-                               i += 1
-                       end
-
-                       v.current_token = n_string.next_token
-               end
-       end
-end
-
-redef class ASuperstringExpr
-       redef fun accept_pretty_printer(v) do
-               for n_expr in n_exprs do v.visit n_expr
-       end
-
-       redef fun must_be_inline do
-               if super then return true
-
-               if not n_exprs.is_empty then
-                       var first = n_exprs.first
-                       return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
-               end
-
-               return false
-       end
-end
+# See `man nitpretty` for more infos.
+import pretty
 
 redef class ToolContext
+       # The working directory used to store temp files.
        var opt_dir = new OptionString("Working directory (default is '.nitpretty')", "--dir")
 
+       # Output pretty printed code with this filename.
        var opt_output = new OptionString("Output name (default is pretty.nit)", "-o",
           "--output")
 
+       # Show diff between source and pretty printed code.
        var opt_diff = new OptionBool("Show diff between source and output", "--diff")
 
+       # Show diff between source and pretty printed code using meld.
        var opt_meld = new OptionBool("Show diff between source and output using meld",
           "--meld")
 
+       # Check formatting instead of pretty printing.
+       #
+       # This option create a tempory pretty printed file then check if
+       # the output of the diff command on the source file and the pretty
+       # printed one is empty.
        var opt_check = new OptionBool("Check format of Nit source files", "--check")
 end
 
diff --git a/src/pretty.nit b/src/pretty.nit
new file mode 100644 (file)
index 0000000..75d9879
--- /dev/null
@@ -0,0 +1,2119 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Library used to pretty print Nit code.
+#
+# Usage:
+#
+#     import parser_util
+#     var tc = new ToolContext
+#     var nmodule = tc.parse_something("class A\nfun   toto :  Int  do   return   5\nend")
+#     var ppv = new PrettyPrinterVisitor
+#     var pmodule = ppv.pretty(nmodule)
+#     assert pmodule.write_to_string == """
+#     class A
+#     \tfun toto: Int do return 5
+#     end"""
+#
+# See `nitpretty` tool for more documentation.
+module pretty
+
+import template
+import toolcontext
+import modelbuilder
+import astutil
+
+# The `PrettyPrinterVisitor` is used to visit a node and pretty print it.
+#
+# The main method here is `visit` that performs the pretty printing of a `ANode`.
+#
+# Because some tokens like `TComment` are not stored in the AST,
+# we visit the AST like traditionnal visitor and also maintain a
+# pointer to the `current_token` (actually the next one to be printed).
+#
+# Visited productions are in charges to move the token pointer using methods such as:
+#
+# * `consume`: print `current_token` and move to the next one
+# * `skip`: move to the next token without printing the current one
+# * `skip_to`: move to a specified token skipping all the tokens before
+# * `catch_up`: consume all the tokens between `current_token` and a specified token
+# * `finish_line`: consume all the tokens between `current_token` and the end of line
+class PrettyPrinterVisitor
+       # Pretty print `n`.
+       fun pretty(n: ANode): Template do
+               clear
+               n.parentize_tokens
+
+               if n isa Prod then
+                       current_token = n.first_token
+                       visit n
+               else if n isa Token then
+                       var p = n.parent
+
+                       while p != null and not p isa Prod do
+                               p = p.parent
+                       end
+
+                       current_token = p.first_token
+                       visit p
+               end
+
+               return tpl.as(not null)
+       end
+
+       # Pretty print the whole `nmodule` with comments before and after.
+       fun pretty_nmodule(nmodule: AModule): Template do
+               clear
+               nmodule.parentize_tokens
+               current_token = nmodule.location.file.first_token
+               visit nmodule
+               catch_up nmodule.location.file.last_token
+               tpl.add "\n"
+               return tpl.as(not null)
+       end
+
+       # Prepare `self` for a new visit.
+       private fun clear do
+               tpl = new Template
+               current_token = null
+               indent = 0
+               current_length = 0
+               previous_length = 0
+               wait_addn = 0
+       end
+
+       # Visit `n` if not null.
+       fun visit(n: nullable ANode) do
+               if n == null then return
+               n.accept_pretty_printer self
+       end
+
+       # Visit a list of `Anode`.
+       fun visit_list(n: nullable ANodes[ANode]) do
+               if n == null then return
+               n.accept_pretty_printer self
+       end
+
+       # Is the node inlinable and can fit on the line.
+       fun can_inline(n: nullable ANode): Bool do
+               if n == null then return true
+               if n.must_be_inline then return true
+               if n.must_be_block then return false
+               # check length
+               if n.collect_length + current_length > max_size then return false
+               # check block is inlinable
+               return n.is_inlinable
+       end
+
+       # Collect all `TComment` between `from` and `to`.
+       fun collect_comments(from: nullable ANode, to: nullable ANode): Array[TComment] do
+               var res = new Array[TComment]
+               if from isa Prod then from = from.first_token
+               if to isa Prod then to = to.first_token
+               if from == null or to == null then return res
+
+               while from != to do
+                       if from isa TComment then res.add from
+                       from = from.as(Token).next_token
+               end
+
+               return res
+       end
+
+       # Token under cursor.
+       #
+       # This is the next token to visit.
+       var current_token: nullable Token = null
+
+       # Skip the `current_token`.
+       fun skip do current_token = current_token.next_token
+
+       # Skip `current_token` until the end of line.
+       fun skip_line do current_token = current_token.last_real_token_in_line
+
+       # Skip `current_token` until `target` is reached.
+       fun skip_to(target: nullable Token) do
+               if target == null then return
+               while current_token != target do skip
+       end
+
+       # Visit `current_token`.
+       fun consume(token: String) do
+               assert current_token.text == token
+               visit current_token
+       end
+
+       # Is there token to visit between `current_token` and `target`?
+       fun need_catch_up(target: nullable Token): Bool do
+               if target == null then return false
+               return current_token != target
+       end
+
+       # Visit all tokens between `current_token` and `target`.
+       fun catch_up(target: nullable ANode) do
+               if target == null then return
+               if current_token == null then return
+               var token: Token
+               if target isa Token then
+                       token = target
+               else if target isa Prod then
+                       token = target.first_token.as(not null)
+               else
+                       abort
+               end
+               assert current_token.location <= token.location
+               while current_token != token do visit current_token
+       end
+
+       # Visit all tokens between `current_token` and the end of line.
+       fun finish_line do
+               if current_token isa TComment then
+                       adds
+                       visit current_token
+               end
+
+               while current_token isa TEol do skip
+       end
+
+       # The template under construction.
+       private var tpl: nullable Template = null
+
+       # Current indent level.
+       var indent = 0
+
+       # Size of a tabulation in spaces.
+       var tab_size = 8
+
+       # Max line size.
+       var max_size = 80
+
+       # Length of the current line.
+       var current_length = 0
+
+       # Length of the previous line.
+       var previous_length = 0
+
+       # Is the last line a blank line?
+       fun last_line_is_blank: Bool do return previous_length == 0
+
+       # Add `t` to current template.
+       fun add(t: String) do
+               if t.is_empty then return
+               while wait_addn > 0 do
+                       tpl.add "\n"
+                       wait_addn -= 1
+               end
+               tpl.add t
+               current_length += t.length
+       end
+
+       # Add a `'\n'`.
+       fun addn do
+               if current_length == 0 and last_line_is_blank then return
+               previous_length = current_length
+               current_length = 0
+               wait_addn += 1
+       end
+
+       # End of line chars are stored until another char is added.
+       # This avoid empty files with only a '`\n`'.
+       private var wait_addn = 0
+
+       # Add `'\t' * indent`.
+       fun addt do add "\t" * indent
+
+       # Add a space.
+       fun adds do add " "
+
+       # Visit explicit receiver, implicit self will be ignored.
+       fun visit_recv(n_expr: AExpr) do
+               if not n_expr isa AImplicitSelfExpr then
+                       visit n_expr
+                       consume "."
+               end
+       end
+end
+
+# Base framework redefs
+
+redef class ANodes[E]
+       private fun accept_pretty_printer(v: PrettyPrinterVisitor) do
+               for e in self do
+                       var e_can_inline = v.can_inline(e)
+
+                       if e != first then
+                               if not e_can_inline then
+                                       v.add ","
+                                       v.addn
+                                       v.addt
+                                       v.addt
+                               else
+                                       v.add ", "
+                               end
+                       end
+
+                       v.visit e
+               end
+       end
+end
+
+redef class ANode
+       # Start visit of `self` using a `PrettyPrinterVisitor`
+       private fun accept_pretty_printer(v: PrettyPrinterVisitor) is abstract
+
+       # Collect the length (in `Char`) of the node.
+       private fun collect_length: Int is abstract
+
+       # Is `self` printable in one line?
+       private fun is_inlinable: Bool do return true
+
+       # Force `self` to be rendered as a block.
+       private var force_block = false
+
+       # Does `self` have to be rendered as a block?
+       private fun must_be_block: Bool do return force_block
+
+       # Force `self` to be rendered as a line.
+       private var force_inline = false
+
+       # Does `self` have be rendered as a line?
+       private fun must_be_inline: Bool do
+               if parent != null and parent.must_be_inline then return true
+               return force_inline
+       end
+
+       # Does `self` was written in one line before transformation?
+       private fun was_inline: Bool is abstract
+end
+
+redef class Token
+       redef fun accept_pretty_printer(v) do
+               v.add text.trim
+               v.current_token = next_token
+       end
+
+       redef fun collect_length do return text.length
+       redef fun is_inlinable do return true
+       redef fun was_inline do return true
+end
+
+redef class Prod
+       redef fun accept_pretty_printer(v) do v.visit first_token
+
+       # The token where the production really start (skipping ADoc).
+       private fun start_token: nullable Token do return first_token
+
+       # Collect all `TComment` contained in the production
+       # (between `start_token` and `end_token`).
+       private fun collect_comments: Array[TComment] do
+               var res = new Array[TComment]
+               if start_token == null or last_token == null then return res
+               var token = start_token
+
+               while token != last_token do
+                       if token isa TComment then res.add token
+                       token = token.next_token
+               end
+
+               return res
+       end
+
+       redef fun collect_length do
+               var res = 0
+               if start_token == null or last_token == null then return res
+               var token = start_token
+
+               while token != last_token do
+                       res += token.text.length
+                       token = token.next_token
+               end
+
+               res += token.text.length
+               return res
+       end
+
+       redef fun was_inline do
+               return first_token.location.line_start == last_token.location.line_end
+       end
+end
+
+# Comments
+
+redef class TComment
+       redef fun accept_pretty_printer(v) do
+               if is_adoc then
+                       v.addt
+                       super
+                       v.addn
+                       return
+               end
+
+               if is_licence then
+                       super
+                       v.addn
+                       if is_last_in_group then v.addn
+                       return
+               end
+
+               if is_orphan then
+                       v.addn
+                       v.addt
+                       super
+                       v.addn
+                       v.addn
+                       return
+               end
+
+               if is_inline then
+                       if next_token isa TComment and is_first_in_group then v.addn
+                       v.addt
+                       super
+                       v.addn
+                       var prev_token = self.prev_token
+                       if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
+                       return
+               end
+
+               super
+       end
+
+       # Is `self` part of an `ADoc`?
+       private fun is_adoc: Bool do return parent isa ADoc and parent.parent != null
+
+       # Is `self` part of a licence?
+       private fun is_licence: Bool do
+               var prev_token = self.prev_token
+
+               if prev_token == null then
+                       return true
+               else if prev_token isa TComment then
+                       return prev_token.is_licence
+               else
+                       return false
+               end
+       end
+
+       # Is `self` starts and ends its line?
+       private fun is_inline: Bool do
+               return self == first_real_token_in_line and self == last_real_token_in_line
+       end
+
+       # Is `self` an orphan line (blank before and after)?
+       private fun is_orphan: Bool do
+               return prev_token isa TEol and
+                  (prev_token.prev_token isa TEol or prev_token.prev_token isa TComment) and
+                  next_token isa TEol
+       end
+
+       # Is `self` the first comment of a group of comment?
+       private fun is_first_in_group: Bool do return not prev_token isa TComment
+
+       # Is `self` the last comment of a group of comments?
+       private fun is_last_in_group: Bool do return not next_token isa TComment
+end
+
+redef class ADoc
+       redef fun accept_pretty_printer(v) do for comment in n_comment do v.visit comment
+       redef fun is_inlinable do return n_comment.length <= 1
+end
+
+# Annotations
+
+redef class AAnnotations
+       redef fun accept_pretty_printer(v) do
+               v.adds
+               v.consume "is"
+
+               if v.can_inline(self) then
+                       v.adds
+                       for n_item in n_items do
+                               v.visit n_item
+                               if n_item != n_items.last then
+                                       v.add ", "
+                               end
+                       end
+                       v.finish_line
+               else if n_items.length > 1 then
+                       v.addn
+                       v.indent += 1
+
+                       for n_item in n_items do
+                               v.addt
+                               v.visit n_item
+                               v.finish_line
+                               v.addn
+                       end
+
+                       v.indent -= 1
+               end
+               if not was_inline and v.current_token isa TKwend then v.skip
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               for annot in n_items do if not annot.is_inlinable then return false
+               return true
+       end
+end
+
+redef class AAnnotation
+       redef fun accept_pretty_printer(v) do
+               v.visit n_atid
+               if not n_args.is_empty then
+                       if n_opar == null then
+                               v.adds
+                       else
+                               v.visit n_opar
+                       end
+                       v.visit_list n_args
+                       v.visit n_cpar
+               end
+       end
+end
+
+redef class ATypeExpr
+       redef fun accept_pretty_printer(v) do v.visit n_type
+end
+
+# Modules
+
+redef class AModule
+       redef fun accept_pretty_printer(v) do
+               v.catch_up start_token
+               v.visit n_moduledecl
+
+               if not n_imports.is_empty then
+                       v.addn
+
+                       for n_import in n_imports do
+                               v.catch_up n_import
+                               v.visit n_import
+                       end
+               end
+
+               if not n_extern_code_blocks.is_empty then
+                       v.addn
+
+                       for n_extern_block in n_extern_code_blocks do
+                               v.catch_up n_extern_block
+                               v.visit n_extern_block
+                               v.addn
+                               if n_extern_block != n_extern_code_blocks.last then v.addn
+                       end
+
+                       if not n_classdefs.is_empty then v.addn
+               end
+
+               if not n_classdefs.is_empty then
+                       v.addn
+
+                       for n_classdef in n_classdefs do
+                               v.catch_up n_classdef
+                               v.visit n_classdef
+                               if n_classdef != n_classdefs.last then v.addn
+                       end
+               end
+
+               assert v.indent == 0
+       end
+
+       # Skip doc if any.
+       redef fun start_token do
+               if n_moduledecl != null then return n_moduledecl.first_token
+               if not n_imports.is_empty then return n_imports.first.first_token
+               if not n_classdefs.is_empty then return n_classdefs.first.first_token
+               return first_token
+       end
+
+       redef fun is_inlinable do return false
+end
+
+redef class AModuledecl
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               v.visit n_kwmodule
+               v.adds
+               v.visit n_name
+
+               if n_annotations != null then
+                       var annot_inline = v.can_inline(n_annotations)
+                       v.visit n_annotations
+
+                       if not annot_inline then
+                               if v.current_token isa TKwend then
+                                       v.consume "end"
+                                       v.finish_line
+                               else
+                                       v.add "end"
+                               end
+                       end
+               end
+
+               v.finish_line
+               v.addn
+       end
+end
+
+redef class AModuleName
+       redef fun accept_pretty_printer(v) do
+               for path in n_path do
+                       v.visit path
+                       v.add "::"
+               end
+
+               v.visit n_id
+       end
+end
+
+redef class ANoImport
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwimport
+               v.adds
+               v.visit n_kwend
+               v.finish_line
+               v.addn
+       end
+end
+
+redef class AStdImport
+       redef fun accept_pretty_printer(v) do
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               v.visit n_kwimport
+               v.adds
+               v.visit n_name
+               v.finish_line
+               v.addn
+       end
+end
+
+# Classes
+
+redef class AClassdef
+       redef fun accept_pretty_printer(v) do
+               for n_propdef in n_propdefs do
+                       v.catch_up n_propdef
+
+                       if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
+                               if n_propdef != n_propdefs.first then v.addn
+                               v.visit n_propdef
+                               if n_propdef != n_propdefs.last then v.addn
+                       else
+                               v.visit n_propdef
+                       end
+               end
+       end
+end
+
+redef class AStdClassdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               var can_inline = v.can_inline(self)
+
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               if n_kwredef != null then
+                       v.visit n_kwredef
+                       v.adds
+               end
+
+               v.visit n_classkind
+               v.adds
+               v.visit n_id
+
+               if not n_formaldefs.is_empty then
+                       v.consume "["
+                       v.visit_list n_formaldefs
+                       v.consume "]"
+               end
+
+               if n_extern_code_block != null then
+                       v.adds
+                       v.visit n_extern_code_block
+               end
+
+               if can_inline then
+                       v.adds
+
+                       if not n_superclasses.is_empty then
+                               for n_superclass in n_superclasses do
+                                       v.visit n_superclass
+                                       v.adds
+                               end
+                       end
+               else
+                       v.finish_line
+                       v.addn
+                       v.indent += 1
+
+                       for n_superclass in n_superclasses do
+                               v.catch_up n_superclass
+                               v.addt
+                               v.visit n_superclass
+                               v.finish_line
+                               v.addn
+                       end
+
+                       if not n_superclasses.is_empty and not n_propdefs.is_empty then
+                               v.addn
+                       end
+
+                       super
+                       v.catch_up n_kwend
+                       v.indent -= 1
+               end
+
+               v.visit n_kwend
+               v.finish_line
+               v.addn
+               assert v.indent == 0
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if not n_propdefs.is_empty then return false
+               if n_superclasses.length > 1 then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+
+       redef fun start_token do
+               if not n_visibility isa APublicVisibility then return n_visibility.first_token
+               if n_kwredef != null then return n_kwredef
+               return n_classkind.first_token
+       end
+end
+
+redef class AAbstractClasskind
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwabstract
+               v.adds
+               v.visit n_kwclass
+       end
+end
+
+redef class AExternClasskind
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwextern
+               v.adds
+               v.visit n_kwclass
+       end
+end
+
+redef class AFormaldef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+       end
+end
+
+redef class AType
+       redef fun accept_pretty_printer(v) do
+               if n_kwnullable != null then
+                       v.visit n_kwnullable
+                       v.adds
+               end
+
+               v.visit n_id
+
+               if not n_types.is_empty then
+                       v.consume "["
+                       v.visit_list n_types
+                       v.consume "]"
+               end
+       end
+end
+
+redef class ASuperclass
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwsuper
+               v.adds
+               v.visit n_type
+       end
+end
+
+# Properties
+
+redef class APropdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_doc
+               v.addt
+
+               if not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
+
+               if n_kwredef != null then
+                       v.visit n_kwredef
+                       v.adds
+               end
+       end
+
+       redef fun start_token do
+               if n_doc == null then return super
+               return n_doc.last_token.next_token
+       end
+end
+
+redef class AAttrPropdef
+       redef fun accept_pretty_printer(v) do
+               super
+               v.visit n_kwvar
+               v.adds
+               v.visit n_id2
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_expr != null then
+                       v.adds
+                       v.consume "="
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_annotations != null then v.visit n_annotations
+               v.finish_line
+               v.addn
+       end
+
+       redef fun first_token do
+               if n_doc != null then return n_doc.first_token
+               if not n_visibility isa APublicVisibility then return n_visibility.first_token
+               if n_kwredef != null then return n_kwredef
+               return n_kwvar
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class ATypePropdef
+       redef fun accept_pretty_printer(v) do
+               super
+               v.visit n_kwtype
+               v.adds
+               v.visit n_id
+               v.consume ":"
+               v.adds
+               v.visit n_type
+               v.finish_line
+               v.addn
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AMethPropdef
+       redef fun accept_pretty_printer(v) do
+               #  TODO: Handle extern annotations
+
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+               super
+               if n_kwinit != null then v.visit n_kwinit
+               if n_kwmeth != null then v.visit n_kwmeth
+               if n_kwnew != null then v.visit n_kwnew
+
+               if not n_methid == null then
+                       v.adds
+                       v.visit n_methid
+               end
+
+               v.visit n_signature
+
+               if n_annotations != null then
+                       v.visit n_annotations
+               else
+                       v.adds
+               end
+
+               if n_extern_calls != null or n_extern_code_block != null then
+                       if n_annotations != null then v.adds
+                       if n_extern_calls != null then v.visit n_extern_calls
+                       if n_extern_code_block != null then v.visit n_extern_code_block
+               end
+
+               var n_block = self.n_block
+
+               if n_block != null then
+                       while not v.current_token isa TKwdo do v.skip
+                       if n_annotations != null then
+                               if v.can_inline(n_annotations) then
+                                       v.adds
+                               else
+                                       v.addt
+                               end
+                       end
+                       v.consume "do"
+
+                       if can_inline then
+                               v.adds
+
+                               if n_block isa ABlockExpr then
+                                       if n_block.n_expr.is_empty then
+                                               v.visit n_block.n_kwend
+                                       else
+                                               v.visit n_block.n_expr.first
+                                               v.current_token = n_block.n_kwend
+                                               v.skip
+                                       end
+                               else
+                                       v.visit n_block
+                                       if v.current_token isa TKwend then v.skip
+                               end
+                       else
+                               v.finish_line
+                               v.addn
+                               v.indent += 1
+
+                               if n_block isa ABlockExpr then
+                                       n_block.force_block = true
+                                       v.visit n_block
+                                       v.catch_up n_block.n_kwend
+                               else
+                                       v.addt
+                                       v.visit n_block
+                                       v.addn
+                               end
+
+                               v.indent -= 1
+                               v.addt
+                               if n_block isa ABlockExpr then
+                                       v.visit n_block.n_kwend
+                               else
+                                       v.add "end"
+                               end
+                       end
+               end
+
+               v.finish_line
+               v.addn
+               assert v.indent == before
+       end
+
+       # Can be inlined if:
+       # * block is empty or can be inlined
+       # * contains no comments
+       redef fun is_inlinable do
+               if not super then return false
+               if n_annotations != null and not n_annotations.is_inlinable then return false
+               if n_block != null and not n_block.is_inlinable then return false
+               if n_extern_calls != null and not n_extern_calls.is_inlinable then return false
+               if n_extern_code_block != null and not n_extern_code_block.is_inlinable then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+end
+
+redef class AMainMethPropdef
+       redef fun accept_pretty_printer(v) do
+               v.visit n_block
+               v.addn
+       end
+end
+
+redef class ASignature
+       redef fun accept_pretty_printer(v) do
+               if not n_params.is_empty then
+                       v.consume "("
+                       v.visit_list n_params
+                       v.consume ")"
+               end
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+       end
+end
+
+redef class AParam
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_dotdotdot != null then v.visit n_dotdotdot
+       end
+end
+
+# Extern
+
+redef class AExternCalls
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwimport
+
+               if can_inline then
+                       v.adds
+                       v.visit_list n_extern_calls
+               else
+                       v.addn
+                       v.addt
+                       v.addt
+                       v.visit_list n_extern_calls
+               end
+
+               v.adds
+       end
+end
+
+redef class AFullPropExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.visit n_dot
+               v.visit n_methid
+       end
+end
+
+redef class ALocalPropExternCall
+       redef fun accept_pretty_printer(v) do v.visit n_methid
+end
+
+redef class AInitPropExternCall
+       redef fun accept_pretty_printer(v) do v.visit n_type
+end
+
+redef class ACastAsExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_from_type
+               v.visit n_dot
+               v.visit n_kwas
+               v.consume "("
+               v.visit n_to_type
+               v.consume ")"
+       end
+end
+
+redef class AAsNullableExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.consume "."
+               v.visit n_kwas
+               v.adds
+               v.visit n_kwnullable
+       end
+end
+
+redef class AAsNotNullableExternCall
+       redef fun accept_pretty_printer(v) do
+               v.visit n_type
+               v.consume "."
+               v.visit n_kwas
+               v.adds
+               v.visit n_kwnot
+               v.adds
+               v.visit n_kwnullable
+       end
+end
+
+redef class AExternCodeBlock
+       redef fun accept_pretty_printer(v) do
+               if n_in_language != null then
+                       v.visit n_in_language
+                       v.adds
+               end
+
+               v.visit n_extern_code_segment
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               return n_extern_code_segment.is_inlinable
+       end
+end
+
+redef class AInLanguage
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwin
+               v.adds
+               v.visit n_string
+       end
+end
+
+redef class TExternCodeSegment
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if can_inline then
+                       super
+               else
+                       var text = text.substring(2, text.length - 4)
+                       var lines = text.r_trim.split("\n")
+
+                       if text.is_empty then
+                               v.add "`\{`\}"
+                       else
+                               v.add "`\{"
+
+                               if not lines.first.trim.is_empty then
+                                       v.addn
+                                       lines.first.l_trim
+                                       v.indent += 1
+                                       v.addt
+                                       v.indent -= 1
+                               end
+
+                               for line in lines do
+                                       v.add line.r_trim
+                                       v.addn
+                               end
+
+                               v.addt
+                               v.add "`\}"
+                       end
+
+                       v.current_token = next_token
+               end
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               return location.line_start == location.line_end
+       end
+end
+
+# Blocks
+
+redef class ABlockExpr
+       redef fun accept_pretty_printer(v) do
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+
+               if can_inline and not n_expr.is_empty then
+                       v.visit n_expr.first
+                       v.finish_line
+               else
+                       for nexpr in n_expr do
+                               var expr_inline = v.can_inline(nexpr)
+                               if not expr_inline and nexpr != n_expr.first then v.addn
+                               v.catch_up nexpr
+                               v.addt
+                               v.visit nexpr
+                               v.finish_line
+                               v.addn
+                               if not expr_inline and nexpr != n_expr.last then v.addn
+                       end
+               end
+
+               assert v.indent == before
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if not collect_comments.is_empty then return false
+
+               if not n_expr.is_empty then
+                       if n_expr.length > 1 then return false
+                       if not n_expr.first.is_inlinable then return false
+               end
+
+               return true
+       end
+end
+
+redef class AIfExpr
+       redef fun accept_pretty_printer(v) do
+               var before = v.indent
+               var can_inline = v.can_inline(self)
+               v.visit n_kwif
+               v.adds
+
+               if v.can_inline(n_expr) then
+                       v.visit n_expr
+                       v.adds
+               else
+                       v.visit n_expr
+                       v.addn
+                       v.addt
+               end
+
+               # skip comments before `then` token
+               while not v.current_token isa TKwthen do v.skip
+               v.consume "then"
+               var n_else = self.n_else
+
+               if can_inline then
+                       v.adds
+                       if n_then != null then v.visit n_then
+
+                       if has_else(v) then
+                               n_else.force_inline = true
+                               v.adds
+                               v.consume "else"
+                               v.adds
+                               v.visit n_else
+                       else if n_then == null then
+                               v.add "end"
+                       end
+
+                       v.skip_to last_token.last_real_token_in_line
+               else
+                       v.finish_line
+                       v.addn
+                       v.indent += 1
+
+                       if n_then != null then
+                               if n_then isa ABlockExpr then
+                                       n_then.force_block = true
+                                       v.visit n_then
+                               else
+                                       v.addt
+                                       v.visit n_then
+                                       v.addn
+                               end
+                       end
+
+                       if has_else(v) then
+                               while not v.current_token isa TKwelse do
+                                       v.consume v.current_token.text
+                               end
+
+                               v.indent -= 1
+                               v.addt
+                               v.consume "else"
+
+                               if n_else isa AIfExpr then
+                                       n_else.force_block = true
+                                       v.adds
+                                       v.visit n_else
+                               else
+                                       v.finish_line
+                                       v.addn
+                                       v.indent += 1
+
+                                       if n_else isa ABlockExpr then
+                                               n_else.force_block = true
+                                               v.visit n_else
+                                       else
+                                               v.addt
+                                               v.visit n_else
+                                               v.addn
+                                       end
+
+                                       if last_token isa TKwend then
+                                               v.catch_up last_token
+                                               v.indent -= 1
+                                               v.addt
+                                               v.consume "end"
+                                       else
+                                               v.indent -= 1
+                                               v.addt
+                                               v.add "end"
+                                       end
+                               end
+                       else
+                               if last_token.location >= v.current_token.location then v.catch_up last_token
+                               v.indent -= 1
+                               v.addt
+                               v.add "end"
+                               if v.current_token isa TKwend then v.skip
+                       end
+               end
+
+               assert v.indent == before
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if n_then != null and not n_then.is_inlinable then return false
+               var n_else = self.n_else
+               if (n_else isa ABlockExpr and not n_else.n_expr.is_empty) or
+                  (not n_else isa ABlockExpr and n_else != null) then
+                       return false
+               end
+               if not collect_comments.is_empty then return false
+               return true
+       end
+
+       # Does this `if` statement contains a `else` part?
+       private fun has_else(v: PrettyPrinterVisitor): Bool do
+               var n_else = n_else
+               if n_else == null then return false
+               var n_kwelse = collect_kwelse
+               if n_kwelse == null then return false
+
+               if n_else isa ABlockExpr then
+                       var comments: Array[TComment]
+
+                       if n_then == null then
+                               comments = v.collect_comments(n_expr.last_token, n_else.last_token)
+                       else
+                               comments = v.collect_comments(n_then.last_token, n_else.last_token)
+                       end
+
+                       if not comments.is_empty then return true
+                       return not n_else.n_expr.is_empty
+               end
+
+               return true
+       end
+
+       # Lookup for `else` token in `self`.
+       private fun collect_kwelse: nullable TKwelse do
+               var token = first_token
+
+               while token != last_token do
+                       if token isa TKwelse then return token
+                       token = token.next_token
+               end
+
+               return null
+       end
+end
+
+# Used to factorize work on loops.
+private class ALoopHelper
+       super AExpr
+
+       fun loop_block: nullable ANode is abstract
+       fun loop_label: nullable ANode is abstract
+
+       fun visit_loop_block(v: PrettyPrinterVisitor) do
+               var n_block = loop_block
+               v.finish_line
+               v.addn
+               v.indent += 1
+
+               if n_block isa ABlockExpr then
+                       n_block.force_block = true
+                       v.visit n_block
+                       v.catch_up n_block.n_kwend
+                       v.indent -= 1
+                       v.addt
+                       v.visit n_block.n_kwend
+               else
+                       v.addt
+                       v.visit n_block
+                       v.addn
+                       v.indent -= 1
+                       v.addt
+                       v.add "end"
+               end
+
+               if loop_label != null then
+                       v.adds
+                       v.visit loop_label
+               end
+       end
+
+       fun visit_loop_inline(v: PrettyPrinterVisitor) do
+               var n_block = loop_block
+               v.adds
+
+               if n_block isa ABlockExpr then
+                       if n_block.n_expr.is_empty then
+                               v.visit n_block.n_kwend
+                       else
+                               v.visit n_block.n_expr.first
+                               v.current_token = n_block.n_kwend
+                               v.skip
+                       end
+               else
+                       v.visit n_block
+                       if v.current_token isa TKwend then v.skip
+               end
+
+               if loop_label != null then
+                       v.adds
+                       v.visit loop_label
+               end
+       end
+
+       redef fun is_inlinable do
+               var n_block = loop_block
+               if not super then return false
+               if n_block isa ABlockExpr and not n_block.is_inlinable then return false
+               if not collect_comments.is_empty then return false
+               return true
+       end
+end
+
+redef class ALoopExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwloop
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class AWhileExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwwhile
+               v.adds
+               v.visit n_expr
+               v.adds
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class ADoExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class AForExpr
+       super ALoopHelper
+
+       redef fun loop_block do return n_block
+       redef fun loop_label do return n_label
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwfor
+               v.adds
+
+               for n_id in n_ids do
+                       v.visit n_id
+                       if n_id != n_ids.last then v.add ", "
+               end
+
+               v.adds
+               v.consume "in"
+               v.adds
+               v.visit n_expr
+               v.adds
+               v.visit n_kwdo
+               if can_inline then visit_loop_inline v else visit_loop_block v
+       end
+end
+
+redef class ABreakExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwbreak
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_label != null then
+                       v.adds
+                       v.visit n_label
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AContinueExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwcontinue
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+
+               if n_label != null then
+                       v.adds
+                       v.visit n_label
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Calls
+
+redef class ASendExpr
+       redef fun is_inlinable do return true
+end
+
+redef class ACallExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit_recv n_expr
+
+               if not n_expr isa AImplicitSelfExpr and not can_inline then
+                       v.addn
+                       v.addt
+                       v.addt
+               end
+
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       if is_stmt and n_args.n_exprs.length == 1 then
+                               v.adds
+                               if v.current_token isa TOpar then v.skip
+                               v.visit n_args.n_exprs.first
+                               if v.current_token isa TCpar then v.skip
+                       else
+                               if v.current_token isa TOpar then
+                                       v.consume "("
+                               else
+                                       v.adds
+                               end
+
+                               v.visit_list n_args.n_exprs
+                               if v.current_token isa TCpar then v.consume ")"
+                       end
+               end
+       end
+
+       # Is the call alone on its line?
+       fun is_stmt: Bool do return parent isa ABlockExpr
+end
+
+redef class ACallAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+
+               v.adds
+               v.consume "="
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ACallReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ABraExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+       end
+end
+
+redef class ABraAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class ABraReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "["
+                       v.visit_list n_args.n_exprs
+                       v.consume "]"
+               end
+
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAssignMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+               v.visit n_assign
+       end
+end
+
+redef class ABraMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_cbra
+       end
+end
+
+redef class ABraassignMethid
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_cbra
+               v.visit n_assign
+       end
+end
+
+redef class AInitExpr
+       redef fun accept_pretty_printer(v) do
+               if not n_expr isa AImplicitSelfExpr then
+                       v.visit n_expr
+                       v.consume "."
+               end
+
+               v.visit n_kwinit
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+       end
+end
+
+redef class ANewExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwnew
+               v.adds
+               v.visit n_type
+
+               if n_id != null then
+                       v.consume "."
+
+                       if not can_inline then
+                               v.addn
+                               v.addt
+                               v.addt
+                       end
+
+                       v.visit n_id
+               end
+
+               if not n_args.n_exprs.is_empty then
+                       v.consume "("
+                       v.visit_list n_args.n_exprs
+                       v.consume ")"
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Attributes
+
+redef class AAttrExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AAttrAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAttrReassignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit_recv n_expr
+               v.visit n_id
+               v.adds
+               v.visit n_assign_op
+               v.adds
+               v.visit n_value
+       end
+end
+
+# Exprs
+
+redef class AVardeclExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwvar
+               v.adds
+               v.visit n_id
+
+               if n_type != null then
+                       v.consume ":"
+                       v.adds
+                       v.visit n_type
+               end
+
+               if n_expr != null then
+                       v.adds
+                       v.consume "="
+                       v.adds
+                       v.visit n_expr
+               end
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AVarAssignExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_id
+               v.adds
+               v.visit n_assign
+               v.adds
+               v.visit n_value
+       end
+end
+
+redef class AAssertExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+               v.visit n_kwassert
+
+               if n_id != null then
+                       v.adds
+                       v.visit n_id
+                       v.consume ":"
+               end
+
+               v.adds
+               v.visit n_expr
+               var n_else = self.n_else
+
+               if n_else != null then
+                       v.adds
+                       v.consume "else"
+
+                       if can_inline then
+                               v.adds
+                               v.visit n_else
+                       else
+                               v.addn
+
+                               if n_else isa ABlockExpr then
+                                       v.indent += 1
+                                       n_else.force_block = true
+                                       v.visit n_else
+                                       v.indent -= 1
+                                       v.addt
+                                       v.visit n_else.n_kwend
+                               else
+                                       v.indent += 1
+                                       v.addt
+                                       v.visit n_else
+                                       v.addn
+                                       v.indent -= 1
+                                       v.addt
+                                       v.add "end"
+                               end
+                       end
+               end
+       end
+
+       redef fun is_inlinable do
+               if not super then return false
+               if n_else != null and not n_else.is_inlinable then return false
+               return true
+       end
+end
+
+redef class AReturnExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwreturn
+
+               if n_expr != null then
+                       v.adds
+                       v.visit n_expr
+               end
+       end
+end
+
+redef class ASuperExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwsuper
+
+               if not n_args.n_exprs.is_empty then
+                       if is_stmt and n_args.n_exprs.length == 1 then
+                               v.adds
+                               if v.current_token isa TOpar then v.skip
+                               v.visit n_args.n_exprs.first
+                               if v.current_token isa TCpar then v.skip
+                       else
+                               if v.current_token isa TOpar then
+                                       v.consume "("
+                               else
+                                       v.adds
+                               end
+
+                               v.visit_list n_args.n_exprs
+                               if v.current_token isa TCpar then v.consume ")"
+                       end
+               end
+       end
+
+       # Is the call alone on its line?
+       fun is_stmt: Bool do return self.first_token.is_starting_line
+
+       redef fun is_inlinable do return true
+end
+
+redef class AOnceExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwonce
+               v.adds
+               v.visit n_expr
+       end
+
+       redef fun is_inlinable do return true
+end
+
+redef class AAbortExpr
+       redef fun accept_pretty_printer(v) do v.visit n_kwabort
+       redef fun is_inlinable do return true
+end
+
+redef class ANotExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_kwnot
+               v.adds
+               v.visit n_expr
+       end
+end
+
+redef class AAsCastExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.consume "."
+               v.visit n_kwas
+               v.visit n_opar
+               v.visit n_type
+               v.visit n_cpar
+       end
+end
+
+redef class AAsNotnullExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.consume "."
+               v.visit n_kwas
+               v.visit n_opar
+               v.visit n_kwnot
+               v.adds
+               v.visit n_kwnull
+               v.visit n_cpar
+       end
+end
+
+# Binops
+
+# Used to factorize work on Or, And, Implies and Binop expressions.
+private class ABinOpHelper
+       super AExpr
+
+       fun bin_expr1: AExpr is abstract
+       fun bin_expr2: AExpr is abstract
+
+       # Operator string
+       fun bin_op: String is abstract
+
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if not can_inline then
+                       if (self isa ABinopExpr and bin_expr1 isa ABinopExpr) or
+                          (self isa AAndExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr)) or
+                          (self isa AOrExpr and (bin_expr1 isa AAndExpr or bin_expr1 isa AOrExpr))
+                       then
+                               bin_expr1.force_block = true
+                       end
+               end
+
+               v.visit bin_expr1
+               v.adds
+               v.consume bin_op
+
+               if can_inline then
+                       v.adds
+                       v.visit bin_expr2
+               else
+                       v.addn
+                       v.addt
+                       v.addt
+                       v.visit bin_expr2
+               end
+       end
+end
+
+redef class AAndExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "and"
+end
+
+redef class AOrExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "or"
+end
+
+redef class AImpliesExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+       redef fun bin_op do return "implies"
+end
+
+redef class ABinopExpr
+       super ABinOpHelper
+
+       redef fun bin_expr1 do return n_expr
+       redef fun bin_expr2 do return n_expr2
+end
+
+redef class AEqExpr
+       redef fun bin_op do return "=="
+end
+
+redef class AGeExpr
+       redef fun bin_op do return ">="
+end
+
+redef class AGgExpr
+       redef fun bin_op do return ">>"
+end
+
+redef class AGtExpr
+       redef fun bin_op do return ">"
+end
+
+redef class ALeExpr
+       redef fun bin_op do return "<="
+end
+
+redef class ALlExpr
+       redef fun bin_op do return "<<"
+end
+
+redef class ALtExpr
+       redef fun bin_op do return "<"
+end
+
+redef class AMinusExpr
+       redef fun bin_op do return "-"
+end
+
+redef class ANeExpr
+       redef fun bin_op do return "!="
+end
+
+redef class APercentExpr
+       redef fun bin_op do return "%"
+end
+
+redef class APlusExpr
+       redef fun bin_op do return "+"
+end
+
+redef class ASlashExpr
+       redef fun bin_op do return "/"
+end
+
+redef class AStarExpr
+       redef fun bin_op do return "*"
+end
+
+redef class AStarstarExpr
+       redef fun bin_op do return "**"
+end
+
+redef class AStarshipExpr
+       redef fun bin_op do return "<=>"
+end
+
+redef class AIsaExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.adds
+               v.consume "isa"
+               v.adds
+               v.visit n_type
+       end
+end
+
+redef class AOrElseExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_expr
+               v.adds
+               v.consume "or"
+               v.adds
+               v.consume "else"
+               v.adds
+               v.visit n_expr2
+       end
+
+       redef fun is_inlinable do return true
+end
+
+# Syntax
+
+redef class AUminusExpr
+       redef fun accept_pretty_printer(v) do
+               v.consume "-"
+               v.visit n_expr
+       end
+end
+
+redef class ANullExpr
+       redef fun accept_pretty_printer(v) do v.visit n_kwnull
+       redef fun is_inlinable do return true
+end
+
+redef class AParExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_opar
+               v.visit n_expr
+               v.visit n_cpar
+       end
+end
+
+redef class AArrayExpr
+       redef fun accept_pretty_printer(v) do
+               v.consume "["
+               v.visit_list n_exprs.n_exprs
+               v.consume "]"
+       end
+end
+
+redef class ACrangeExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_expr
+               v.consume ".."
+               v.visit n_expr2
+               v.visit n_cbra
+       end
+end
+
+redef class AOrangeExpr
+       redef fun accept_pretty_printer(v) do
+               v.visit n_obra
+               v.visit n_expr
+               v.consume ".."
+               v.visit n_expr2
+               v.visit n_cbra
+       end
+end
+
+# Strings
+
+redef class AStringFormExpr
+       redef fun accept_pretty_printer(v) do
+               var can_inline = v.can_inline(self)
+
+               if can_inline then
+                       v.visit n_string
+               else
+                       var text = n_string.text
+                       var i = 0
+
+                       while i < text.length do
+                               v.add text[i].to_s
+
+                               if v.current_length >= v.max_size and i <= text.length - 3 then
+                                       v.add "\" +"
+                                       v.addn
+                                       v.indent += 1
+                                       v.addt
+                                       v.indent -= 1
+                                       v.add "\""
+                               end
+
+                               i += 1
+                       end
+
+                       v.current_token = n_string.next_token
+               end
+       end
+end
+
+redef class ASuperstringExpr
+       redef fun accept_pretty_printer(v) do
+               for n_expr in n_exprs do v.visit n_expr
+       end
+
+       redef fun must_be_inline do
+               if super then return true
+
+               if not n_exprs.is_empty then
+                       var first = n_exprs.first
+                       return first isa AStringFormExpr and first.n_string.text.has_prefix("\"\"\"")
+               end
+
+               return false
+       end
+end
index 32b13e3..7881db0 100644 (file)
@@ -111,7 +111,7 @@ class RapidTypeAnalysis
        # Return a ready-to-save CSV document objet that agregates informations about live types.
        # Each discovered type is listed in a line, with its status: resolution, liveness, cast-liveness.
        # Note: types are listed in an alphanumeric order to improve human reading.
-       fun live_types_to_csv: CSVDocument
+       fun live_types_to_csv: CsvDocument
        do
                # Gather all kind of type
                var typeset = new HashSet[MType]
@@ -121,7 +121,8 @@ class RapidTypeAnalysis
                typeset.add_all(live_open_cast_types)
                var types = typeset.to_a
                (new CachedAlphaComparator).sort(types)
-               var res = new CSVDocument
+               var res = new CsvDocument
+               res.format = new CsvFormat('"', ';', "\n")
                res.header = ["Type", "Resolution", "Liveness", "Cast-liveness"]
                for t in types do
                        var reso
@@ -130,7 +131,7 @@ class RapidTypeAnalysis
                        if t isa MClassType and (live_types.has(t) or live_open_types.has(t)) then live = "LIVE" else live = "DEAD"
                        var cast
                        if live_cast_types.has(t) or live_open_cast_types.has(t) then cast = "CAST LIVE" else cast = "CAST DEAD"
-                       res.add_line(t, reso, live, cast)
+                       res.add_record(t, reso, live, cast)
                end
                return res
        end
index 0f1b117..37f5c74 100644 (file)
@@ -141,19 +141,26 @@ class TestSuite
                if not toolcontext.test_dir.file_exists then
                        toolcontext.test_dir.mkdir
                end
+               write_to_nit
+               compile
                toolcontext.info("Execute test-suite {mmodule.name}", 1)
                var before_module = self.before_module
-               if not before_module == null then run_case(before_module)
-               for case in test_cases do run_case(case)
+               if not before_module == null then before_module.run
+               for case in test_cases do case.run
                var after_module = self.after_module
-               if not after_module == null then run_case(after_module)
+               if not after_module == null then after_module.run
        end
 
-       # Execute a test case
-       fun run_case(test_case: TestCase) do
-               test_case.write_to_nit
-               test_case.compile
-               test_case.run
+       # Write the test unit for `self` in a nit compilable file.
+       fun write_to_nit do
+               var file = new Template
+               file.addn "intrude import test_suite"
+               file.addn "import {mmodule.name}\n"
+               file.addn "var name = args.first"
+               for case in test_cases do
+                       case.write_to_nit(file)
+               end
+               file.write_to_file("{test_file}.nit")
        end
 
        # Return the test suite in XML format compatible with Jenkins.
@@ -161,56 +168,22 @@ class TestSuite
        fun to_xml: HTMLTag do
                var n = new HTMLTag("testsuite")
                n.attr("package", mmodule.name)
-               for test in test_cases do n.add test.to_xml
+               if failure != null then
+                       var f = new HTMLTag("failure")
+                       f.attr("message", failure.to_s)
+                       n.add f
+               else
+                       for test in test_cases do n.add test.to_xml
+               end
                return n
        end
-end
-
-# A test case is a unit test considering only a `MMethodDef`.
-class TestCase
-
-       # Test suite wich `self` belongs to.
-       var test_suite: TestSuite
-
-       # Test method to be compiled and tested.
-       var test_method: MMethodDef
-
-       # `ToolContext` to use to display messages and find `nitg` bin.
-       var toolcontext: ToolContext
-
-       # `MMethodDef` to call before the test case.
-       var before_test: nullable MMethodDef = null
-
-       # `MMethodDef` to call after the test case.
-       var after_test: nullable MMethodDef = null
 
        # Generated test file name.
        fun test_file: String do
-               var dir = toolcontext.test_dir
-               var mod = test_method.mclassdef.mmodule.name
-               var cls = test_method.mclassdef.name
-               var name = test_method.name
-               return "{dir}/{mod}_{cls}_{name}"
-       end
-
-       # Generate the test unit in a nit file.
-       fun write_to_nit do
-               var name = test_method.name
-               var file = new Template
-               file.addn "intrude import test_suite"
-               file.addn "import {test_method.mclassdef.mmodule.name}\n"
-               if test_method.mproperty.is_toplevel then
-                       file.addn name
-               else
-                       file.addn "var subject = new {test_method.mclassdef.name}.nitunit"
-                       if before_test != null then file.addn "subject.{before_test.name}"
-                       file.addn "subject.{name}"
-                       if after_test != null then file.addn "subject.{after_test.name}"
-               end
-               file.write_to_file("{test_file}.nit")
+               return toolcontext.test_dir / "gen_{mmodule.name.escape_to_c}"
        end
 
-       # Compile all test cases in once.
+       # Compile all `test_cases` cases in one file.
        fun compile do
                # find nitg
                var nit_dir = toolcontext.nit_dir
@@ -221,49 +194,86 @@ class TestCase
                end
                # compile test suite
                var file = test_file
-               var include_dir = test_method.mclassdef.mmodule.location.file.filename.dirname
+               var include_dir = mmodule.location.file.filename.dirname
                var cmd = "{nitg} --no-color '{file}.nit' -I {include_dir} -o '{file}.bin' > '{file}.out' 2>&1 </dev/null"
                var res = sys.system(cmd)
                var f = new IFStream.open("{file}.out")
                var msg = f.read_all
                f.close
                # set test case result
-               var loc = test_method.location
+               var loc = mmodule.location
                if res != 0 then
                        failure = msg
-                       toolcontext.warning(loc, "failure", "FAILURE: {test_method.name} (in file {file}.nit): {msg}")
+                       toolcontext.warning(loc, "failure", "FAILURE: {mmodule.name} (in file {file}.nit): {msg}")
                        toolcontext.modelbuilder.failed_tests += 1
                end
                toolcontext.check_errors
        end
 
+       # Error occured during test-suite compilation.
+       var failure: nullable String = null
+end
+
+# A test case is a unit test considering only a `MMethodDef`.
+class TestCase
+
+       # Test suite wich `self` belongs to.
+       var test_suite: TestSuite
+
+       # Test method to be compiled and tested.
+       var test_method: MMethodDef
+
+       # `ToolContext` to use to display messages and find `nitg` bin.
+       var toolcontext: ToolContext
+
+       # `MMethodDef` to call before the test case.
+       var before_test: nullable MMethodDef = null
+
+       # `MMethodDef` to call after the test case.
+       var after_test: nullable MMethodDef = null
+
+       # Generate the test unit for `self` in `file`.
+       fun write_to_nit(file: Template) do
+               var name = test_method.name
+               file.addn "if name == \"{name}\" then"
+               if test_method.mproperty.is_toplevel then
+                       file.addn "\t{name}"
+               else
+                       file.addn "\tvar subject = new {test_method.mclassdef.name}.nitunit"
+                       if before_test != null then file.addn "\tsubject.{before_test.name}"
+                       file.addn "\tsubject.{name}"
+                       if after_test != null then file.addn "\tsubject.{after_test.name}"
+               end
+               file.addn "end"
+       end
+
        # Execute the test case.
        fun run do
                toolcontext.info("Execute test-case {test_method.name}", 1)
                was_exec = true
                if toolcontext.opt_noact.value then return
                # execute
-               var file = test_file
-               var res = sys.system("{file.to_program_name}.bin > '{file}.out1' 2>&1 </dev/null")
-               var f = new IFStream.open("{file}.out1")
+               var method_name = test_method.name
+               var test_file = test_suite.test_file
+               var res_name = "{test_file}_{method_name.escape_to_c}"
+               var res = sys.system("{test_file}.bin {method_name} > '{res_name}.out1' 2>&1 </dev/null")
+               var f = new IFStream.open("{res_name}.out1")
                var msg = f.read_all
                f.close
                # set test case result
                var loc = test_method.location
                if res != 0 then
                        error = msg
-                       toolcontext.warning(loc, "failure", "ERROR: {test_method.name} (in file {file}.nit): {msg}")
+                       toolcontext.warning(loc, "failure",
+                          "ERROR: {method_name} (in file {test_file}.nit): {msg}")
                        toolcontext.modelbuilder.failed_tests += 1
                end
                toolcontext.check_errors
        end
 
-       # Error occured during execution.
+       # Error occured during test-case execution.
        var error: nullable String = null
 
-       # Error occured during compilation.
-       var failure: nullable String = null
-
        # Was the test case executed at least one?
        var was_exec = false
 
@@ -284,11 +294,6 @@ class TestCase
                                n.attr("message", error.to_s)
                                tc.add n
                        end
-                       if failure != null then
-                               n = new HTMLTag("failure")
-                               n.attr("message", failure.to_s)
-                               tc.add n
-                       end
                end
                return tc
        end
@@ -366,8 +371,13 @@ redef class MModule
 end
 
 redef class ModelBuilder
+       # Number of test classes generated.
        var total_classes = 0
+
+       # Number of tests generated.
        var total_tests = 0
+
+       # Number of failed tests.
        var failed_tests = 0
 
        # Run NitUnit test file for mmodule (if exists).
index b9a37c4..53a99fa 100644 (file)
@@ -54,10 +54,21 @@ class VirtualMachine super NaiveInterpreter
                super
        end
 
-       # Subtyping test for the virtual machine
+       # Runtime subtyping test
        redef fun is_subtype(sub, sup: MType): Bool
        do
+               if sub == sup then return true
+
                var anchor = self.frame.arguments.first.mtype.as(MClassType)
+
+               # `sub` or `sup` are formal or virtual types, resolve them to concrete types
+               if sub isa MParameterType or sub isa MVirtualType then
+                       sub = sub.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false)
+               end
+               if sup isa MParameterType or sup isa MVirtualType then
+                       sup = sup.resolve_for(anchor.mclass.mclass_type, anchor, mainmodule, false)
+               end
+
                var sup_accept_null = false
                if sup isa MNullableType then
                        sup_accept_null = true
@@ -77,11 +88,6 @@ class VirtualMachine super NaiveInterpreter
                end
                # Now the case of direct null and nullable is over
 
-               # An unfixed formal type can only accept itself
-               if sup isa MParameterType or sup isa MVirtualType then
-                       return sub == sup
-               end
-
                if sub isa MParameterType or sub isa MVirtualType then
                        sub = sub.anchor_to(mainmodule, anchor)
                        # Manage the second layer of null/nullable
@@ -100,31 +106,29 @@ class VirtualMachine super NaiveInterpreter
 
                assert sup isa MClassType
 
-               # Create the sup vtable if not create
+               # `sub` and `sup` can be discovered inside a Generic type during the subtyping test
                if not sup.mclass.loaded then create_class(sup.mclass)
-
-               # Sub can be discovered inside a Generic type during the subtyping test
                if not sub.mclass.loaded then create_class(sub.mclass)
 
-               if sup isa MGenericType then
-                       var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass)
-                       assert sub2.mclass == sup.mclass
-
-                       for i in [0..sup.mclass.arity[ do
-                               var sub_arg = sub2.arguments[i]
-                               var sup_arg = sup.arguments[i]
-                               var res = is_subtype(sub_arg, sup_arg)
-
-                               if res == false then return false
-                       end
-                       return true
-               end
-
+               # For now, always use perfect hashing for subtyping test
                var super_id = sup.mclass.vtable.id
                var mask = sub.mclass.vtable.mask
 
-               # For now, we always use perfect hashing for subtyping test
-               return inter_is_subtype_ph(super_id, mask, sub.mclass.vtable.internal_vtable)
+               var res = inter_is_subtype_ph(super_id, mask, sub.mclass.vtable.internal_vtable)
+               if res == false then return false
+               # sub and sup can be generic types, each argument of generics has to be tested
+
+               if not sup isa MGenericType then return true
+               var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass)
+
+               # Test each argument of a generic by recursive calls
+               for i in [0..sup.mclass.arity[ do
+                       var sub_arg = sub2.arguments[i]
+                       var sup_arg = sup.arguments[i]
+                       var res2 = is_subtype(sub_arg, sup_arg)
+                       if res2 == false then return false
+               end
+               return true
        end
 
        # Subtyping test with perfect hashing
@@ -555,7 +559,9 @@ redef class MClass
        private fun superclasses_ordering(v: VirtualMachine): Array[MClass]
        do
                var superclasses = new Array[MClass]
-               superclasses.add_all(ancestors)
+
+               # Add all superclasses of `self`
+               superclasses.add_all(self.in_hierarchy(v.mainmodule).greaters)
 
                var res = new Array[MClass]
                if superclasses.length > 1 then
diff --git a/tests/Linux.skip b/tests/Linux.skip
new file mode 100644 (file)
index 0000000..609105e
--- /dev/null
@@ -0,0 +1,3 @@
+cocoa_extern_types
+cocoa_message_box
+hello_cocoa
index c1b4cff..578880a 100644 (file)
@@ -170,12 +170,18 @@ It is a success.
 The `$engine.skip` files (where `$engine` is an engine name, see below) describe tests that are skipped completely on a given engine.
 Usually it used with then engine `niti` because tests are too long.
 
-The `cc.skip` file describes tests that are analysed but no executable is generated.
-Usually it is because expected CC errors or missing C libraries.
+The `cc.skip` file describes tests that are analyzed but no executable is generated.
+Usually it is because of expected CC errors or missing C libraries.
 
 The `exec.skip` file describes tests that compiled but not executed.
 Usually it is because the programs are interactive or run some kind of server.
 
+The `$os.skip` file describes tests that are to be skipped completely on the given OS.
+Usually it is because of OS specific libraries.
+
+The `turing.skip` file describes tests that are to be skipped completely on the Turing cluster doing continuous testing over MPI.
+Usually it is because of an unavailable library or a large work which would not benefit from parallelization.
+
 These `*.skip` files contain a list of patterns that will be used against test names.
 A single substring can thus be used to skip a full family of tests.
 
diff --git a/tests/base_empty_module2.nit b/tests/base_empty_module2.nit
new file mode 100644 (file)
index 0000000..dc498f0
--- /dev/null
@@ -0,0 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2004-2008 Jean Privat <jean@pryen.org>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import end
index 9f22200..8725c3e 100644 (file)
@@ -5,3 +5,7 @@ neo_doxygen_dump
 neo_doxygen_file_compound
 neo_doxygen_graph_empty_project
 neo_doxygen_member_resolve_introducer
+test_docdown
+nith
+nit_pretty
+nitmetrics
index 9da5a56..cb551a2 100644 (file)
@@ -18,3 +18,5 @@ emscripten
 nitserial_args
 nitunit_args
 nitpretty_args
+hamming_number
+hailstone
index a64bbf0..b0818df 100644 (file)
@@ -1,5 +1,5 @@
-base_as_notnull2.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-base_as_notnull2.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+base_as_notnull2.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+base_as_notnull2.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 1
 1
 2
index a28e2e1..01ee624 100644 (file)
@@ -1,3 +1,3 @@
-alt/base_as_notnull2_alt1.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-alt/base_as_notnull2_alt1.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+alt/base_as_notnull2_alt1.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+alt/base_as_notnull2_alt1.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 alt/base_as_notnull2_alt1.nit:58,7--10: Type error: expected Object, got null
index ccaa594..e6335c5 100644 (file)
@@ -1,5 +1,5 @@
-alt/base_as_notnull2_alt2.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-alt/base_as_notnull2_alt2.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+alt/base_as_notnull2_alt2.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+alt/base_as_notnull2_alt2.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 Runtime error: Cast failed (alt/base_as_notnull2_alt2.nit:40)
 1
 1
index 7ce3a5b..4fb6e73 100644 (file)
@@ -1,3 +1,3 @@
-alt/base_as_notnull2_alt3.nit:30,12--25: Warning: expression is already not null, since it is a `E: Object`.
-alt/base_as_notnull2_alt3.nit:50,12--25: Warning: expression is already not null, since it is a `E: Object`.
+alt/base_as_notnull2_alt3.nit:30,12--25: Warning: expression is already not null, since it is a `Object`.
+alt/base_as_notnull2_alt3.nit:50,12--25: Warning: expression is already not null, since it is a `F: Object`.
 alt/base_as_notnull2_alt3.nit:64,7--10: Type error: expected Int, got null
index 4b49330..9ac131b 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `B` (alt/base_gen_variance2_alt1.nit:27)
+Runtime error: Cast failed. Expected `F`, got `B` (alt/base_gen_variance2_alt1.nit:27)
 3
index 10f4ba7..27315ba 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `D` (alt/base_gen_variance2_alt2.nit:27)
+Runtime error: Cast failed. Expected `F`, got `D` (alt/base_gen_variance2_alt2.nit:27)
 3
index f450e1f..c6eef59 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `D` (alt/base_gen_variance3_alt1.nit:27)
+Runtime error: Cast failed. Expected `B`, got `D` (alt/base_gen_variance3_alt1.nit:27)
 2
index f912514..3c8702a 100644 (file)
@@ -1,2 +1,2 @@
-Runtime error: Cast failed. Expected `E`, got `Char` (alt/base_gen_variance_int_alt1.nit:27)
+Runtime error: Cast failed. Expected `Int`, got `Char` (alt/base_gen_variance_int_alt1.nit:27)
 2
diff --git a/tests/sav/cocoa_extern_types.res b/tests/sav/cocoa_extern_types.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
diff --git a/tests/sav/cocoa_message_box.res b/tests/sav/cocoa_message_box.res
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/sav/hailstone.res b/tests/sav/hailstone.res
new file mode 100644 (file)
index 0000000..0fb3d98
--- /dev/null
@@ -0,0 +1,2 @@
+Sequence for 27 has 112 begin with: 27, 82, 41, 124 and end with: 8, 4, 2, 1
+The number with longest sequence is 77031 with length of 351
diff --git a/tests/sav/hamming_number.res b/tests/sav/hamming_number.res
new file mode 100644 (file)
index 0000000..72c05b7
--- /dev/null
@@ -0,0 +1,21 @@
+1: 1
+2: 2
+3: 3
+4: 4
+5: 5
+6: 6
+7: 8
+8: 9
+9: 10
+10: 12
+11: 15
+12: 16
+13: 18
+14: 20
+15: 24
+16: 25
+17: 27
+18: 30
+19: 32
+20: 36
+1691: 2125764000
diff --git a/tests/sav/hello_cocoa.res b/tests/sav/hello_cocoa.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
index e95605c..62be912 100644 (file)
@@ -8,10 +8,10 @@ import serialization
 redef class Deserializer
        redef fun deserialize_class(name)
        do
+               if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
                if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
                if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
                if name == "Array[String]" then return new Array[String].from_deserializer(self)
-               if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
                return super
        end
 end
index dc528b1..b00e342 100644 (file)
@@ -2,7 +2,7 @@ test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit.test_nitunit::X.<class>
 
 test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit-3.nit): .nitunit/test_nitunit-3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
 
-test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/test_test_nitunit_TestX_test_foo1.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
+test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/gen_test_test_nitunit.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
 
 DocUnits:
 Entities: 27; Documented ones: 3; With nitunits: 3; Failures: 2
index 87254bb..9ce343f 100644 (file)
@@ -1 +1 @@
-Runtime error: Assert failed (../lib/c.nit:44)
+Runtime error: Assert failed (../lib/c.nit:45)
index 2529d3a..5f9676c 100644 (file)
@@ -1,4 +1,4 @@
 A hello world!
-C hello world!
 B hello world!
+C hello world!
 D hello world!
index 514b4d6..6803f77 100644 (file)
@@ -4,9 +4,9 @@ c
 d
 e
 f
-e
-f
 remove: a
 remove: b
 remove: c
 remove: d
+e
+f
index e11e7e7..fb6d6df 100644 (file)
@@ -1,4 +1,4 @@
 Created in Nit
-Also created in Nit
 Created in Java
+Also created in Nit
 Also created in Java
diff --git a/tests/sav/test_ffi_objc_types_and_callbacks.res b/tests/sav/test_ffi_objc_types_and_callbacks.res
new file mode 100644 (file)
index 0000000..9eb68aa
--- /dev/null
@@ -0,0 +1,2 @@
+From Objective-C: 2468 12.340000 1234
+From Nit: 2468 12.34 1234
diff --git a/tests/sav/test_file_open_fail.res b/tests/sav/test_file_open_fail.res
new file mode 100644 (file)
index 0000000..613542b
--- /dev/null
@@ -0,0 +1,2 @@
+Error: Opening file at 'donotcreate.bing' failed with 'No such file or directory'
+Error: Opening file at 'donotcreate.bing' failed with 'No such file or directory'
index 2972528..9fb5305 100644 (file)
@@ -1,14 +1,14 @@
+Compilation des classes Java ...
+Initialisation de la JVM ...
+---------------------Test 1----------------------
 From java, pushing premier
 From java, pushing deuxi?me
 From java, pushing troisi?me
 From java, popping premier
-From java, popping deuxi?me
-From java, popping troisi?me
-Compilation des classes Java ...
-Initialisation de la JVM ...
----------------------Test 1----------------------
 premier
+From java, popping deuxi?me
 deuxième
+From java, popping troisi?me
 troisième
 --------------------Test 2---------------------
 true
index 4aa3a13..4ad3dc3 100644 (file)
@@ -1,2 +1 @@
-Runtime error: Assert failed (alt/test_sqlite3_nity_alt1.nit:27)
-unable to open database file
+UNDEFINED
index 8d75750..4ad3dc3 100644 (file)
@@ -1,2 +1 @@
-Runtime error: Assert failed (alt/test_sqlite3_nity_alt2.nit:39)
-SQL logic error or missing database
+UNDEFINED
index c1e3e84..6de686a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import stream
+import file
 
-var fd_in = new FDIStream(0)
-var fd_out = new FDOStream(1)
-var fd_err = new FDOStream(2)
+var fd_in = new IFStream.from_fd(0)
+var fd_out = new OFStream.from_fd(1)
+var fd_err = new OFStream.from_fd(2)
 
 fd_out.write("Hello\n")
 
 var s = fd_in.read_line
 fd_out.write(s)
+fd_out.write("\n")
 
 fd_err.write("World\n")
diff --git a/tests/test_ffi_objc_types_and_callbacks.nit b/tests/test_ffi_objc_types_and_callbacks.nit
new file mode 100644 (file)
index 0000000..9b0cd1c
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_ffi_objc_types_and_callbacks
+
+fun foo(i: Int, f: Float, s: String)
+import bar, String.to_cstring, Int.+ in "ObjC" `{
+       char *cstr = String_to_cstring(s);
+
+       long ii = Int__plus(i, i);
+
+       printf("From Objective-C: %ld %f %s\n", ii, f, cstr);
+
+       Object_bar(recv, ii, f, s);
+`}
+
+fun bar(i: Int, f: Float, s: String)
+do
+       print "From Nit: {i} {f} {s}"
+end
+
+foo(1234, 12.34, "1234")
diff --git a/tests/test_file_open_fail.nit b/tests/test_file_open_fail.nit
new file mode 100644 (file)
index 0000000..f71ed82
--- /dev/null
@@ -0,0 +1,29 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var ifs = new IFStream.open("donotcreate.bing")
+
+var s = ifs.read_all
+
+ifs.close
+
+if ifs.last_error != null then print ifs.last_error.as(not null)
+
+ifs.reopen
+
+s = ifs.read_all
+
+ifs.close
+
+if ifs.last_error != null then print ifs.last_error.as(not null)
index a893976..7981d06 100644 (file)
@@ -25,7 +25,7 @@ p1 = new IProcess( "sleep", "0.2" )
 p2 = new IProcess( "sleep", "0.1" )
 p3 = new IProcess( "sleep", "0.4" )
 
-var order = new Array[FDStream]
+var order = new Array[FStream]
 var streams = [p1.stream_in, p2.stream_in, p3.stream_in]
 
 while not streams.is_empty do
@@ -33,7 +33,7 @@ while not streams.is_empty do
        if s == null then continue # may have been interrupted
 
        order.add( s )
-       streams.remove( s.as(FDIStream ) )
+       streams.remove( s.as(IFStream ) )
 end
 
 print order[0] == p2.stream_in
index ca15577..88487d4 100755 (executable)
@@ -29,9 +29,9 @@ unset NIT_DIR
 shopt -s nullglob
 JAVA_HOME=$(dirname $(dirname $(readlink -f $(which javac))))
 
-paths=`echo $JAVA_HOME/jre/lib/*/{client,server}/`
-paths=($paths) 
-JNI_LIB_PATH=${paths[0]}
+paths=`echo $JAVA_HOME/jre/lib/*/{client,server}/libjvm.so`
+paths=($paths)
+JNI_LIB_PATH=`dirname ${paths[0]}`
 shopt -u nullglob
 
 outdir="out"
@@ -316,6 +316,14 @@ need_skip()
                echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
                return 0
        fi
+
+       # Skip by OS
+       os_skip_file=`uname`.skip
+       if test -e $os_skip_file && echo "$1" | grep -f "$os_skip_file"; then
+               echo "=> $2: [skip os]"
+               echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
+               return 0
+       fi
        return 1
 }
 
diff --git a/tests/turing.skip b/tests/turing.skip
new file mode 100644 (file)
index 0000000..7cac9bd
--- /dev/null
@@ -0,0 +1,20 @@
+nitg
+nitx
+_linux
+android
+gles
+shoot
+curl
+neo
+gtk
+nitcorn
+ffi_objc
+mpi
+pnacl
+emscipten
+nodejs
+nitunit
+test_annot_c_compiler
+base_very
+test_jvm
+test_glsl_validation