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>
# Query the Github PR API to perform a merge
module github_merge
-import github_api
+import github::github_curl
import template
redef class Object
# 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.
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
# 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
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
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
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
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
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
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
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
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")
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
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
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]
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]
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)
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
# 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)
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
# 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
# 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)
# 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]
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
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
# 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)
# 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]
# 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
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
# 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
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
# 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
# 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
return true
end
- # Recusively extends item outside the core
+ # Recursively extends item outside the core
fun extends(i: Item)
do
var e = i.next
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?
# 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
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]
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
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]
# 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
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('\'')
abort
end
- fun id(c: Char)
+ private fun id(c: Char)
do
var b = new FlatBuffer
b.add c
tokens.add token
end
- fun kw(c: Char)
+ private fun kw(c: Char)
do
var b = new FlatBuffer
b.add c
tokens.add token
end
- fun trim
+ private fun trim
do
while iter.is_ok and iter.item <= ' ' do
iter.next
class CollectNameVisitor
super Visitor
+ # All the productions
var nprods = new Array[Nprod]
# Symbol table to associate things (prods and exprs) with their name
# 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
# 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}"
# 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
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
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
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
# 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
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
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
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
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]
end
redef class Alternative
+ # The short name of the alternative.
+ # Used for errors
var short_name: nullable String
end
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)
# 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`
# 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
# 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`
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")
"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?
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
# 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
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
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/"
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
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
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`
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
# 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
--- /dev/null
+# 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}"
--- /dev/null
+# 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)}"
loop
var frontier_node: nullable N = null
- var bucket_searched: Int = 0
+ var bucket_searched = 0
# find next valid node in frontier/buckets
loop
# 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
# 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
# Something acting on the game
class Turnable[G: Game]
+
+ # Execute `turn` for this instance.
fun do_turn(turn: GameTurn[G]) is abstract
end
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
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)
# Game related event
interface GameEvent
+
+ # Apply `self` to `game` logic.
fun apply( game : ThinGame ) is abstract
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 )
# 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
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)
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
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
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)
redef fun +(o) do
var s = o.to_s
- var mlen = length
var slen = s.length
if s isa FlatString then
var r = right
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
# 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
return native_array[index]
end
+ # Set `val` at `index`.
fun []=(index: Int, val: E)
do
assert not destroyed
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
# 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
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
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; `}
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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
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
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
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
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
class TermMoveUp
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
class TermMoveDown
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
class TermMoveFoward
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
class TermMoveBackward
super TermDirectionalMove
- init do end
-
# Move by the specified number of cells.
init by(magnitude: Int) do self.magnitude = magnitude
# 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.
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
+++ /dev/null
-# 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
--- /dev/null
+# 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")
--- /dev/null
+# 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
# 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;
# 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
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
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
--- /dev/null
+# 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
# 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
super Curl
# Headers to use on all requests
- var header: HeaderMap
+ var header: HeaderMap is noinit
# OAuth token
var auth: String
# 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
p.close
return token.trim
end
-
--- /dev/null
+# 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
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.
# 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
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
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
end
end
+ # Instanciate a new `Array` from its serialized representation.
init from_deserializer(v: Deserializer)
do
if v isa JsonDeserializer then
# 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
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
end
end
+ # Load playlist named `name`.
fun load_playlist(name: String)
do
write_and_check "load \"{name}\""
# 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
# Deserialize message
var deserializer = new JsonDeserializer(buffer)
var deserialized = deserializer.deserialize
-
+
if deserialized == null then print "|{buffer}|{buffer.chars.join("-")}| {buffer.length}"
return deserialized
# 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
# 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
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
+ #include <poll.h>
#define MAX_DICTIONARY_QUEUE_SIZE 200
#define MAX_MESSAGE_QUEUE_SIZE 10
}
/* 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.
# 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
fun show_dot
do
var f = new OProcess("dot", "-Txlib")
- f.write "\}\n"
write_dot(f)
f.close
f.wait
# Standard input and output can be handled through streams.
module exec
-import stream
+import file
# Simple sub-process
class Process
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
redef fun execute
do
super
- stream_in = new FDIStream(data.out_fd)
+ stream_in = new IFStream.from_fd(data.out_fd)
end
end
redef fun execute
do
super
- stream_out = new FDOStream(data.in_fd)
+ stream_out = new OFStream.from_fd(data.in_fd)
end
end
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;
}
}
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
+ #include <poll.h>
+ #include <errno.h>
`}
# File Abstract Stream
# 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
# 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
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
_buffer.length = nb
_buffer_pos = 0
end
-
+
# End of file?
redef var end_reached: Bool = false
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
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
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.
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"
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
# 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`).
}
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);
+}
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))
#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))
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
# 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
# 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)
# 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
# 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
end
# IStream capable of declaring if readable without blocking
-interface PollableIStream
+abstract class PollableIStream
super IStream
# Is there something to read? (without blocking)
end
# Abstract output stream
-interface OStream
+abstract class OStream
super IOS
# write a string
fun write(s: Text) is abstract
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
redef fun read(i)
do
+ if last_error != null then return ""
if _buffer.length == _buffer_pos then
if not eof then
fill_buffer
redef fun read_all
do
+ if last_error != null then return ""
var s = new FlatBuffer
while not eof do
var j = _buffer_pos
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.
+++ /dev/null
-/* 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;
-}
+++ /dev/null
-#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
# 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
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/
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
" 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
`--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>
},
_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));
},
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) {
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
# 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
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
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
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
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
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
end
tpl.add ">"
tpl.add content
- tpl.add "</li>"
+ tpl.addn "</li>"
add_raw(tpl)
end
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
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
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
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
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
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
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)
for child in children do
add child
end
- add """</article>"""
+ addn """</article>"""
end
redef fun is_empty: Bool do
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>"
add " "
add location.as(not null)
end
- add "</div>"
+ addn "</div>"
end
private fun render_comment do
end
redef fun rendering do
- add "<div class='definition'>"
+ addn "<div class='definition'>"
render_comment
render_info
- add "</div>"
+ addn "</div>"
end
end
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
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
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
redef fun rendering do
add "<li class='{css_classes.join(" ")}'>"
add content
- add "</li>"
+ addn "</li>"
end
end
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
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
redef fun rendering do
add "<script"
for attr in attrs do add attr
- add ">"
+ addn ">"
render_content
- add "</script>"
+ addn "</script>"
end
end
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
import cpp
import java
import extra_java_files
+import objc
redef class MModule
# Does this module uses the FFI?
--- /dev/null
+# 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
import semantize
private import parser::tables
import mixin
+import primitive_types
redef class ToolContext
# --discover-call-trace
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
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
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)
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
--- /dev/null
+# 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
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
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
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")
line.add("n/a")
end
end
- csv.lines.add(line)
+ csv.records.add(line)
end
return csv
end
# 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]
# 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
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
# 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
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}")
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
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
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
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
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)
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
# `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
--- /dev/null
+# 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
# 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]
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
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
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.
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
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
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
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).
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
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
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
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
--- /dev/null
+cocoa_extern_types
+cocoa_message_box
+hello_cocoa
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.
--- /dev/null
+# 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
neo_doxygen_file_compound
neo_doxygen_graph_empty_project
neo_doxygen_member_resolve_introducer
+test_docdown
+nith
+nit_pretty
+nitmetrics
nitserial_args
nitunit_args
nitpretty_args
+hamming_number
+hailstone
-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
-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
-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
-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
-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
-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
-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
-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
--- /dev/null
+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
--- /dev/null
+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
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
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
-Runtime error: Assert failed (../lib/c.nit:44)
+Runtime error: Assert failed (../lib/c.nit:45)
A hello world!
-C hello world!
B hello world!
+C hello world!
D hello world!
-777
From Nit
+777
11
12346
d
e
f
-e
-f
remove: a
remove: b
remove: c
remove: d
+e
+f
Created in Nit
-Also created in Nit
Created in Java
+Also created in Nit
Also created in Java
-777
-asdf
From Nit
+777
11
12346
+asdf
--- /dev/null
+From Objective-C: 2468 12.340000 1234
+From Nit: 2468 12.34 1234
--- /dev/null
+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'
+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
-Runtime error: Assert failed (alt/test_sqlite3_nity_alt1.nit:27)
-unable to open database file
+UNDEFINED
-Runtime error: Assert failed (alt/test_sqlite3_nity_alt2.nit:39)
-SQL logic error or missing database
+UNDEFINED
# 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")
--- /dev/null
+# 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")
--- /dev/null
+# 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)
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
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
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"
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
}
--- /dev/null
+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