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