From: Jean Privat Date: Wed, 22 Oct 2014 22:45:27 +0000 (-0400) Subject: Merge: Some gammar improvements X-Git-Tag: v0.6.10~19 X-Git-Url: http://nitlanguage.org?hp=98808ec99b1a37d6b4e635dc9092235138617c27 Merge: Some gammar improvements 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 Reviewed-by: Lucas Bajolet --- diff --git a/NOTICE b/NOTICE index 14107e5..d0fe8c4 100644 --- a/NOTICE +++ b/NOTICE @@ -2,24 +2,51 @@ This product includes software developed as part of the Nit Language project ( http://nitlanguage.org ). Files: * -Copyright: 2004-2012 Jean Privat +Copyright: 2004-2014 Jean Privat 2006-2008 Floréal Morandat + 2009 Julien Chevalier 2009-2011 Jean-Sebastien Gelinas - 2009-2012 Alexis Laferrière - 2009 Julien Chevalier - 2011-2012 Alexandre Terrasa + 2009-2014 Alexis Laferrière + 2011 Matthieu Auger + 2011-2014 Alexandre Terrasa + 2012 Alexandre Pennetier + 2013-2014 Lucas Bajolet + 2013 Stefan Lage + 2013 Nathan Heu + 2013 Matthieu Lucas + 2014 Romain Chanoir + 2014 Frédéric Vachon + 2014 Johan Kayser + 2014 Julien Pagès + 2014 Geoffrey Hecht + 2014 Jean-Christophe Beaupré 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 +Copyright: 2004-2014 Jean Privat 2006-2008 Floréal Morandat 2009-2011 Jean-Sebastien Gelinas - 2009-2012 Alexis Laferrière - 2009 Julien Chevalier - 2011-2012 Alexandre Terrasa + 2009-2014 Alexis Laferrière + 2009 Julien Chevalier + 2011-2014 Alexandre Terrasa + 2012 Alexandre Pennetier + 2013-2014 Lucas Bajolet + 2013 Nathan Heu + 2013 Matthieu Lucas + 2013 Stefan Lage + 2014 Romain Chanoir + 2014 Frédéric Vachon + 2014 Johan Kayser + 2014 Geoffrey Hecht + 2014 Julien Pagès + 2014 Christophe Gigax + 2014 Clement de Figueiredo + 2014 Maxime Leroy + 2014 Johann Dubois + 2014 Jean-Christophe Beaupré Licence: BSD 2 (see LICENSE-BSD) Comment: Use of libraries and resources is basically unrestricted. We hold the copyright on the compiler and the tools but not on the programs made by the users. @@ -29,11 +56,12 @@ Copyright: 2011 John Resig, http://jquery.com/ Licence: BSD 2 (see LICENSE-BSD) Files: /misc/gtksourceview/nit.lang -Copyright: 2009-2012 Alexis Laferrière +Copyright: 2009-2011 Alexis Laferrière + 2011 Jean Privat Based on ruby.lang from - 2004 Archit Baweja - 2005 Michael Witrant - 2006 Gabriel Bauman + 2004 Archit Baweja + 2005 Michael Witrant + 2006 Gabriel Bauman License: GPL 2.0 (see LICENSE-GPL-2) Comment: GPL because the original work is GPL diff --git a/contrib/brainfuck/README.md b/contrib/brainfuck/README.md new file mode 100644 index 0000000..136b674 --- /dev/null +++ b/contrib/brainfuck/README.md @@ -0,0 +1,34 @@ +# Brainfuck + +Brainfuck is as its name implies a simple Brainfuck interpreter written in Nit. + +It has almost as much purposes as the language itself, except it provides a good example for Nit programs that work while being concise. + +[Specification](http://www.muppetlabs.com/~breadbox/bf/) + +The language is designed to need only a few things : + +* One instruction pointer to the current instruction +* One array of Bytes for all manipulations of data +* One data pointer to select where to write/read data + +Brainfuck a small instruction set, only eight instructions : + +* `>`: Increments the data pointer +* `<`: Decrements the data pointer +* `+`: Increments the byte in the current cell +* `-`: Decrements the byte in the current cell +* `[`: If the current cell's value is 0, jumps to the matching `]` +* `]`: If the current cell's value is non-zero, jumps to the matching `[` +* `.`: Writes the current cell's value to stdout +* `,`: Reads a char from stdin and stores it in the current cell + +## How to use + +First, compile the interpreter with the Nit compiler/interpreter, and launch the program on a brainfuck source file for interpretation. + +Example: +~~~ +nitg ./brainfuck.nit +./brainfuck ./examples/hello.bf +~~~ diff --git a/contrib/brainfuck/brainfuck.nit b/contrib/brainfuck/brainfuck.nit new file mode 100644 index 0000000..b614776 --- /dev/null +++ b/contrib/brainfuck/brainfuck.nit @@ -0,0 +1,134 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Simple brainfuck interpreter +module brainfuck + +# Interpreter for Brainfuck source code. +class BFInterpret + # Data cells + var dr = new Array[Char] + # Data pointer + var dp = 0 + # Instruction pointer + var ip = 0 + + # The program being interpreted + var program: String + + # Contains the set of valid instructions, used in next + var valid_instr: Set[Char] + + # Starts interpretation of file `filename` + init(filename: String) do + var ifs = new IFStream.open(filename.simplify_path) + valid_instr = new HashSet[Char] + valid_instr.add_all "><[].,+-".chars + dr.add 0.ascii + program = ifs.read_all + start + end + + # Starts the interpretation of the loaded program + fun start do + loop + if ip >= program.length then break + eval + next + end + end + + # Go to the next executable instruction + fun next do + ip += 1 + while ip < program.length and not valid_instr.has(program[ip]) do + ip += 1 + end + end + + # Evaluates the current instruction + fun eval do + var instr = program[ip] + if instr == '.' then printn dr[dp] + if instr == '[' then + if dr[dp] == 0.ascii then + ip = find_matching_rbra + return + end + end + if instr == ']' then + if dr[dp] != 0.ascii then + ip = find_matching_lbra + return + end + end + if instr == '>' then + dp += 1 + if dp >= dr.length then dr.add(0.ascii) + end + if instr == '<' then + dp -= 1 + if dp < 0 then abort + end + if instr == '+' then + dr[dp] = (dr[dp].ascii + 1).ascii + end + if instr == '-' then + dr[dp] = (dr[dp].ascii - 1).ascii + end + if instr == ',' then + dr[dp] = getc + end + end + + # Seeks for the position of the matching `]` for the `[` located at `ip` + fun find_matching_rbra: Int do + var pos = ip + 1 + var lbracnt = 0 + loop + if pos > program.length then abort + if program[pos] == ']' then + if lbracnt > 0 then + lbracnt -= 1 + else + break + end + end + if program[pos] == '[' then lbracnt += 1 + pos += 1 + end + return pos + end + + # Seeks for the position of the matching `[` for the `]` located at `ip` + fun find_matching_lbra: Int do + var pos = ip - 1 + var rbracnt = 0 + loop + if pos < 0 then abort + if program[pos] == '[' then + if rbracnt > 0 then + rbracnt -= 1 + else + break + end + end + if program[pos] == ']' then rbracnt += 1 + pos -= 1 + end + return pos + end +end + +var i = new BFInterpret(args[0]) diff --git a/contrib/brainfuck/examples/hello.bf b/contrib/brainfuck/examples/hello.bf new file mode 100644 index 0000000..8fa0f72 --- /dev/null +++ b/contrib/brainfuck/examples/hello.bf @@ -0,0 +1 @@ +++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++. diff --git a/contrib/brainfuck/examples/hello2.bf b/contrib/brainfuck/examples/hello2.bf new file mode 100644 index 0000000..8fc90f6 --- /dev/null +++ b/contrib/brainfuck/examples/hello2.bf @@ -0,0 +1,38 @@ +[ This program prints "Hello World!" and a newline to the screen, its + length is 106 active command characters [it is not the shortest.] + This loop is a "comment loop", it's a simple way of adding a comment + to a BF program such that you don't have to worry about any command + characters. Any ".", ",", "+", "-", "<" and ">" characters are simply + ignored, the "[" and "]" characters just have to be balanced. +] ++++++ +++ Set Cell #0 to 8 +[ + >++++ Add 4 to Cell #1; this will always set Cell #1 to 4 + [ as the cell will be cleared by the loop + >++ Add 2 to Cell #2 + >+++ Add 3 to Cell #3 + >+++ Add 3 to Cell #4 + >+ Add 1 to Cell #5 + <<<<- Decrement the loop counter in Cell #1 + ] Loop till Cell #1 is zero; number of iterations is 4 + >+ Add 1 to Cell #2 + >+ Add 1 to Cell #3 + >- Subtract 1 from Cell #4 + >>+ Add 1 to Cell #6 + [<] Move back to the first zero cell you find; this will + be Cell #1 which was cleared by the previous loop + <- Decrement the loop Counter in Cell #0 +] Loop till Cell #0 is zero; number of iterations is 8 +The result of this is: +Cell No : 0 1 2 3 4 5 6 +Contents: 0 0 72 104 88 32 8 +Pointer : ^ +>>. Cell #2 has value 72 which is 'H' +>---. Subtract 3 from Cell #3 to get 101 which is 'e' ++++++++..+++. Likewise for 'llo' from Cell #3 +>>. Cell #5 is 32 for the space +<-. Subtract 1 from Cell #4 for 87 to give a 'W' +<. Cell #3 was set to 'o' from the end of 'Hello' ++++.------.--------. Cell #3 for 'rl' and 'd' +>>+. Add 1 to Cell #5 gives us an exclamation point +>++. And finally a newline from Cell #6 diff --git a/contrib/brainfuck/examples/rot13.bf b/contrib/brainfuck/examples/rot13.bf new file mode 100644 index 0000000..85a97fa --- /dev/null +++ b/contrib/brainfuck/examples/rot13.bf @@ -0,0 +1,28 @@ +-,+[ Read first character and start outer character reading loop + -[ Skip forward if character is 0 + >>++++[>++++++++<-] Set up divisor (32) for division loop + (MEMORY LAYOUT: dividend copy remainder divisor quotient zero zero) + <+<-[ Set up dividend (x minus 1) and enter division loop + >+>+>-[>>>] Increase copy and remainder / reduce divisor / Normal case: skip forward + <[[>+<-]>>+>] Special case: move remainder back to divisor and increase quotient + <<<<<- Decrement dividend + ] End division loop + ]>>>[-]+ End skip loop; zero former divisor and reuse space for a flag + >--[-[<->+++[-]]]<[ Zero that flag unless quotient was 2 or 3; zero quotient; check flag + ++++++++++++<[ If flag then set up divisor (13) for second division loop + (MEMORY LAYOUT: zero copy dividend divisor remainder quotient zero zero) + >-[>+>>] Reduce divisor; Normal case: increase remainder + >[+[<+>-]>+>>] Special case: increase remainder / move it back to divisor / increase quotient + <<<<<- Decrease dividend + ] End division loop + >>[<+>-] Add remainder back to divisor to get a useful 13 + >[ Skip forward if quotient was 0 + -[ Decrement quotient and skip forward if quotient was 1 + -<<[-]>> Zero quotient and divisor if quotient was 2 + ]<<[<<->>-]>> Zero divisor and subtract 13 from copy if quotient was 1 + ]<<[<<+>>-] Zero divisor and add 13 to copy if quotient was 0 + ] End outer skip loop (jump to here if ((character minus 1)/32) was not 2 or 3) + <[-] Clear remainder from first division if second division was skipped + <.[-] Output ROT13ed character from copy and clear it + <-,+ Read next character +] End character reading loop diff --git a/contrib/nitcc/src/autom.nit b/contrib/nitcc/src/autom.nit index 86f25b3..8825b01 100644 --- a/contrib/nitcc/src/autom.nit +++ b/contrib/nitcc/src/autom.nit @@ -536,7 +536,7 @@ class Automaton # From the important values, build a sequence of TSymbols var a = alphabet.to_a - (new ComparableSorter[Int]).sort(a) + default_comparator.sort(a) var tsyms = new Array[TSymbol] var last = 0 for i in a do diff --git a/contrib/nitester/Makefile b/contrib/nitester/Makefile new file mode 100644 index 0000000..ffe3530 --- /dev/null +++ b/contrib/nitester/Makefile @@ -0,0 +1,3 @@ +all: + mkdir -p bin/ + ../../bin/nitg src/nitester.nit -o bin/nitester diff --git a/contrib/nitester/src/nitester.nit b/contrib/nitester/src/nitester.nit new file mode 100644 index 0000000..5edfe22 --- /dev/null +++ b/contrib/nitester/src/nitester.nit @@ -0,0 +1,657 @@ +# This file is part of NIT (http://www.nitlanguage.org). +# +# Copyright 2014 Alexis Laferrière +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tester of Nit engines on an MPI cluster +module nitester + +import mpi +import signals +import opts + +# Any processor, worker or controller +# +# All data and methods within this class are shared by the controller and the +# workers. +abstract class Processor + super SignalHandler + + # Controller rank is always 0 + var controller_rank: Rank = 0.rank + + # Where to store data for transfer between nodes + # + # Require: `buffer.length % 4 == 0` + var buffer = new CIntArray(1024) + + # Run in verbose mode, display more text + var verbose = 0 + + init + do + # OpenMPI sends a SIGTERM to all nodes upon receiving a SIGTERM or SIGINT + # on the first process. + handle_signal(sigterm, true) + end + + # Tag of a new task packet of size `tasks_per_packet` + var task_tag: Tag = 0.tag + + # Tag to return a set of `Result` throught `buffer` + var result_tag: Tag = 1.tag + + # Tag to notify `Worker` when to quit + var quit_tag: Tag = 2.tag + + # Tag to request more work from the `Controller` by a `Worker` + var need_work_tag: Tag = 4.tag + + # Tag to notify `Controller` that the sender `Worker` is done + var done_tag: Tag = 5.tag + + # Number of tasks within each task assignation with `task_tag` + var tasks_per_packet = 4 + + # Run the main logic of this node + fun run is abstract + + # Engines targetted by this execution + var engines: Array[String] is noinit + + # All known engines, used to detect errors in `engines` + var all_engines: Array[String] = ["nitg-s", "nitg-sg", "nitg-g", "nitg-e", "niti", "emscripten"] + + # Programs to test in this execution + var test_programs: Array[String] is noinit + + # Root of the temporary directory + var tmp_dir = "/dev/shm/" + + # `ccache` directory + var ccache_dir = "/dev/shm/nit_ccache" + + # Read command line options + fun read_cli_options + do + var opt_ctx = new OptionContext + var opt_engines = new OptionString( + "Engines to test, separated with commas ({all_engines.join(", ")} or all)", + "--engine", "-e") + var opt_help = new OptionBool("Print this help message", "--help", "-h") + var opt_verbose = new OptionCount( + "Be verbose, repeat to increase verbose level (max with -vvv)", + "--verbose", "-v") + var opt_cleanup = new OptionBool( + "Clean up all nitester files (and do not run tests)", + "--cleanup", "-C") + + opt_ctx.add_option(opt_engines, opt_help, opt_verbose, opt_cleanup) + opt_ctx.parse args + + # --help? + if opt_help.value then opt_ctx.usage_error null + + # --verbose? + verbose = opt_verbose.value + + # --cleanup? + if opt_cleanup.value then + assert tmp_dir.file_exists + for file in tmp_dir.files do if file.has_prefix("nit") then + var full_path = tmp_dir / file + if full_path == ccache_dir then continue + + assert full_path.file_exists + + var stat = full_path.file_lstat + if stat.is_dir then + full_path.rmdir + else + full_path.file_delete + end + stat.free + end + mpi.finalize + exit 0 + end + + # any files? + var rest = opt_ctx.rest + if rest.is_empty then opt_ctx.usage_error "This tool needs at least one test_program.nit" + test_programs = rest + + # gather and check engines + var engines_str = opt_engines.value + var engines + if engines_str == null then + # default + engines = ["nitg-s"] + else + engines = engines_str.split(',') + + if engines.has("all") then + # all engines + engines = all_engines + end + end + + # check validity of targetted engines + var unknown_engines = new Array[String] + for engine in engines do if not all_engines.has(engine) then unknown_engines.add engine + + if not unknown_engines.is_empty then + opt_ctx.usage_error "Unknown engines: {unknown_engines.join(", ")} (expected one or most of {all_engines.join(", ")})" + end + self.engines = engines + end + + # All tasks to be performed + var tasks = new Array[Task] + + # Gather and registar all tasks + fun create_tasks + do + var c = 0 + for engine in engines do for prog in test_programs do + tasks.add new Task(engine, prog) + c += 1 + end + end +end + +# Single controller to dispatch tasks, gather results and produce stats +class Controller + super Processor + + # Id as `Int` of the next task to distribute + var next_task_id = 0 + + redef fun receive_signal(signal) + do + shutdown + print_results + + mpi.finalize + exit 0 + end + + redef fun run + do + read_cli_options + create_tasks + distribute_tasks + print_results + end + + # Cumulated results from workers + var results = new ResultSet + + # Maintain communication with workers to distribute tasks and receiver results + fun distribute_tasks + do + var at_work = new Array[Rank] + + # send initial tasks + for r in [1..comm_world.size[ do + var sent = send_task_to(r.rank) + if sent then + at_work.add r.rank + else + mpi.send_empty(r.rank, quit_tag, comm_world) + end + end + + var status = new Status + # await results and send new tasks + while not at_work.is_empty do + check_signals + + # Double probe to avoid bug with some implementation of MPI + mpi.probe(new Rank.any, new Tag.any, comm_world, status) + mpi.probe(new Rank.any, new Tag.any, comm_world, status) + + if status.tag == result_tag then + # Receive results fron a worker + var count = status.count(new DataType.int) + mpi.recv_into(buffer, 0, count, status.source, status.tag, comm_world) + + # Parse results from C array to `Result` instances + # + # Each result is on 4 ints: task id, arg, alt and result. + # + # See the comments where the data is produced in `Worker::work_on_tasks` for more informationé + assert count % 4 == 0 + for t in (count/4).times do + var tt = t*4 + + var task_id = buffer[tt] + var arg = buffer[tt+1] + var alt = buffer[tt+2] + var res = buffer[tt+3] + + var result = new Result(tasks[task_id], arg, alt) + + if res == 1 then result.ok = true + if res == 2 then result.ok_empty = true + if res == 3 then result.no_sav = true + if res == 4 then result.fixme = true + if res == 5 then result.fail = true + if res == 6 then result.soso = true + if res == 7 then result.skip = true + if res == 0 then result.unknown = true + + results.add result + + if verbose > 0 and results.length % 25 == 0 then print_short_results + end + + else if status.tag == need_work_tag then + # A worker needs more work + mpi.recv_empty(status.source, status.tag, comm_world) + var sent = send_task_to(status.source) + if not sent then + # no more work, quit + mpi.send_empty(status.source, quit_tag, comm_world) + end + else if status.tag == done_tag then + # A worker is done and will quit + mpi.recv_empty(status.source, status.tag, comm_world) + at_work.remove(status.source) + + if verbose > 1 then print "worker {status.source} is done ({at_work.length} still at work)" + else + print "Unexpected tag {status.tag}" + shutdown + break + end + end + status.free + end + + # Send a packet of tasks to worker at `rank` + fun send_task_to(rank: Rank): Bool + do + if next_task_id >= tasks.length then return false + + buffer[0] = next_task_id + next_task_id += tasks_per_packet + + mpi.send_from(buffer, 0, 1, rank, task_tag, comm_world) + + if verbose > 1 then print "sent tasks [{buffer[0]}..{next_task_id}[ to worker {rank}" + return true + end + + # Display the accumulated results received from workers + fun print_results + do + print "# results #" + print "* {results.length} total" + print "* {results.oks.length + results.ok_empties.length} oks & 0ks" + print "* {results.fails.length} fails" + print "* {results.no_savs.length} no savs" + print "* {results.fixmes.length} fixmes" + print "* {results.sosos.length} sosos" + print "* {results.skips.length} skips" + print "* {results.unknowns.length} unknowns (bug in tests.sh or nitester)" + end + + fun print_short_results do print "oks & fails / total: {results.oks.length + results.ok_empties.length} " + + "& {results.fails.length} / {results.length}" + + # Shutdown anormaly the running tests + fun shutdown + do + print "Shutting down" + mpi.send_empty(new Rank.any, quit_tag, comm_world) + end +end + +# A worker node which actually execute the tests +class Worker + super Processor + + # The `Rank` of `self` + var rank: Rank + + # Compilation directory + var comp_dir = "/dev/shm/nit_compile{rank}" is lazy + + # Output file directory + var out_dir = "/dev/shm/nit_out{rank}" is lazy + + # Output file of the `tests.sh` script + var tests_sh_out = "/dev/shm/nit_local_out{rank}" is lazy + + # Path to the local copy of the Nit repository + var nit_copy_dir = "/dev/shm/nit{rank}/" is lazy + + # Source Nit repository, must be already updated and `make` before execution + var nit_source_dir = "~/nit" + + # Compiled `Regex` to detect the argument of an execution + var re_arg: Regex = "arg [0-9]+".to_re + + # Compiled `Regex` to detect the alternative of an execution + var re_alt: Regex = "_alt[0-9]+".to_re + + redef fun run + do + read_cli_options + setup + create_tasks + work_on_tasks + cleanup + end + + # Setup the testing environment + # + # Clone the nit repository. + fun setup + do + if verbose > 0 then sys.system "hostname" + sys.system "git clone {nit_source_dir} {nit_copy_dir}" + end + + # Clean up the testing environment + # + # Delete all temporary files, except `ccache_dir`. + fun cleanup + do + if comp_dir.file_exists then comp_dir.rmdir + if out_dir.file_exists then out_dir.rmdir + if nit_copy_dir.file_exists then nit_copy_dir.rmdir + if tests_sh_out.file_exists then tests_sh_out.file_delete + end + + # Single C `int` to hold the next task id received from the `Controller` + var task_buffer = new CIntArray(1) + + # Manage communication with the `Controller` and execute dispatched `Task`s + fun work_on_tasks + do + var status = new Status + loop + check_signals + + # We double probe to prevent bug where a single probes does not receive the + # real next read. + mpi.probe(controller_rank, new Tag.any, comm_world, status) + mpi.probe(controller_rank, new Tag.any, comm_world, status) + + if status.tag == task_tag then + # Receive tasks to execute + mpi.recv_into(task_buffer, 0, 1, status.source, status.tag, comm_world) + var first_id = task_buffer[0] + for task_id in [first_id .. first_id + tasks_per_packet] do + + # If id is over all known tasks, stop right here + if task_id >= tasks.length then break + var task = tasks[task_id] + + # Command line to execute test + var cmd = "XMLDIR={out_dir} ERRLIST={out_dir}/errlist TMPDIR={out_dir} " + + "CCACHE_DIR={ccache_dir} CCACHE_TEMPDIR={ccache_dir} CCACHE_BASEDIR={comp_dir} " + + "./tests.sh --compdir {comp_dir} --outdir {out_dir} -o \"--make-flags '-j1'\"" + + " --node --engine {task.engine} {nit_copy_dir / "tests" / task.test_program} > {tests_sh_out}" + + # Execute test + sys.system cmd + + # Test results were written to file, read them + var fstream = new IFStream.open(tests_sh_out) + var content = fstream.read_all + fstream.close + + # Parse result and prepare them for sending + # + # The structure is composed of 4 ints for each result. + # 1. task id + # 2. arg number + # 3. alt number + # 4. test result as int + var c = results_count + for line in content.split('\n') do if not line.is_empty then + var cc = c*4 + + buffer[cc] = task_id + + var arg_match = line.search(re_arg) + var arg = 0 + if arg_match != null then arg = arg_match.to_s.substring_from(4).to_i + buffer[cc+1] = arg + + var alt_match = line.search(re_alt) + var alt = 0 + if alt_match != null then alt = alt_match.to_s.substring_from(4).to_i + buffer[cc+2] = alt + + var res = null + if line.has("[ok]") then res = 1 + if line.has("[0k]") then res = 2 + if line.has("[=== no sav ===]") then res = 3 + if line.has("[fixme]") then res = 4 + if line.has("[======= fail") then res = 5 + if line.has("[======= soso") then res = 6 + if line.has("[skip]") then res = 7 + + if res == null then + res = 0 + if verbose > 1 then print "Unknown result: '{line}'" + end + buffer[cc+3] = res + + c += 1 + + if verbose > 2 then print "tests.sh output line: {line}" + + # If result buffer is full, send to `Controller` + if c*4 == buffer.length then + send_results + c = 0 + end + end + + self.results_count = c + end + + mpi.send_empty(controller_rank, need_work_tag, comm_world) + else if status.tag == quit_tag then + # Notification from the `Controller` to quit + mpi.recv_empty(status.source, status.tag, comm_world) + + # Send remaining results + send_results + + # Notify `Controller` that `self` is done and will quit + mpi.send_empty(controller_rank, done_tag, comm_world) + break + else + print "Unexpected tag {status.tag}" + break + end + end + status.free + end + + # Total results listed in `buffer` and ready to send + var results_count = 0 + + # Send all results in `buffer` to the `Controller` + fun send_results + do + if results_count > 0 then + if verbose > 1 then print "sending {results_count} results" + mpi.send_from(buffer, 0, results_count*4, controller_rank, result_tag, comm_world) + results_count = 0 + end + end + + redef fun receive_signal(signal) + do + cleanup + mpi.finalize + exit 0 + end +end + +# A single test task, on a `test_program` with an `engine` +# +# Note that a task may involve more than one program to test considering the +# alts and args for the `test_program`. +class Task + # Engine to test executing `test_program` + var engine: String + + # Program to execute with `engine` + var test_program: String + + redef fun to_s do return "{engine} {test_program}" +end + +# Result of a `Task` +# +# There may be more than one result per `Task`. +class Result + # `Task` associated to `self` + var task: Task + + # Argument index of the execution resulting in `self` + var arg: Int + + # Alternative index of the execution resulting in `self` + var alt: Int + + # Is `self` result an _ok_? + var ok = false + + # Is `self` result an _0k_? + var ok_empty = false + + # Is `self` result a _no sav_? + var no_sav = false + + # Is `self` result a _fixme_? + var fixme = false + + # Is `self` result a _fail_? + var fail = false + + # Is `self` result a _soso_? + var soso = false + + # Is `self` skipped test? + var skip = false + + # Is `self` an unknown result, probably an error + var unknown = false + + redef fun to_s + do + var err = "Unknown" + if no_sav then err = "no sav" + if ok then err = "ok" + if ok_empty then err = "0k" + if fixme then err = "fixme" + if fail then err = "fail" + + return "{task} arg{arg} alt{alt} => {err}" + end +end + +# A global and sorted collection of `Result` +class ResultSet + super HashSet[Result] + + var no_savs = new HashSet[Result] + var oks = new HashSet[Result] + var ok_empties = new HashSet[Result] + var fixmes = new HashSet[Result] + var fails = new HashSet[Result] + var sosos = new HashSet[Result] + var skips = new HashSet[Result] + var unknowns = new HashSet[Result] + + # TODO remove + var per_engines = new HashMap[String, Result] + + redef fun add(result) + do + if result.no_sav then no_savs.add result + if result.ok then oks.add result + if result.ok_empty then ok_empties.add result + if result.fixme then fixmes.add result + if result.fail then fails.add result + if result.soso then sosos.add result + if result.skip then skips.add result + if result.unknown then unknowns.add result + + super + end + + redef fun remove(r) do abort + + redef fun clear do abort +end + +redef class OptionContext + + # Print usage with a possible error `message` + private fun usage_error(message: nullable String) + do + var ret = 0 + if message != null then + print "Error: {message}" + ret = 1 + end + + if comm_world.rank == 0 then + print "Usage: mpirun nitester [Options] test_program.nit [other_test.nit [...]]" + usage + end + + mpi.finalize + exit ret + end +end + +# On `Worker` nodes, prefix all prints with `rank/comm_world.size` +redef fun print(msg: Object) +do + if comm_world.rank != 0.rank then + super "{comm_world.rank}/{comm_world.size}: {msg}" + else super msg +end + +# Running MPI instance +fun mpi: MPI do return once new MPI + +# Launch mpi +mpi + +# Local rank +var rank = comm_world.rank + +var processor: Processor +if rank == 0.rank then + # If rank == 0, this is the `Controller` + processor = new Controller +else + # This is a worker + processor = new Worker(rank) +end +processor.run + +mpi.finalize diff --git a/contrib/opportunity/Makefile b/contrib/opportunity/Makefile new file mode 100644 index 0000000..bbcf10b --- /dev/null +++ b/contrib/opportunity/Makefile @@ -0,0 +1,3 @@ +all: + mkdir -p bin/ + ../../bin/nitg --dir bin/ src/opportunity_web.nit diff --git a/contrib/opportunity/README.md b/contrib/opportunity/README.md new file mode 100644 index 0000000..32eb786 --- /dev/null +++ b/contrib/opportunity/README.md @@ -0,0 +1,30 @@ +Opportunity is a web-application written in Nit to plan meetups with people in real-life (or on the internet, why not !). + +It runs on Nit's official web server: Nitcorn, available along with the compiler for the language on Github . + +# Compile and execute + +To compile the software, make sure you have all the dependencies for the Nit compiler at hand, make the compiler. + +In addition to those, you will also need libevent-dev and libsqlite3-dev. + +Under Debian or any Debian-based distribution of Linux, you can use apt-get install for those. +If you run an RPM based distribution, yum install should work as well. + +For those who run an OSX-based system, macports or brew might have the packages you need to run Opportunity. + +There is no support for Windows yet. + +Then change directory to Opportunity, and use the command make here as well. + +This will make an executable for opportunity in the bin folder that you can run from a terminal as usual. + +# Features/TODO + +- [x] Creation and visualization of Meetups +- [x] Adding people to meetups +- [x] Change answers +- [ ] Edit an Existing Meetup +- [ ] More security and stability for the software +- [ ] Full support of a REST API for eventual extern clients +- [ ] Maybe support another DB diff --git a/contrib/opportunity/src/opportunity_controller.nit b/contrib/opportunity/src/opportunity_controller.nit new file mode 100644 index 0000000..032b7d3 --- /dev/null +++ b/contrib/opportunity/src/opportunity_controller.nit @@ -0,0 +1,205 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Actions for Opportunity web application +module opportunity_controller + +import nitcorn +import sha1 +import templates +import opportunity_model + +# Any kind of opportunity `Action` (serves a request) +abstract class OpportunityAction + super Action + + # Path to db + var db_path = "opportunity" + + # Returns a bad request with an error code 400 + # + # TODO: Add a specific body to the bad request page. + fun bad_req: HttpResponse do + var rsp = new HttpResponse(400) + rsp.body = (new OpportunityHomePage).write_to_string + return rsp + end +end + +# Welcome page for Opportunity +class OpportunityWelcome + super OpportunityAction + + redef fun answer(request, url) do + print "Received request for {url}" + var get = request.get_args + var rq = url.split("/") + if rq.has("meetup_create") then + var mname = request.string_arg("meetup_name") + var mdate = request.string_arg("meetup_date") + var mplace = request.string_arg("meetup_place") + if mname == null or mdate == null or mplace == null then return bad_req + var db = new OpportunityDB.open(db_path) + var meet = new Meetup(mname, mdate, mplace) + if meet == null then + db.close + return bad_req + end + meet.commit(db) + var ans_tmp = "answer_" + var cnt = 1 + loop + var anss = request.string_arg(ans_tmp + cnt.to_s) + if anss == null then break + var ans = new Answer(anss) + ans.meetup = meet + ans.commit(db) + cnt += 1 + end + db.close + var rsp = new HttpResponse(200) + if meet.id == "" then + rsp.body = (new MeetupCreationPage).write_to_string + else + rsp.body = (new MeetupConfirmation(meet)).write_to_string + end + return rsp + end + if rq.has("new_meetup") then + var rsp = new HttpResponse(200) + var page = new MeetupCreationPage + rsp.body = page.write_to_string + return rsp + end + if get.has_key("meetup_id") then + var rsp = new HttpResponse(200) + rsp.body = (new OpportunityMeetupPage.from_id(get["meetup_id"])).write_to_string + return rsp + end + var rsp = new HttpResponse(200) + rsp.body = (new OpportunityHomePage).write_to_string + return rsp + end + +end + +# Any kind of REST request to Opportunity +class OpportunityRESTAction + super OpportunityAction + + redef fun answer(request, uri) do + print "Received REST request from {uri}" + var get = request.get_args + var req = uri.split("/") + if req.has("people") then + return (new OpportunityPeopleREST).answer(request, uri) + else if req.has("answer") then + return (new OpportunityAnswerREST).answer(request, uri) + else if req.has("meetup") then + return (new OpportunityMeetupREST).answer(request, uri) + else + return new HttpResponse(400) + end + end + +end + +# REST Actions working on People +class OpportunityPeopleREST + super OpportunityAction + + redef fun answer(request, uri) do + # Should be DELETE for true REST API + # TODO : change method to DELETE once supported by Nitcorn + if request.method == "POST" then + var meth = request.string_arg("method") + if meth == null then return bad_req + if meth != "DELETE" then return bad_req + var pid = request.int_arg("p_id") + if pid == null then return bad_req + var db = new OpportunityDB.open(db_path) + db.remove_people_by_id(pid) + db.close + return new HttpResponse(200) + end + return new HttpResponse(400) + end + +end + +# REST Actions working on Answers +class OpportunityAnswerREST + super OpportunityAction + + redef fun answer(request, uri) do + var persid = request.int_arg("pers_id") + var ansid = request.int_arg("answer_id") + var ans = request.bool_arg("answer") + if persid == null or ansid == null or ans == null then return bad_req + var db = new OpportunityDB.open(db_path) + db.change_answer(ansid, persid, ans) + db.close + return new HttpResponse(200) + end +end + +# REST Actions working on Meetups +class OpportunityMeetupREST + super OpportunityAction + + redef fun answer(request, uri) do + var args = uri.split("/") + if args.has("new_pers") then + var name = request.string_arg("persname") + var m_id = request.string_arg("meetup_id") + var ans = request.string_arg("answers").split("&") + if name == null or m_id == null then return bad_req + print ans + var ansmap = new HashMap[Int, Bool] + for i in ans do + var mp = i.split("=") + var b = false + if mp.last == "true" then b = true + var id = mp.first.split("_").last + if not id.is_numeric then continue + ansmap[id.to_i] = b + end + var db = new OpportunityDB.open(db_path) + var m = db.find_meetup_by_id(m_id) + var sublen = name.index_of(' ') + var rname = "" + var rsurname = "" + if sublen == -1 then + rsurname = name + else + rsurname = name.substring(0, sublen) + rname = name.substring_from(sublen + 1) + end + var p = new People(rname, rsurname) + for i in m.answers(db) do + if not ansmap.has_key(i.id) then + p.answers[i] = false + else + p.answers[i] = ansmap[i.id] + end + end + p.commit(db) + db.close + var rsp = new HttpResponse(200) + rsp.body = p.id.to_s + return rsp + end + return new HttpResponse(400) + end +end diff --git a/contrib/opportunity/src/opportunity_model.nit b/contrib/opportunity/src/opportunity_model.nit new file mode 100644 index 0000000..fa9e674 --- /dev/null +++ b/contrib/opportunity/src/opportunity_model.nit @@ -0,0 +1,301 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Model for the data of Opportunity +module opportunity_model + +import sqlite3 +import sha1 +import serialization + +# A SQLiteDB object for `Opportunity` +class OpportunityDB + super Sqlite3DB + + init open(x) do + super + + create_db + end + + # Creates the tables and triggers for Opportunity (SQLite3 DB) + fun create_db do + assert create_table("IF NOT EXISTS meetups (id CHAR(40) PRIMARY KEY, name TEXT, date TEXT, place TEXT);") else + print error or else "?" + end + assert create_table("IF NOT EXISTS people(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT);") else + print error or else "?" + end + assert create_table("IF NOT EXISTS answers(id INTEGER PRIMARY KEY AUTOINCREMENT, meetup_id CHAR(40), name TEXT, FOREIGN KEY(meetup_id) REFERENCES meetups(id));") else + print error or else "?" + end + assert create_table("IF NOT EXISTS part_answers(id_part INTEGER, id_ans INTEGER, value INTEGER, FOREIGN KEY(id_part) REFERENCES people(id), FOREIGN KEY(id_ans) REFERENCES answers(id));") else + print error or else "?" + end + #NOTE: The following triggers could be replaced by ON DELETE CASCADE clauses + # Thing is, SQLite does not seem to support those operations (well, not by default, it seems + # we must re-compile the lib to support it. So, well, let's just create triggers heh. + assert execute("CREATE TRIGGER IF NOT EXISTS answers_clean AFTER DELETE ON meetups BEGIN DELETE FROM answers WHERE answers.meetup_id=OLD.id;END;") else + print error or else "?" + end + assert execute("CREATE TRIGGER IF NOT EXISTS ans_clean AFTER DELETE ON answers BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_ans;END;") else + print error or else "?" + end + assert execute("CREATE TRIGGER IF NOT EXISTS ppl_clean AFTER DELETE ON people BEGIN DELETE FROM part_answers WHERE OLD.id=part_answers.id_part;END;") + end + + # Find a `People` by its id, or `null` if it could not be found + fun find_people_by_id(id: Int): nullable People do + var req = select("* from people where id={id};") + for i in req do + return new People.from_db(i[0].to_i, i[1].to_s, i[2].to_s) + end + return null + end + + # Find a `Meetup` by its id or `null` if it could not be found + fun find_meetup_by_id(id: String): nullable Meetup do + var req = select("* FROM meetups where id={id.to_sql_string};") + for i in req do + return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s) + end + return null + end + + # Change an Answer `ansid` for someone with an id `pid` to `resp` + # + # Returns `true` if the request was sucessful, false otherwise + fun change_answer(pid: Int, ansid: Int, resp: Bool): Bool do + var rsp = 0 + if resp then rsp = 1 + var rq = execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES({pid},{ansid},{rsp});") + if not rq then + print "Error while updating answer {ansid}:{pid}" + print error or else "Unknown error" + return false + end + return true + end + + # Removes a person in the Database by its `id` + # + # Returns true if sucessful, false otherwise + fun remove_people_by_id(id: Int): Bool do + var rq = execute("DELETE FROM people WHERE id = {id};") + if not rq then + print "Cannot delete people {id}" + print error or else "Unknown error" + return false + end + return true + end +end + +# Any kind of Database Object that can be persisted to the database +abstract class DBObject + + # Commits the modifications done to the Object in the database + fun commit(db: OpportunityDB): Bool is abstract +end + +# A Meetup participant, linked to the DB +class People + super DBObject + + # ID in the Database, -1 if not set + var id: Int = -1 + # Name of the participant + var name: String + # Surname of the participant + var surname: String + # Map of the answers of a Meetup and the answers of the participant + var answers: Map[Answer, Bool] = new HashMap[Answer, Bool] + + # To be used internally when fetching the `People` in Database + private init from_db(id: Int, name, surname: String) do + init(name, surname) + self.id = id + end + + # Changes an answer `ans` (or adds it) + fun answer=(ans: Answer, resp: Bool) do + answers[ans] = resp + end + + # Loads the answers for a Meetup + # + # NOTE: If `self` does not exist in the Database, no answers will be fetched + fun load_answers(db: OpportunityDB, meetup: Meetup) do + self.answers = new HashMap[Answer, Bool] + var req = db.select("answers.id, answers.name, part_answers.value FROM part_answers, answers WHERE part_answers.id_part={id} AND answers.id=part_answers.id_ans AND answers.meetup_id={meetup.id.to_sql_string} GROUP BY answers.id;") + for i in req do + var ans = new Answer.from_db(i[0].to_i, i[1].to_s) + answers[ans] = false + if i[2].to_i == 1 then answers[ans] = true + end + end + + redef fun to_s do return "{surname.capitalized} {name.capitalized}" + + redef fun commit(db) do + if id == -1 then + if not db.execute("INSERT INTO people (name,surname) VALUES ({name.to_sql_string}, {surname.to_sql_string});") then + print "Error while adding people {self}" + print db.error or else "Unknown error" + return false + end + id = db.last_insert_rowid + else + if not db.execute("UPDATE people SET name={name.to_sql_string}, surname={surname.to_sql_string} WHERE ID={id};") then + print "Error while updating people {self}" + print db.error or else "Unknown error" + return false + end + end + for i,j in answers do + if i.id == -1 then i.commit(db) + var val = 0 + if j then val = 1 + if not db.execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES ({id},{i.id},{val});") then + print("Error while adding/replacing part_answers {id}|{i.id}|{j}") + print db.error or else "Unknown error" + return false + end + end + return true + end +end + +# A `Meetup` is an opportunity of meeting, linked to the database +class Meetup + super DBObject + + # ID of the meetup, SHA-1 of the informations that are contained + var id: String = "" + # Name for the meetup + var name: String + # SQLite-formatted date : YYYY:DD:MM HH:MM:SS + var date: String + # Place of the meetup + var place: String + + # Builds the object with all the informations found in the database + private init from_db(id, name, date, place: String) do + self.id = id + self.name = name + self.date = date + self.place = place + end + + # Gets the answers bound to the current `Meetup` + fun answers(db: OpportunityDB): Array[Answer] do + if id == "" then + return new Array[Answer] + end + var res = db.select("id, name FROM answers WHERE meetup_id={id.to_sql_string}") + var ans = new Array[Answer] + for i in res do + ans.add new Answer.from_db(i[0].to_i, i[1].to_s) + end + return ans + end + + # Gets the list of the participants of a `Meetup` + fun participants(db: OpportunityDB): Array[People] do + var resp = db.select("people.* FROM people, meetups, answers, part_answers WHERE meetups.id={id.to_sql_string} AND answers.meetup_id={id.to_sql_string} AND part_answers.id_ans=answers.id AND people.id=part_answers.id_part GROUP BY people.id;") + var arr = new Array[People] + for i in resp do + arr.add (new People.from_db(i[0].to_i, i[1].to_s, i[2].to_s)) + end + return arr + end + + redef fun commit(db) do + if id == "" then + var tmpid = (name + date + place).sha1_to_s + if not db.execute("INSERT INTO meetups (id, name, date, place) VALUES({tmpid.to_sql_string}, {name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string});") then + print "Error recording entry Meetup {self}" + print db.error or else "Null error" + return false + end + id = tmpid + return true + else + return db.execute("UPDATE meetups (name, date, place) VALUES({name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string}) WHERE ID={id.to_sql_string};") + end + end + + redef fun to_s do + return "Event : {name}\nWhen : {date}\nWhere : {place}" + end +end + +# An answer linked to a Meetup in the database +class Answer + super DBObject + + # Name of the answer (title) + var name: String + # Id in the database, -1 if not set + var id: Int = -1 + # Meetup the answer is linked to (null while it is not added in the database or set via API) + var meetup: nullable Meetup = null is writable + + # To be used internally when fetching the object from Database + private init from_db(id: Int, name: String) do + init name + self.id = id + end + + # Loads the Meetup associated to `self` + # + # REQUIRE: is loaded in database + fun load_meetup(db: OpportunityDB): Meetup do + assert id != null + var res = db.select("meetups.* FROM meetups, answers WHERE answers.id={id} AND answers.meetup_id=meetups.id;") + for i in res do + return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s) + end + # If no Meetup could be loaded, the contract was not respected + abort + end + + redef fun commit(db) do + var m = meetup + if m == null then return false + if m.id == "" then + if not m.commit(db) then + print "Error when creating meetup {m}" + return false + end + end + if id == -1 then + if not db.execute("INSERT INTO answers (name, meetup_id) VALUES({name.to_sql_string}, {m.id.to_sql_string});") then + print "Cannot create {self} in database" + print db.error or else "Unknown error" + return false + end + id = db.last_insert_rowid + else + if not db.execute("UPDATE answers (name) VALUES ({name.to_sql_string}) WHERE meetup_id={m.id.to_sql_string};") then + print "Error updating {self} in database" + print db.error or else "Unknown error" + return false + end + end + return true + end + + redef fun to_s do return name +end diff --git a/contrib/opportunity/src/opportunity_web.nit b/contrib/opportunity/src/opportunity_web.nit new file mode 100644 index 0000000..73a3b39 --- /dev/null +++ b/contrib/opportunity/src/opportunity_web.nit @@ -0,0 +1,35 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Web server for Opportunity : A meetup scheduler for real-world encounters ! +module opportunity_web + +import opportunity_controller +import opts +import privileges + +# Avoids a crash when running automatic tests +if "NIT_TESTING".environ == "true" then exit 0 + +var iface = "localhost:80" + +var vh = new VirtualHost(iface) +vh.routes.add new Route("/rest/", new OpportunityRESTAction) +vh.routes.add new Route(null, new OpportunityWelcome) + +var fac = new HttpFactory.and_libevent +fac.config.virtual_hosts.add vh + +print "Launching server on http://{iface}/" +fac.run diff --git a/contrib/opportunity/src/templates/boilerplate.nit b/contrib/opportunity/src/templates/boilerplate.nit new file mode 100644 index 0000000..cb2b11c --- /dev/null +++ b/contrib/opportunity/src/templates/boilerplate.nit @@ -0,0 +1,124 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Contains the main components of a webpage for Opportunity +module boilerplate + +import template + +# Header for a Opportunity page +class OpportunityHeader + super Template + + # Javascript code that is included in the `OpportunityPage` + var page_js = "" is writable + + redef fun rendering do + add """ + + + + Opportunity - The meetup planner + + + + + + + + + + +
+""" + end + +end + +# Footer for a Opportunity page +class OpportunityFooter + super Template + + redef fun rendering do + add """ +
+ + + +""" + end + +end + +# Any Opportunity page that contains the header, body and footer. +class OpportunityPage + super Template + + var header = new OpportunityHeader + + var body: Streamable = "" is writable + + var footer = new OpportunityFooter + + redef fun rendering do + add header + add body + add footer + end + +end diff --git a/contrib/opportunity/src/templates/meetup.nit b/contrib/opportunity/src/templates/meetup.nit new file mode 100644 index 0000000..d9af51d --- /dev/null +++ b/contrib/opportunity/src/templates/meetup.nit @@ -0,0 +1,193 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Shows a meetup and allows to modify its participants +module meetup + +import opportunity_model +import boilerplate +import welcome +import template + +# Shows a meetup and allows to modify its participants +class OpportunityMeetupPage + super OpportunityPage + + # Meetup the page is supposed to show + var meetup: nullable Meetup = null + + init from_id(id: String) do + var db = new OpportunityDB.open("opportunity") + meetup = db.find_meetup_by_id(id) + db.close + end + + init do + header.page_js = """ + function change_answer(ele){ + var e = document.getElementById(ele.id); + var i = e.innerHTML; + var ans = true; + if(i === "
✔
"){ + ans = false; + e.innerHTML = "
✘
" + e.style.color = "red"; + }else{ + e.innerHTML = "
✔
"; + 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 === "
✔
"){ + ans = false; + e.innerHTML = "
✘
"; + e.style.color = "red"; + }else{ + e.innerHTML = "
✔
"; + 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 +

{{{name}}}

+

When : {{{date}}}

+

Where : {{{place}}}

+ + +""" + t.add "" + t.add "" + for i in answers(db) do + t.add "" + end + t.add "" + for i in participants(db) do + t.add "" + t.add """""" + i.load_answers(db, self) + t.add "" + for j,k in i.answers do + t.add """" + end + t.add "" + end + t.add """ + + + + """ + for i in answers(db) do + t.add "" + end + t.add "" + t.add "
Participating" + t.add i.to_s + t.add "
❌
" + t.add i.to_s + t.add "
" + if k then + t.add "✔" + else + t.add "✘" + end + t.add "
+
✘
" + return t + end +end diff --git a/contrib/opportunity/src/templates/meetup_confirmation.nit b/contrib/opportunity/src/templates/meetup_confirmation.nit new file mode 100644 index 0000000..0752a37 --- /dev/null +++ b/contrib/opportunity/src/templates/meetup_confirmation.nit @@ -0,0 +1,44 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Page to show when the creation of a Meetup is successful +module meetup_confirmation + +import boilerplate +import opportunity_model + +# Show this page when a `Meetup` is sucessfully created. +class MeetupConfirmation + super OpportunityPage + + var meetup: Meetup + + init do + body = """ + +

+

Your meetup was successfully created.

+

+ You can invite people to participate to your event by sharing them this link : {{{meetup.name}}} +

+

+ See you soon for more Opportunities ! +

+

+ """ + end + +end diff --git a/contrib/opportunity/src/templates/meetup_creation.nit b/contrib/opportunity/src/templates/meetup_creation.nit new file mode 100644 index 0000000..8cd364a --- /dev/null +++ b/contrib/opportunity/src/templates/meetup_creation.nit @@ -0,0 +1,66 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +module meetup_creation + +import boilerplate + +class MeetupCreationPage + super OpportunityPage + + init do + header.page_js = """ + function new_answer(sender){ + var ansdiv = $('#answers'); + var nb = ansdiv.children() + var s = nb.last(); + var ss = s.attr("id").split('_'); + var l = ss[ss.length - 1]; + nb = parseInt(l) + 1; + var ch = ansdiv.children(); + ch.last().after('') + ch.last().after(''); + } + """ + body = """ + +
+
+
+ + + + + + +
+
+

Answers

+ + +
+
+ +
+
+ +
+
+
+""" + end + +end diff --git a/contrib/opportunity/src/templates/templates.nit b/contrib/opportunity/src/templates/templates.nit new file mode 100644 index 0000000..d2dfd9c --- /dev/null +++ b/contrib/opportunity/src/templates/templates.nit @@ -0,0 +1,22 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# All the templates used for the view are to be declared here +module templates + +import boilerplate +import welcome +import meetup_creation +import meetup +import meetup_confirmation diff --git a/contrib/opportunity/src/templates/welcome.nit b/contrib/opportunity/src/templates/welcome.nit new file mode 100644 index 0000000..eaae527 --- /dev/null +++ b/contrib/opportunity/src/templates/welcome.nit @@ -0,0 +1,41 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Welcome page for Opportunity +module welcome + +import boilerplate + +# Welcome page for Opportunity +class OpportunityHomePage + super OpportunityPage + + init do + body = """ + +

+

Opportunity is a free (as in free software), easy-to-use, meetup planifier.

+

You can start using it right now by creating a new Meetup and sharing it with your friends!

+

+

+ +
+

+

+""" + end + +end diff --git a/contrib/opportunity/tests/db_tests.nit b/contrib/opportunity/tests/db_tests.nit new file mode 100644 index 0000000..cbec6f7 --- /dev/null +++ b/contrib/opportunity/tests/db_tests.nit @@ -0,0 +1,99 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# Tests for the model of Opportunity +module db_tests + +import opportunity_model + +redef class OpportunityDB + + fun wipe do + execute("DROP TABLE people;") + execute("DROP TABLE meetups;") + execute("DROP TABLE answers;") + execute("DROP TABLE part_answers;") + execute("DROP INDEX answers_clean;") + execute("DROP INDEX ans_clean;") + execute("DROP INDEX ppl_clean;") + end + +end + +print "Opening DB" + +var db = new OpportunityDB.open("opportunity") + +print "DB opened" + +db.wipe + +print "Wiped" + +db.close + +db = new OpportunityDB.open("opportunity") + +var hj = new People("Jack", "Handsome") + +var m = new Meetup("Awaken the warrior", "2024/05/28", "Vault of the Warrior") +assert m.commit(db) + +var vh = new People("Hunter", "Vault") + +var ll = new People("", "Lilith") + +var y = new Answer("Yes") +y.meetup = m +y.commit(db) + +var n = new Answer("No") +n.meetup = m +n.commit(db) + +var h = new Answer("I have no choice, I'm a hostage") +h.meetup = m +h.commit(db) + +hj.answer(y) = true +hj.answer(n) = false +hj.answer(h) = false + +vh.answer(y) = true +vh.answer(n) = false +vh.answer(h) = false + +ll.answer(y) = true +ll.answer(n) = false +ll.answer(h) = true + +hj.commit db +vh.commit db +ll.commit db + +assert hj.commit(db) +assert vh.commit(db) +assert ll.commit(db) + +print db.find_meetup_by_id(m.id) or else "null" + +for i in m.participants(db) do + print "Answers for {i.to_s.trim}" + i.load_answers(db, m) + for k,v in i.answers do + print "{k.to_s.trim} => {v.to_s.trim}" + end +end + +db.close diff --git a/contrib/opportunity/tests/sav/db_tests.res b/contrib/opportunity/tests/sav/db_tests.res new file mode 100644 index 0000000..1667896 --- /dev/null +++ b/contrib/opportunity/tests/sav/db_tests.res @@ -0,0 +1,18 @@ +Opening DB +DB opened +Wiped +Event : Awaken the warrior +When : 2024/05/28 +Where : Vault of the Warrior +Answers for Handsome Jack +Yes => true +No => false +I have no choice, I'm a hostage => false +Answers for Vault Hunter +Yes => true +No => false +I have no choice, I'm a hostage => false +Answers for Lilith +Yes => true +No => false +I have no choice, I'm a hostage => true diff --git a/contrib/pep8analysis/src/pep8analysis_web.nit b/contrib/pep8analysis/src/pep8analysis_web.nit index 259fd09..18ee374 100644 --- a/contrib/pep8analysis/src/pep8analysis_web.nit +++ b/contrib/pep8analysis/src/pep8analysis_web.nit @@ -108,15 +108,6 @@ redef class AnalysisManager fun show_graph(content: String) do "show_graph('{content.escape_to_c}');".run_js end -class StringIStream - super BufferedIStream - - init(str: String) do _buffer = new FlatBuffer.from(str) - - redef fun fill_buffer do end_reached = true - redef var end_reached: Bool = false -end - redef class NativeString fun run_analysis do manager.run to_s end diff --git a/examples/mnit_dino/src/game_logic.nit b/examples/mnit_dino/src/game_logic.nit index 1df0119..8d1a849 100644 --- a/examples/mnit_dino/src/game_logic.nit +++ b/examples/mnit_dino/src/game_logic.nit @@ -386,7 +386,8 @@ class Bush super Entity end # Sort entities on screen in order of Y, entities in the back are drawn first class EntitiesSorter - super AbstractSorter[Entity] + super Comparator + redef type COMPARED: Entity redef fun compare(a, b) do return b.pos.y <=> a.pos.y end diff --git a/lib/ai/search.nit b/lib/ai/search.nit index f279d4f..21a349d 100644 --- a/lib/ai/search.nit +++ b/lib/ai/search.nit @@ -608,7 +608,8 @@ end # Used to compare nodes with their score. # Smaller is score, smaller is the node. private class NodeComparator[S: Object, A] - super Comparator[SearchNode[S, A]] + super Comparator + redef type COMPARED: SearchNode[S, A] redef fun compare(a,b) do return a.score <=> b.score end diff --git a/lib/console.nit b/lib/console.nit index e6a129f..04dcb6e 100644 --- a/lib/console.nit +++ b/lib/console.nit @@ -12,25 +12,185 @@ # 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 diff --git a/lib/counter.nit b/lib/counter.nit index 75278d2..670fc59 100644 --- a/lib/counter.nit +++ b/lib/counter.nit @@ -174,7 +174,8 @@ class Counter[E: Object] end private class CounterComparator[E: Object] - super Comparator[E] + super Comparator + redef type COMPARED: E var counter: Counter[E] redef fun compare(a,b) do return self.counter.map[a] <=> self.counter.map[b] end diff --git a/lib/io/io.nit b/lib/io/io.nit new file mode 100644 index 0000000..c48b171 --- /dev/null +++ b/lib/io/io.nit @@ -0,0 +1,14 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Additional services for streams. +module io + +import push_back_reader diff --git a/lib/io/push_back_reader.nit b/lib/io/push_back_reader.nit new file mode 100644 index 0000000..257c662 --- /dev/null +++ b/lib/io/push_back_reader.nit @@ -0,0 +1,110 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Input stream that permits to push bytes back to the stream. +module io::push_back_reader + +# Input stream that permits to push bytes back to the stream. +interface PushBackReader + super IStream + + # Push the specified byte back to the stream. + # + # The specified byte is usually the last read byte that is not + # “unread” already. + fun unread_char(c: Char) is abstract + + # Push the specified string back to the stream. + # + # The specified string is usually the last read string that is not + # “unread” already. + fun unread(s: String) do + for c in s.chars.reverse_iterator do unread_char(c) + end +end + +# Decorates an input stream to permit to push bytes back to the input stream. +class PushBackDecorator + super PushBackReader + + # The parent stream. + var parent: IStream + + # The stack of the pushed-back bytes. + # + # Bytes are in the reverse order they will reappear in the stream. + # `unread` pushes bytes after already pushed-back bytes. + # + # TODO: With optimized bulk array copy operations, a reversed stack (like in + # OpenJDK) would be more efficient. + private var buf: Sequence[Char] = new Array[Char] + + redef fun read_char: Int do + if buf.length <= 0 then return parent.read_char + return buf.pop.ascii + end + + redef fun read(i: Int): String do + if i <= 0 then return "" + if buf.length <= 0 then return parent.read(i) + var s = new FlatBuffer.with_capacity(i) + + loop + s.chars.push(buf.pop) + i -= 1 + if i <= 0 then + return s.to_s + else if buf.length <= 0 then + s.append(parent.read(i)) + return s.to_s + end + end + end + + redef fun read_all: String do + if buf.length <= 0 then return parent.read_all + var s = new FlatBuffer + + loop + s.chars.push(buf.pop) + if buf.length <= 0 then + s.append(parent.read_all) + return s.to_s + end + end + end + + redef fun append_line_to(s: Buffer) do + if buf.length > 0 then + var c: Char + + loop + c = buf.pop + s.chars.push(c) + if c == '\n' then return + if buf.length <= 0 then break + end + end + parent.append_line_to(s) + end + + redef fun eof: Bool do return buf.length <= 0 and parent.eof + + redef fun close do + buf.clear + parent.close + end + + redef fun unread_char(c: Char) do buf.push(c) + + redef fun unread(s: String) do + for c in s.chars.reverse_iterator do buf.push(c) + end +end diff --git a/lib/io/test_push_back_reader.nit b/lib/io/test_push_back_reader.nit new file mode 100644 index 0000000..5bace8f --- /dev/null +++ b/lib/io/test_push_back_reader.nit @@ -0,0 +1,158 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Test suites for module `push_back_reader` +module test_push_back_reader is test_suite + +import test_suite +import io::push_back_reader + +class TestPushBackDecorator + super TestSuite + + private fun sample: PushBackDecorator do + return new PushBackDecorator(new StringIStream(""" +abcd + +efg +""")) + end + + fun test_read_char do + var subject = sample + + assert 'a' == subject.read_char.ascii + end + + fun test_read_char_eof do + var subject = new PushBackDecorator(new StringIStream("")) + + assert -1 == subject.read_char + end + + fun test_unread_read_char do + var subject = sample + + subject.unread_char('z') + assert 'z' == subject.read_char.ascii + assert 'a' == subject.read_char.ascii + end + + fun test_read_partial do + var subject = sample + + assert "abcd" == subject.read(4) + end + + fun test_read_too_much do + var subject = sample + var exp = """ +abcd + +efg +""" + assert exp == subject.read(100) + end + + fun test_unread_read do + var subject = sample + + subject.unread("a") + assert "aab" == subject.read(3) + end + + fun test_unread_read_mixed do + var subject = sample + + subject.unread("a") + assert "aab" == subject.read(3) + end + + fun test_read_all do + var subject = sample + var exp = """ +abcd + +efg +""" + assert exp == subject.read_all + end + + fun test_unread_read_all do + var subject = sample + var exp = """ +fooabcd + +efg +""" + subject.unread("foo") + assert exp == subject.read_all + end + + fun test_read_line do + var subject = sample + + assert "abcd\n" == subject.read_line + assert "\n" == subject.read_line + end + + fun test_unread_read_line do + var subject = sample + + subject.unread("a\nb") + assert "a\n" == subject.read_line + assert "babcd\n" == subject.read_line + end + + fun test_eof do + var subject = sample + + assert not subject.eof + subject.read_all + assert subject.eof + end + + fun test_eof_empty do + var subject = new PushBackDecorator(new StringIStream("")) + + assert subject.eof + end + + fun test_close do + var subject = sample + + subject.close + assert subject.eof + end + + fun test_unread_close do + var subject = sample + + subject.unread("foo") + subject.close + assert subject.eof + end + + fun test_unread_char_order do + var subject = sample + + subject.unread_char('z') + subject.unread_char('y') + assert "yzab" == subject.read(4) + end + + fun test_unread_order do + var subject = sample + + subject.unread("bar") + subject.unread("foo") + assert "foobarab" == subject.read(8) + end +end diff --git a/lib/mnit/mnit_injected_input.nit b/lib/mnit/mnit_injected_input.nit index c590a85..4ed9664 100644 --- a/lib/mnit/mnit_injected_input.nit +++ b/lib/mnit/mnit_injected_input.nit @@ -76,12 +76,12 @@ redef class App redef fun setup do var env = "MNIT_SRAND".environ - if env != null and env != "" then + if env != "" then srand_from(env.to_i) end var input = "MNIT_READ_INPUT".environ - if input != null and input != "" then + if input != "" then injected_input_stream = new IFStream.open(input) print "GET injected_input_stream {input}" end diff --git a/lib/nitcorn/http_request.nit b/lib/nitcorn/http_request.nit index 07b91ad..47e6823 100644 --- a/lib/nitcorn/http_request.nit +++ b/lib/nitcorn/http_request.nit @@ -57,6 +57,41 @@ class HttpRequest # The arguments passed with the POST or GET method (with a priority on POST) var all_args = new HashMap[String, String] + + # Returns argument `arg_name` in the request as a String + # or null if it was not found. + # Also cleans the String by trimming it. + # If the Strings happens to be empty after trimming, + # the method will return `null` + # + # NOTE: Prioritizes POST before GET + fun string_arg(arg_name: String): nullable String do + if not all_args.has_key(arg_name) then return null + var s = all_args[arg_name].trim + if s.is_empty then return null + return s + end + + # Returns argument `arg_name` as an Int or `null` if not found or not a number. + # + # NOTE: Prioritizes POST before GET + fun int_arg(arg_name: String): nullable Int do + if not all_args.has_key(arg_name) then return null + var i = all_args[arg_name] + if not i.is_numeric then return null + return i.to_i + end + + # Returns argument `arg_name` as a Bool or `null` if not found or not a boolean. + # + # NOTE: Prioritizes POST before GET + fun bool_arg(arg_name: String): nullable Bool do + if not all_args.has_key(arg_name) then return null + var i = all_args[arg_name] + if i == "true" then return true + if i == "false" then return false + return null + end end # Utility class to parse a request string and build a `HttpRequest` @@ -114,10 +149,6 @@ class HttpRequestParser var parts = line.split_once_on('=') if parts.length > 1 then var decoded = parts[1].replace('+', " ").from_percent_encoding - if decoded == null then - print "decode error" - continue - end http_request.post_args[parts[0]] = decoded http_request.all_args[parts[0]] = decoded else diff --git a/lib/opts.nit b/lib/opts.nit index a1bffc1..c088325 100644 --- a/lib/opts.nit +++ b/lib/opts.nit @@ -194,11 +194,7 @@ class OptionEnum redef fun pretty_default do - if default_value != null then - return " ({values[default_value]})" - else - return "" - end + return " ({values[default_value]})" end end diff --git a/lib/ordered_tree.nit b/lib/ordered_tree.nit index f6cd69a..91f8e63 100644 --- a/lib/ordered_tree.nit +++ b/lib/ordered_tree.nit @@ -80,7 +80,7 @@ class OrderedTree[E: Object] # Sort roots and other elements using a comparator method # This method basically sorts roots then each group of children - fun sort_with(comparator: Comparator[E]) + fun sort_with(comparator: Comparator) do comparator.sort(roots) for a in sub.values do diff --git a/lib/pipeline.nit b/lib/pipeline.nit index da1b4c4..31d007b 100644 --- a/lib/pipeline.nit +++ b/lib/pipeline.nit @@ -35,7 +35,7 @@ redef interface Iterator[E] # Filter: sort with a given `comparator`. # Important: require O(n) memory. - fun sort_with(comparator: Comparator[E]): Iterator[E] + fun sort_with(comparator: Comparator): Iterator[E] do var a = self.to_a comparator.sort(a) diff --git a/lib/poset.nit b/lib/poset.nit index 3149b21..7a26f90 100644 --- a/lib/poset.nit +++ b/lib/poset.nit @@ -24,7 +24,9 @@ module poset # * transitivity: `(self.has_edge(e,f) and self.has_edge(f,g)) implies self.has_edge(e,g)` class POSet[E: Object] super Collection[E] - super Comparator[E] + super Comparator + + redef type COMPARED: E is fixed redef fun iterator do return elements.keys.iterator diff --git a/lib/signals.nit b/lib/signals.nit index 89f4a1b..f1044e8 100644 --- a/lib/signals.nit +++ b/lib/signals.nit @@ -15,6 +15,48 @@ # 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 `{ diff --git a/lib/sqlite3/sqlite3.nit b/lib/sqlite3/sqlite3.nit index d016a4c..05cbed0 100644 --- a/lib/sqlite3/sqlite3.nit +++ b/lib/sqlite3/sqlite3.nit @@ -105,6 +105,9 @@ class Sqlite3DB if err.is_ok then return null return err.to_s end + + # Returns the id for the last successful insert on the current connection. + fun last_insert_rowid: Int do return native_connection.last_insert_rowid end # A prepared Sqlite3 statement, created from `Sqlite3DB::prepare` or `Sqlite3DB::select` diff --git a/lib/standard/collection/abstract_collection.nit b/lib/standard/collection/abstract_collection.nit index d2cdb2c..843f8eb 100644 --- a/lib/standard/collection/abstract_collection.nit +++ b/lib/standard/collection/abstract_collection.nit @@ -152,6 +152,16 @@ interface Iterator[E] # Iterate over `self` fun iterator: Iterator[E] do return self + + # Post-iteration hook. + # + # Used to inform `self` that the iteration is over. + # Specific iterators can use this to free some resources. + # + # Is automatically invoked at the end of `for` structures. + # + # Do nothing by default. + fun finish do end end # A collection that contains only one item. @@ -526,6 +536,16 @@ interface MapIterator[K: Object, V] # Set a new `item` at `key`. #fun item=(item: E) is abstract + + # Post-iteration hook. + # + # Used to inform `self` that the iteration is over. + # Specific iterators can use this to free some resources. + # + # Is automatically invoked at the end of `for` structures. + # + # Do nothing by default. + fun finish do end end # Iterator on a 'keys' point of view of a map diff --git a/lib/standard/collection/sorter.nit b/lib/standard/collection/sorter.nit index b6319e1..cfcc376 100644 --- a/lib/standard/collection/sorter.nit +++ b/lib/standard/collection/sorter.nit @@ -10,33 +10,84 @@ # You are allowed to redistribute it and sell it, alone or is a part of # another product. -# This module contains classes used to sorts arrays. +# This module contains classes used to compare things and sorts arrays. +# # In order to provide your own sort class you should define a subclass of `Comparator` with -# a custom `Comparator::compare` function. +# a custom `Comparator::compare` function and a specific `COMPARED` virtual type. module sorter import range import array # This abstract class generalizes ways to sort an array -interface Comparator[E] +interface Comparator + # What to compare to + type COMPARED: nullable Object + # Compare `a` and `b`. # Returns: # -1 if a < b # 0 if a = b # 1 if a > b - fun compare(a: E, b: E): Int is abstract + fun compare(a: COMPARED, b: COMPARED): Int is abstract + + # Is `seq` sorted? + # + # assert default_comparator.is_sorted([1,2,2,3]) == true + # assert default_comparator.is_sorted([1,10,2,3]) == false + # assert alpha_comparator.is_sorted([1,10,2,3]) == true + fun is_sorted(seq: SequenceRead[COMPARED]): Bool + do + if seq.length <= 1 then return true + var prev = seq.first + for e in seq do + if compare(prev, e) > 0 then return false + prev = e + end + return true + end + + # Returns the minimum between `a` and `b`. + # + # assert default_comparator.min(2,10) == 2 + # assert alpha_comparator.min(2,10) == 10 + # + # If both are equivalent, then returns `a`. + # + # var m = alpha_comparator.min(1, "1") + # assert m == 1 + # assert m != "1" + fun min(a,b: COMPARED): COMPARED + do + if compare(a,b) > 0 then return b else return a + end + + # Returns the maximum between `a` and `b`. + # + # assert default_comparator.max(2,10) == 10 + # assert alpha_comparator.max(2,10) == 2 + # + # If both are equivalent, then returns `a`. + # + # var m = alpha_comparator.max(1, "1") + # assert m == 1 + # assert m != "1" + fun max(a,b: COMPARED): COMPARED + do + if compare(a,b) < 0 then return b else return a + end # Sort `array` using the `compare` function. # - # var s = new DefaultComparator[Int] - # var a = [5, 2, 3, 1, 4] - # s.sort(a) - # assert a == [1, 2, 3, 4, 5] - fun sort(array: Array[E]) do sub_sort(array, 0, array.length-1) + # var a = [10, 2, 3, 1, 4] + # default_comparator.sort(a) + # assert a == [1, 2, 3, 4, 10] + # alpha_comparator.sort(a) + # assert a == [1, 10, 2, 3, 4] + fun sort(array: Array[COMPARED]) do sub_sort(array, 0, array.length-1) # Sort `array` between `from` and `to` indices - private fun sub_sort(array: Array[E], from: Int, to: Int) + private fun sub_sort(array: Array[COMPARED], from: Int, to: Int) do if from >= to then return @@ -50,11 +101,10 @@ interface Comparator[E] # Quick-sort `array` between `from` and `to` indices # Worst case: O(n^2), Average case: O(n lg n) # - # var s = new DefaultComparator[Int] # var a = [5, 2, 3, 1, 4] - # s.quick_sort(a, 0, a.length - 1) + # default_comparator.quick_sort(a, 0, a.length - 1) # assert a == [1, 2, 3, 4, 5] - fun quick_sort(array: Array[E], from: Int, to: Int) do + fun quick_sort(array: Array[COMPARED], from: Int, to: Int) do var pivot = array[from] var i = from var j = to @@ -76,11 +126,10 @@ interface Comparator[E] # Bubble-sort `array` between `from` and `to` indices # Worst case: O(n^2), average case: O(n^2) # - # var s = new DefaultComparator[Int] # var a = [5, 2, 3, 1, 4] - # s.bubble_sort(a, 0, a.length - 1) + # default_comparator.bubble_sort(a, 0, a.length - 1) # assert a == [1, 2, 3, 4, 5] - fun bubble_sort(array: Array[E], from: Int, to: Int) + fun bubble_sort(array: Array[COMPARED], from: Int, to: Int) do var i = from while i < to do @@ -105,12 +154,10 @@ interface Comparator[E] # Insertion-sort `array` between `from` and `to` indices # Worst case: O(n^2), average case: O(n^2) # - # var s = new DefaultComparator[Int] # var a = [5, 2, 3, 1, 4] - # s.insertion_sort(a, 0, a.length - 1) + # default_comparator.insertion_sort(a, 0, a.length - 1) # assert a == [1, 2, 3, 4, 5] - fun insertion_sort(array: Array[E], from: Int, to: Int) do - var last = array.length + fun insertion_sort(array: Array[COMPARED], from: Int, to: Int) do for i in [from..to] do var j = i while j > 0 and compare(array[j], array[j - 1]) < 0 do @@ -123,11 +170,10 @@ interface Comparator[E] # Merge-sort `array` between `from` and `to` indices # Worst case: O(n lg n), average: O(n lg n) # - # var s = new DefaultComparator[Int] # var a = [5, 2, 3, 1, 4] - # s.merge_sort(a, 0, a.length - 1) + # default_comparator.merge_sort(a, 0, a.length - 1) # assert a == [1, 2, 3, 4, 5] - fun merge_sort(array: Array[E], from, to: Int) do + fun merge_sort(array: Array[COMPARED], from, to: Int) do if from >= to then return var mid = (to + from) / 2 merge_sort(array, from, mid) @@ -135,10 +181,10 @@ interface Comparator[E] merge(array, from, mid, to) end - private fun merge(array: Array[E], from, mid, to: Int) do - var l = new Array[E] + private fun merge(array: Array[COMPARED], from, mid, to: Int) do + var l = new Array[COMPARED] for i in [from..mid] do l.add array[i] - var r = new Array[E] + var r = new Array[COMPARED] for i in [mid + 1..to] do r.add array[i] var i = 0 var j = 0 @@ -162,11 +208,10 @@ interface Comparator[E] # Heap-sort `array` between `from` and `to` indices # Worst case: O(n lg n), average: O(n lg n) # - # var s = new DefaultComparator[Int] # var a = [5, 2, 3, 1, 4] - # s.heap_sort(a, 0, a.length - 1) + # default_comparator.heap_sort(a, 0, a.length - 1) # assert a == [1, 2, 3, 4, 5] - fun heap_sort(array: Array[E], from, to: Int) do + fun heap_sort(array: Array[COMPARED], from, to: Int) do var size = build_heap(array) for j in [from..to[ do array.swap_at(0, size) @@ -175,7 +220,7 @@ interface Comparator[E] end end - private fun build_heap(array: Array[E]): Int do + private fun build_heap(array: Array[COMPARED]): Int do var size = array.length - 1 var i = size / 2 while i >= 0 do @@ -185,7 +230,7 @@ interface Comparator[E] return size end - private fun heapify(array: Array[E], from, to: Int) do + private fun heapify(array: Array[COMPARED], from, to: Int) do var l = from * 2 var r = l + 1 var largest: Int @@ -205,25 +250,14 @@ interface Comparator[E] end -# Deprecated class, use `Comparator` instead -interface AbstractSorter[E] - super Comparator[E] -end - # This comparator uses the operator `<=>` to compare objects. # see `default_comparator` for an easy-to-use general stateless default comparator. -class DefaultComparator[E: Comparable] - super Comparator[E] +class DefaultComparator + super Comparator + redef type COMPARED: Comparable # Return a <=> b redef fun compare(a, b) do return a <=> b - - init do end -end - -# Deprecated class, use `DefaultComparator` instead -class ComparableSorter[E: Comparable] - super DefaultComparator[E] end # Easy-to-use general stateless default comparator that uses `<=>` to compare things. -fun default_comparator: Comparator[Comparable] do return once new DefaultComparator[Comparable] +fun default_comparator: DefaultComparator do return once new DefaultComparator diff --git a/lib/standard/file.nit b/lib/standard/file.nit index 36878e5..30e48e3 100644 --- a/lib/standard/file.nit +++ b/lib/standard/file.nit @@ -65,6 +65,7 @@ class IFStream redef fun close do var i = _file.io_close + _buffer.clear end_reached = true end @@ -327,27 +328,49 @@ redef class String # Correctly join two path using the directory separator. # - # Using a standard "{self}/{path}" does not work when `self` is the empty string. - # This method ensure that the join is valid. + # Using a standard "{self}/{path}" does not work in the following cases: + # + # * `self` is empty. + # * `path` ends with `'/'`. + # * `path` starts with `'/'`. # - # assert "hello".join_path("world") == "hello/world" - # assert "hel/lo".join_path("wor/ld") == "hel/lo/wor/ld" - # assert "".join_path("world") == "world" - # assert "/hello".join_path("/world") == "/world" + # This method ensures that the join is valid. # - # Note: you may want to use `simplify_path` on the result + # assert "hello".join_path("world") == "hello/world" + # assert "hel/lo".join_path("wor/ld") == "hel/lo/wor/ld" + # assert "".join_path("world") == "world" + # assert "hello".join_path("/world") == "/world" + # assert "hello/".join_path("world") == "hello/world" + # assert "hello/".join_path("/world") == "/world" # - # Note: I you want to join a great number of path, you can write + # Note: You may want to use `simplify_path` on the result. # - # [p1, p2, p3, p4].join("/") + # Note: This method works only with POSIX paths. fun join_path(path: String): String do if path.is_empty then return self if self.is_empty then return path if path.chars[0] == '/' then return path + if self.last == '/' then return "{self}{path}" return "{self}/{path}" end + # Convert the path (`self`) to a program name. + # + # Ensure the path (`self`) will be treated as-is by POSIX shells when it is + # used as a program name. In order to do that, prepend `./` if needed. + # + # assert "foo".to_program_name == "./foo" + # assert "/foo".to_program_name == "/foo" + # assert "".to_program_name == "./" # At least, your shell will detect the error. + fun to_program_name: String do + if self.has_prefix("/") then + return self + else + return "./{self}" + end + end + # Alias for `join_path` # # assert "hello" / "world" == "hello/world" diff --git a/lib/standard/math.nit b/lib/standard/math.nit index ba4db63..a2e0966 100644 --- a/lib/standard/math.nit +++ b/lib/standard/math.nit @@ -23,10 +23,32 @@ in "C header" `{ redef class Int # Returns a random `Int` in `[0 .. self[`. fun rand: Int is extern "kernel_Int_Int_rand_0" + + # Returns the result of a binary AND operation on `self` and `i` + # + # assert 0x10.bin_and(0x01) == 0 fun bin_and(i: Int): Int is extern "kernel_Int_Int_binand_0" + + # Returns the result of a binary OR operation on `self` and `i` + # + # assert 0x10.bin_or(0x01) == 0x11 fun bin_or(i: Int): Int is extern "kernel_Int_Int_binor_0" + + # Returns the result of a binary XOR operation on `self` and `i` + # + # assert 0x101.bin_xor(0x110) == 0x11 fun bin_xor(i: Int): Int is extern "kernel_Int_Int_binxor_0" + + # Returns the 1's complement of `self` + # + # assert 0x2F.bin_not == -48 + fun bin_not: Int is extern "kernel_Int_Int_binnot_0" + + # Returns the square root of `self` + # + # assert 16.sqrt == 4 fun sqrt: Int `{ return sqrt(recv); `} + # Returns the greatest common divisor of `self` and `o` # # assert 54.gcd(24) == 6 diff --git a/lib/standard/math_nit.h b/lib/standard/math_nit.h index 466a443..e13aac3 100644 --- a/lib/standard/math_nit.h +++ b/lib/standard/math_nit.h @@ -21,6 +21,7 @@ #define kernel_Int_Int_binand_0(self, p0) (self & p0) #define kernel_Int_Int_binor_0(self, p0) (self | p0) #define kernel_Int_Int_binxor_0(self, p0) (self ^ p0) +#define kernel_Int_Int_binnot_0(self) (~self) #define kernel_Float_Float_sqrt_0(self) sqrt(self) #define kernel_Float_Float_cos_0(self) cos(self) #define kernel_Float_Float_sin_0(self) sin(self) diff --git a/lib/standard/queue.nit b/lib/standard/queue.nit index c4cce96..ceab407 100644 --- a/lib/standard/queue.nit +++ b/lib/standard/queue.nit @@ -47,7 +47,7 @@ interface Queue[E] # assert a.take == 2 # assert a.take == 1 # - # var h = new MinHeapCmp[Int] + # var h = new MinHeap[Int].default # h.add 2 # h.add 1 # h.add 10 @@ -206,11 +206,10 @@ end # A min-heap implemented over an array # # The order is given by the `comparator`. -# If `E` is Comparable, then the subclass `MinHeapCmp` can be used instead. # # ~~~ # var a = [3,4,1,2] -# var h = new MinHeap[Int](new DefaultComparator[Int]) +# var h = new MinHeap[Int].default # h.add_all(a) # assert h.peek == 1 # var b = h.take_all @@ -220,7 +219,20 @@ class MinHeap[E: Object] super Queue[E] private var items = new Array[E] - var comparator: Comparator[E] + + # The comparator used to order the Heap + var comparator: Comparator + + # Use the `default_comparator` on Comparable elements + # + # Require self isa MinHeap[Comparable] + init default + do + assert self isa MinHeap[Comparable] + init(default_comparator) + end + + init(comparator: Comparator) do self.comparator = comparator redef fun is_empty do return items.is_empty redef fun length do return items.length @@ -307,18 +319,3 @@ class MinHeap[E: Object] return true end end - -# A `MinHeap` for `Comparable` that does not need a specific `Comparator` -# -# ~~~ -# var a = [3,4,1,2] -# var h = new MinHeapCmp[Int] -# h.add_all(a) -# assert h.peek == 1 -# var b = h.take_all -# assert b == [1, 2, 3, 4] -# ~~~ -class MinHeapCmp[E: Comparable] - super MinHeap[E] - init is old_style_init do super(new DefaultComparator[E]) -end diff --git a/lib/standard/stream.nit b/lib/standard/stream.nit index 6b70e21..6765305 100644 --- a/lib/standard/stream.nit +++ b/lib/standard/stream.nit @@ -1,13 +1,11 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # -# Copyright 2004-2008 Jean Privat -# -# This file is free software, which comes along with NIT. This software is +# This file is free software, which comes along with NIT. This software is # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header # is kept unaltered, and a notification of the changes is added. -# You are allowed to redistribute it and sell it, alone or is a part of +# You are allowed to redistribute it and sell it, alone or is a part of # another product. # Input and output streams of characters @@ -209,7 +207,7 @@ abstract class BufferedIStream fill_buffer end return s.to_s - end + end redef fun append_line_to(s) do @@ -258,7 +256,7 @@ abstract class BufferedIStream # Fill the buffer protected fun fill_buffer is abstract - # Is the last fill_buffer reach the end + # Is the last fill_buffer reach the end protected fun end_reached: Bool is abstract # Allocate a `_buffer` for a given `capacity`. @@ -399,7 +397,9 @@ redef interface Object `} end -# Stream to a String. Mainly used for compatibility with OStream type and tests. +# Stream to a String. +# +# Mainly used for compatibility with OStream type and tests. class StringOStream super OStream @@ -415,3 +415,33 @@ class StringOStream protected var closed = false redef fun close do closed = true end + +# Stream from a String. +# +# Mainly used for compatibility with IStream type and tests. +class StringIStream + super IStream + + # The string to read from. + var source: String + + # The current position in the string. + private var cursor: Int = 0 + + redef fun read_char do + if cursor < source.length then + var c = source[cursor].ascii + + cursor += 1 + return c + else + return -1 + end + end + + redef fun close do + source = "" + end + + redef fun eof do return cursor >= source.length +end diff --git a/lib/standard/string.nit b/lib/standard/string.nit index 3eede86..f78c0ac 100644 --- a/lib/standard/string.nit +++ b/lib/standard/string.nit @@ -1775,12 +1775,12 @@ redef class Int end redef class Float - # Pretty print self, print needoed decimals up to a max of 3. + # Pretty representation of `self`, with decimals as needed from 1 to a maximum of 3 # - # assert 12.34.to_s == "12.34" - # assert (-0120.03450).to_s == "-120.035" + # assert 12.34.to_s == "12.34" + # assert (-0120.030).to_s == "-120.03" # - # see `to_precision` for a different precision. + # see `to_precision` for a custom precision. redef fun to_s do var str = to_precision( 3 ) if is_inf != 0 or is_nan then return str @@ -1799,13 +1799,15 @@ redef class Float return str end - # `self` representation with `nb` digits after the '.'. + # `String` representation of `self` with the given number of `decimals` # - # assert 12.345.to_precision(1) == "12.3" - # assert 12.345.to_precision(2) == "12.35" - # assert 12.345.to_precision(3) == "12.345" - # assert 12.345.to_precision(4) == "12.3450" - fun to_precision(nb: Int): String + # assert 12.345.to_precision(0) == "12" + # assert 12.345.to_precision(3) == "12.345" + # assert (-12.345).to_precision(3) == "-12.345" + # assert (-0.123).to_precision(3) == "-0.123" + # assert 0.999.to_precision(2) == "1.00" + # assert 0.999.to_precision(4) == "0.9990" + fun to_precision(decimals: Int): String do if is_nan then return "nan" @@ -1816,25 +1818,34 @@ redef class Float return "-inf" end - if nb == 0 then return self.to_i.to_s + if decimals == 0 then return self.to_i.to_s var f = self - for i in [0..nb[ do f = f * 10.0 + for i in [0..decimals[ do f = f * 10.0 if self > 0.0 then f = f + 0.5 else f = f - 0.5 end var i = f.to_i - if i == 0 then return "0.0" - var s = i.to_s + if i == 0 then return "0." + "0"*decimals + + # Prepare both parts of the float, before and after the "." + var s = i.abs.to_s var sl = s.length - if sl > nb then - var p1 = s.substring(0, s.length-nb) - var p2 = s.substring(s.length-nb, nb) - return p1 + "." + p2 + var p1 + var p2 + if sl > decimals then + # Has something before the "." + p1 = s.substring(0, sl-decimals) + p2 = s.substring(sl-decimals, decimals) else - return "0." + ("0"*(nb-sl)) + s + p1 = "0" + p2 = "0"*(decimals-sl) + s end + + if i < 0 then p1 = "-" + p1 + + return p1 + "." + p2 end # `self` representation with `nb` digits after the '.'. @@ -2119,7 +2130,8 @@ end # # Note: it caching is not usefull, see `alpha_comparator` class CachedAlphaComparator - super Comparator[Object] + super Comparator + redef type COMPARED: Object private var cache = new HashMap[Object, String] @@ -2137,7 +2149,7 @@ end # see `alpha_comparator` private class AlphaComparator - super Comparator[Object] + super Comparator redef fun compare(a, b) do return a.to_s <=> b.to_s end @@ -2149,7 +2161,7 @@ end # var a = [1, 2, 3, 10, 20] # alpha_comparator.sort(a) # assert a == [1, 10, 2, 20, 3] -fun alpha_comparator: Comparator[Object] do return once new AlphaComparator +fun alpha_comparator: Comparator do return once new AlphaComparator # The arguments of the program as given by the OS fun args: Sequence[String] diff --git a/misc/jenkins/listnit.sh b/misc/jenkins/listnit.sh index 2d38c2f..d0f1ac8 100755 --- a/misc/jenkins/listnit.sh +++ b/misc/jenkins/listnit.sh @@ -26,7 +26,7 @@ exclude="parser_abs.nit\|nitcc_lexer0" # Directories needed for explicit -I options. -includedirs="src contrib/tnitter/src/ contrib/benitlux/src/ examples/ contrib/wiringPi/lib/" +includedirs="src contrib/tnitter/src/ contrib/benitlux/src/ examples/ contrib/wiringPi/lib/ contrib/opportunity/src/" # Flag -I incl="" diff --git a/share/man/Makefile b/share/man/Makefile new file mode 100644 index 0000000..3eeeb03 --- /dev/null +++ b/share/man/Makefile @@ -0,0 +1,21 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +IN=$(wildcard nit*.md) +OUT=$(patsubst %.md,man1/%.1,$(IN)) + +all: $(OUT) + +man1/%.1: %.md + pandoc $< -t man -s -o $@ diff --git a/share/man/README.md b/share/man/README.md new file mode 100644 index 0000000..a167029 --- /dev/null +++ b/share/man/README.md @@ -0,0 +1,21 @@ +# Manual pages for nit commands + +Pages, in markdown, in this directory where initially generated thanks to the `--stub-man` options of the commands. + +Transformation to real man pages (troff) are done with `pandoc -t man -s` command. +See the `Makefile`. + + +Once generated, manpages can then be checked individually with `man -l` + +~~~ +man -l man1/nitg.1 +~~~ + +For global access, one can set the `MANPATH` environment variable to this `man` directory (not the `man1` subdirectory). +Do not forget to append a trailing column (`:`) to keep existing manpages accessible. + +~~~ +export MANPATH=/path/to/nit/share/man: +man nitg +~~~ diff --git a/share/man/nit.md b/share/man/nit.md new file mode 100644 index 0000000..26b2adb --- /dev/null +++ b/share/man/nit.md @@ -0,0 +1,99 @@ +% NIT(1) + +# NAME + +Interprets and debugs Nit programs. + +# SYNOPSYS + +nit [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`--discover-call-trace` +: Trace calls of the first invocation of a method + +`-d` +: Launches the target program with the debugger attached to it + +`-c` +: Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned + +`--socket` +: Launches the target program with raw output on the network via sockets + +`--websocket` +: Launches the target program with output on the network via websockets + +`--port` +: Sets the debug port (Defaults to 22125) - Must be contained between 0 and 65535 + +`-o` +: compatibility (does noting) + +`-m` +: Additionals module to min-in + +`-e` +: Specifies the program from command-line + +`-n` +: Repeatedly run the program for each line in file-name arguments + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitdoc.md b/share/man/nitdoc.md new file mode 100644 index 0000000..d39c076 --- /dev/null +++ b/share/man/nitdoc.md @@ -0,0 +1,114 @@ +% NITDOC(1) + +# NAME + +Generates HTML pages of API documentation from Nit source files. + +# SYNOPSYS + +nitdoc [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`-d`, `--dir` +: output directory + +`--source` +: link for source (%f for filename, %l for first line, %L for last line) + +`--sharedir` +: directory containing nitdoc assets + +`--shareurl` +: use shareurl instead of copy shared files + +`--no-dot` +: do not generate graphes with graphviz + +`--private` +: also generate private API + +`--custom-title` +: custom title for homepage + +`--custom-footer-text` +: custom footer text + +`--custom-overview-text` +: custom intro text for homepage + +`--custom-brand` +: custom link to external site + +`--github-upstream` +: Git branch where edited commits will be pulled into (ex: user:repo:branch) + +`--github-base-sha1` +: Git sha1 of base commit used to create pull request + +`--github-gitdir` +: Git working directory used to resolve path name (ex: /home/me/myproject/) + +`--piwik-tracker` +: Piwik tracker URL (ex: nitlanguage.org/piwik/) + +`--piwik-site-id` +: Piwik site ID + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitg.md b/share/man/nitg.md new file mode 100644 index 0000000..1c45f1d --- /dev/null +++ b/share/man/nitg.md @@ -0,0 +1,177 @@ +% NITG(1) + +# NAME + +Compiles Nit programs. + +# SYNOPSYS + +nitg [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`-o`, `--output` +: Output file + +`--dir` +: Output directory + +`--no-cc` +: Do not invoke C compiler + +`--no-main` +: Do not generate main entry point + +`--make-flags` +: Additional options to make + +`--compile-dir` +: Directory used to generate temporary files + +`--hardening` +: Generate contracts in the C code against bugs in the compiler + +`--no-shortcut-range` +: Always insantiate a range and its iterator on 'for' loops + +`--no-check-covariance` +: Disable type tests of covariant parameters (dangerous) + +`--no-check-attr-isset` +: Disable isset tests before each attribute access (dangerous) + +`--no-check-assert` +: Disable the evaluation of explicit 'assert' and 'as' (dangerous) + +`--no-check-autocast` +: Disable implicit casts on unsafe expression usage (dangerous) + +`--no-check-null` +: Disable tests of null receiver (dangerous) + +`--no-check-all` +: Disable all tests (dangerous) + +`--typing-test-metrics` +: Enable static and dynamic count of all type tests + +`--invocation-metrics` +: Enable static and dynamic count of all method invocations + +`--isset-checks-metrics` +: Enable static and dynamic count of isset checks before attributes access + +`--stacktrace` +: Control the generation of stack traces + +`--no-gcc-directive` +: Disable a advanced gcc directives for optimization + +`--release` +: Compile in release mode and finalize application + +`--global` +: Use global compilation + +`--separate` +: Use separate compilation + +`--no-inline-intern` +: Do not inline call to intern methods + +`--no-union-attribute` +: Put primitive attibutes in a box instead of an union + +`--no-shortcut-equal` +: Always call == in a polymorphic way + +`--inline-coloring-numbers` +: Inline colors and ids (semi-global) + +`--inline-some-methods` +: Allow the separate compiler to inline some methods (semi-global) + +`--direct-call-monomorph` +: Allow the separate compiler to direct call monomorph sites (semi-global) + +`--skip-dead-methods` +: Do not compile dead methods (semi-global) + +`--semi-global` +: Enable all semi-global optimizations + +`--colo-dead-methods` +: Force colorization of dead methods + +`--tables-metrics` +: Enable static size measuring of tables used for vft, typing and resolution + +`--erasure` +: Erase generic types + +`--no-check-erasure-cast` +: Disable implicit casts on unsafe return with erasure-typing policy (dangerous) + +`--rta` +: Activate RTA (implicit with --global and --separate) + +`-m` +: Additionals module to min-in + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitlight.md b/share/man/nitlight.md new file mode 100644 index 0000000..54b2896 --- /dev/null +++ b/share/man/nitlight.md @@ -0,0 +1,84 @@ +% NITLIGHT(1) + +# NAME + +Generates HTML of highlited code from Nit source files. + +# SYNOPSYS + +nitlight [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`-f`, `--fragment` +: Omit document header and footer + +`--first-line` +: Start the source file at this line (default: 1) + +`--last-line` +: End the source file at this line (default: to the end) + +`-d`, `--dir` +: Output html files in a specific directory (required if more than one module) + +`--full` +: Process also imported modules + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitls.md b/share/man/nitls.md new file mode 100644 index 0000000..705a099 --- /dev/null +++ b/share/man/nitls.md @@ -0,0 +1,93 @@ +% NITLS(1) + +# NAME + +Lists the projects and/or paths of Nit sources files. + +# SYNOPSYS + +nitls [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`-k`, `--keep` +: Ignore errors and files that are not a Nit source file + +`-r`, `--recursive` +: Process directories recussively + +`-t`, `--tree` +: List source files in their groups and projects + +`-s`, `--source` +: List source files + +`-P`, `--project` +: List projects paths (default) + +`-d`, `--depends` +: List dependencies of given modules + +`-p`, `--path` +: List only path (instead of name + path) + +`-M` +: List dependencies suitable for a rule in a Makefile. Alias for -d, -p and -s + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitmetrics.md b/share/man/nitmetrics.md new file mode 100644 index 0000000..5aeb779 --- /dev/null +++ b/share/man/nitmetrics.md @@ -0,0 +1,120 @@ +% NITMETRICS(1) + +# NAME + +Computes various metrics on Nit programs. + +# SYNOPSYS + +nitmetrics [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`--all` +: Compute all metrics + +`--mmodules` +: Compute metrics about mmodules + +`--mclasses` +: Compute metrics about mclasses + +`--mendel` +: Compute mendel metrics + +`--inheritance` +: Compute metrics about inheritance usage + +`--refinement` +: Compute metrics about refinement usage + +`--self` +: Compute metrics about the usage of explicit and implicit self + +`--ast` +: Compute metrics about the usage of nodes and identifiers in the AST + +`--nullables` +: Compute metrics on nullables send + +`--static-types` +: Compute explicit static types metrics + +`--tables` +: Compute tables metrics + +`--rta` +: Compute RTA metrics + +`--csv` +: Export metrics in CSV format + +`--generate_hyperdoc` +: Generate Hyperdoc + +`--poset` +: Complete metrics on posets + +`-d`, `--dir` +: Directory where some statistics files are generated + +`--no-colors` +: Disable colors in console outputs + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitpick.md b/share/man/nitpick.md new file mode 100644 index 0000000..f852d27 --- /dev/null +++ b/share/man/nitpick.md @@ -0,0 +1,69 @@ +% NITPICK(1) + +# NAME + +Collect potential style and code issues. + +# SYNOPSYS + +nitpick [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitpretty.md b/share/man/nitpretty.md new file mode 100644 index 0000000..3238ced --- /dev/null +++ b/share/man/nitpretty.md @@ -0,0 +1,84 @@ +% NITPRETTY(1) + +# NAME + +Pretty print Nit code from Nit source files. + +# SYNOPSYS + +nitpretty [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`--dir` +: Working directory (default is '.nitpretty') + +`-o`, `--output` +: Output name (default is pretty.nit) + +`--diff` +: Show diff between source and output + +`--meld` +: Show diff between source and output using meld + +`--check` +: Check format of Nit source files + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitserial.md b/share/man/nitserial.md new file mode 100644 index 0000000..056b692 --- /dev/null +++ b/share/man/nitserial.md @@ -0,0 +1,75 @@ +% NITSERIAL(1) + +# NAME + +Generates a serialization support module + +# SYNOPSYS + +nitserial [*options*]... + +# OPTIONS + +`-o`, `--output` +: Output file (can also be 'stdout') + +`--dir` +: Output directory + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitunit.md b/share/man/nitunit.md new file mode 100644 index 0000000..24d2e1e --- /dev/null +++ b/share/man/nitunit.md @@ -0,0 +1,99 @@ +% NITUNIT(1) + +# NAME + +Executes the unit tests from Nit source files. + +# SYNOPSYS + +nitunit [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +`--full` +: Process also imported modules + +`-o`, `--output` +: Output name (default is 'nitunit.xml') + +`--dir` +: Working directory (default is '.nitunit') + +`--no-act` +: Does not compile and run tests + +`-p`, `--pattern` +: Only run test case with name that match pattern. Examples: 'TestFoo', 'TestFoo*', 'TestFoo::test_foo', 'TestFoo::test_foo*', 'test_foo', 'test_foo*' + +`-t`, `--target-file` +: Specify test suite location. + +`--gen-suite` +: Generate test suite skeleton for a module + +`-f`, `--force` +: Force test generation even if file exists + +`--private` +: Also generate test case for private methods + +`--only-show` +: Only display skeleton, do not write file + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/share/man/nitx.md b/share/man/nitx.md new file mode 100644 index 0000000..73cee66 --- /dev/null +++ b/share/man/nitx.md @@ -0,0 +1,69 @@ +% NITX(1) + +# NAME + +Displays specific pieces of API information from Nit source files. + +# SYNOPSYS + +nitx [*options*]... + +# OPTIONS + +`-W`, `--warn` +: Show more warnings + +`-w`, `--warning` +: Show/hide a specific warning + +`-q`, `--quiet` +: Do not show warnings + +`--stop-on-first-error` +: Stop on first error + +`--no-color` +: Do not use color to display errors and warnings + +`--log` +: Generate various log files + +`--log-dir` +: Directory where to generate log files + +`-h`, `-?`, `--help` +: Show Help (This screen) + +`--version` +: Show version and exit + +`--set-dummy-tool` +: Set toolname and version to DUMMY. Useful for testing + +`-v`, `--verbose` +: Verbose + +`--bash-completion` +: Generate bash_completion file for this program + +`--stub-man` +: Generate a stub manpage in pandoc markdown format + +`--disable-phase` +: DEBUG: Disable a specific phase; use `list` to get the list. + +`-I`, `--path` +: Set include path for loaders (may be used more than once) + +`--only-parse` +: Only proceed to parse step of loaders + +`--only-metamodel` +: Stop after meta-model processing + +`--ignore-visibility` +: Do not check, and produce errors, on visibility issues. + +# SEE ALSO + +The Nit language documentation and the source code of its tools and libraries may be downloaded from diff --git a/src/astbuilder.nit b/src/astbuilder.nit index 8e17b4e..630c1c0 100644 --- a/src/astbuilder.nit +++ b/src/astbuilder.nit @@ -53,6 +53,12 @@ class ASTBuilder return new ABlockExpr.make end + # Make a new, empty, loop of statements + fun make_loop: ALoopExpr + do + return new ALoopExpr.make + end + # Make a new variable read fun make_var_read(variable: Variable, mtype: MType): AVarExpr do @@ -84,6 +90,12 @@ class ASTBuilder return new ADoExpr.make end + # Make a new break for a given escapemark + fun make_break(escapemark: EscapeMark): ABreakExpr + do + return new ABreakExpr.make(escapemark) + end + # Make a new condinionnal # `mtype` is the return type of the whole if, in case of a ternary operator. fun make_if(condition: AExpr, mtype: nullable MType): AIfExpr @@ -142,6 +154,7 @@ redef class AExpr # Note: this method, aimed to `ABlockExpr` is promoted to `AExpr` because of the limitations of the hierarchies generated by sablecc3 fun add(expr: AExpr) do + print "add not implemented in {inspect}" abort end end @@ -172,29 +185,54 @@ redef class ABlockExpr end end +redef class ALoopExpr + private init make + do + _n_kwloop = new TKwloop + self.is_typed = true + n_block = new ABlockExpr + n_block.is_typed = true + end + + redef fun add(expr: AExpr) + do + n_block.add expr + end +end + redef class ADoExpr private init make do _n_kwdo = new TKwdo - escapemark = new EscapeMark(null, false) + self.is_typed = true + n_block = new ABlockExpr + n_block.is_typed = true end # Make a new break expression of the given do fun make_break: ABreakExpr do - var escapemark = self.escapemark + var escapemark = self.break_mark if escapemark == null then - escapemark = new EscapeMark(null, false) - self.escapemark = escapemark + escapemark = new EscapeMark(null) + self.break_mark = escapemark end return new ABreakExpr.make(escapemark) end + + redef fun add(expr: AExpr) + do + n_block.add expr + end end redef class ABreakExpr private init make(escapemark: EscapeMark) do + _n_kwbreak = new TKwbreak self.escapemark = escapemark + escapemark.escapes.add self + self.is_typed = true end end @@ -251,7 +289,6 @@ redef class ACallExpr if args != null then self.n_args.n_exprs.add_all(args) end - var mtype = recv.mtype.as(not null) self.callsite = callsite self.mtype = callsite.msignature.return_mtype self.is_typed = true diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index c98d54e..85222a9 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -22,47 +22,46 @@ import semantize import platform import c_tools private import annotation +import mixin # Add compiling options redef class ToolContext # --output - var opt_output: OptionString = new OptionString("Output file", "-o", "--output") + var opt_output = new OptionString("Output file", "-o", "--output") # --dir - var opt_dir: OptionString = new OptionString("Output directory", "--dir") + var opt_dir = new OptionString("Output directory", "--dir") # --no-cc - var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc") + var opt_no_cc = new OptionBool("Do not invoke C compiler", "--no-cc") # --no-main - var opt_no_main: OptionBool = new OptionBool("Do not generate main entry point", "--no-main") + var opt_no_main = new OptionBool("Do not generate main entry point", "--no-main") # --cc-paths - var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path") + var opt_cc_path = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path") # --make-flags - var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags") + var opt_make_flags = new OptionString("Additional options to make", "--make-flags") # --compile-dir - var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir") + var opt_compile_dir = new OptionString("Directory used to generate temporary files", "--compile-dir") # --hardening - var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") - # --no-shortcut-range - var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range") + var opt_hardening = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") # --no-check-covariance - var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance") + var opt_no_check_covariance = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance") # --no-check-attr-isset - var opt_no_check_attr_isset: OptionBool = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset") + var opt_no_check_attr_isset = new OptionBool("Disable isset tests before each attribute access (dangerous)", "--no-check-attr-isset") # --no-check-assert - var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert") + var opt_no_check_assert = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert") # --no-check-autocast - var opt_no_check_autocast: OptionBool = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast") + var opt_no_check_autocast = new OptionBool("Disable implicit casts on unsafe expression usage (dangerous)", "--no-check-autocast") # --no-check-null - var opt_no_check_null: OptionBool = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null") + var opt_no_check_null = new OptionBool("Disable tests of null receiver (dangerous)", "--no-check-null") # --no-check-all - var opt_no_check_all: OptionBool = new OptionBool("Disable all tests (dangerous)", "--no-check-all") + var opt_no_check_all = new OptionBool("Disable all tests (dangerous)", "--no-check-all") # --typing-test-metrics - var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics") + var opt_typing_test_metrics = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics") # --invocation-metrics - var opt_invocation_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics") + var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics") # --isset-checks-metrics - var opt_isset_checks_metrics: OptionBool = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics") + var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics") # --stacktrace - var opt_stacktrace: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace") + var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace") # --no-gcc-directives var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive") # --release @@ -71,7 +70,7 @@ redef class ToolContext redef init do super - self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range) + self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening) self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all) self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics) self.option_context.add_option(self.opt_stacktrace) @@ -181,7 +180,6 @@ class MakefileToolchain do gather_cc_paths - var mainmodule = compiler.mainmodule var compile_dir = compile_dir # Generate the .h and .c files @@ -229,7 +227,6 @@ class MakefileToolchain compiler.files_to_copy.add "{cc_paths.first}/gc_chooser.h" # FFI - var m2m = toolcontext.modelbuilder.mmodule2nmodule for m in compiler.mainmodule.in_importation.greaters do compiler.finalize_ffi_for_module(m) end @@ -310,7 +307,16 @@ class MakefileToolchain fun makefile_name(mainmodule: MModule): String do return "{mainmodule.name}.mk" - fun default_outname(mainmodule: MModule): String do return mainmodule.name + fun default_outname(mainmodule: MModule): String + do + # Search a non fictive module + var res = mainmodule.name + while mainmodule.is_fictive do + mainmodule = mainmodule.in_importation.direct_greaters.first + res = mainmodule.name + end + return res + end # Combine options and platform informations to get the final path of the outfile fun outfile(mainmodule: MModule): String @@ -342,7 +348,6 @@ class MakefileToolchain end var linker_options = new HashSet[String] - var m2m = toolcontext.modelbuilder.mmodule2nmodule for m in mainmodule.in_importation.greaters do var libs = m.collect_linker_libs if libs != null then linker_options.add_all(libs) @@ -450,7 +455,7 @@ abstract class AbstractCompiler # The real main module of the program var realmainmodule: MModule - # The modeulbuilder used to know the model and the AST + # The modelbuilder used to know the model and the AST var modelbuilder: ModelBuilder is protected writable # Is hardening asked? (see --hardening) @@ -474,7 +479,7 @@ abstract class AbstractCompiler # The list of all associated files # Used to generate .c files - var files: List[CodeFile] = new List[CodeFile] + var files = new List[CodeFile] # Initialize a visitor specific for a compiler engine fun new_visitor: VISITOR is abstract @@ -541,8 +546,6 @@ abstract class AbstractCompiler # Compile C headers # This method call compile_header_strucs method that has to be refined fun compile_header do - var v = self.header - var toolctx = modelbuilder.toolcontext self.header.add_decl("#include ") self.header.add_decl("#include ") self.header.add_decl("#include ") @@ -891,6 +894,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do + if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue var n = self.modelbuilder.mclassdef2nclassdef[cd] for npropdef in n.n_propdefs do if npropdef isa AAttrPropdef then @@ -906,6 +910,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do + if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue var n = self.modelbuilder.mclassdef2nclassdef[cd] for npropdef in n.n_propdefs do if npropdef isa AAttrPropdef then @@ -1044,7 +1049,7 @@ abstract class AbstractCompilerVisitor fun get_property(name: String, recv: MType): MMethod do assert recv isa MClassType - return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule) + return self.compiler.modelbuilder.force_get_primitive_method(self.current_node, name, recv.mclass, self.compiler.mainmodule) end fun compile_callsite(callsite: CallSite, arguments: Array[RuntimeVariable]): nullable RuntimeVariable @@ -1081,39 +1086,45 @@ abstract class AbstractCompilerVisitor fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract - # Transform varargs, in raw arguments, into a single argument of type `Array` - # Note: this method modify the given `args` - # If there is no vararg, then `args` is not modified. - fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable]) + # Evaluate `args` as expressions in the call of `mpropdef` on `recv`. + # This method is used to manage varargs in signatures and returns the real array + # of runtime variables to use in the call. + fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable] do - var recv = args.first - var vararg_rank = msignature.vararg_rank - if vararg_rank >= 0 then - assert args.length >= msignature.arity + 1 # because of self - var rawargs = args - args = new Array[RuntimeVariable] - - args.add(rawargs.first) # recv - - for i in [0..vararg_rank[ do - args.add(rawargs[i+1]) - end + var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var res = new Array[RuntimeVariable] + res.add(recv) - var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity - var vararg = new Array[RuntimeVariable] - for i in [vararg_rank..vararg_lastrank] do - vararg.add(rawargs[i+1]) - end + if args.is_empty then return res - var elttype = msignature.mparameters[vararg_rank].mtype - args.add(self.vararg_instance(mpropdef, recv, vararg, elttype)) + var vararg_rank = msignature.vararg_rank + var vararg_len = args.length - msignature.arity + if vararg_len < 0 then vararg_len = 0 - for i in [vararg_lastrank+1..rawargs.length-1[ do - args.add(rawargs[i+1]) + for i in [0..msignature.arity[ do + if i == vararg_rank then + var ne = args[i] + if ne isa AVarargExpr then + var e = self.expr(ne.n_expr, null) + res.add(e) + continue + end + var vararg = new Array[RuntimeVariable] + for j in [vararg_rank..vararg_rank+vararg_len] do + var e = self.expr(args[j], null) + vararg.add(e) + end + var elttype = msignature.mparameters[vararg_rank].mtype + var arg = self.vararg_instance(mpropdef, recv, vararg, elttype) + res.add(arg) + else + var j = i + if i > vararg_rank then j += vararg_len + var e = self.expr(args[j], null) + res.add(e) end - rawargs.clear - rawargs.add_all(args) end + return res end # Type handling @@ -1217,7 +1228,7 @@ abstract class AbstractCompilerVisitor # Checks - # Add a check and an abort for a null reciever if needed + # Add a check and an abort for a null receiver if needed fun check_recv_notnull(recv: RuntimeVariable) do if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return @@ -1232,7 +1243,7 @@ abstract class AbstractCompilerVisitor # Names handling - private var names: HashSet[String] = new HashSet[String] + private var names = new HashSet[String] private var last: Int = 0 # Return a new name based on `s` and unique in the visitor @@ -1266,6 +1277,14 @@ abstract class AbstractCompilerVisitor return name end + # Insert a C label for associated with an escapemark + fun add_escape_label(e: nullable EscapeMark) + do + if e == null then return + if e.escapes.is_empty then return + add("BREAK_{escapemark_name(e)}: (void)0;") + end + private var escapemark_names = new HashMap[EscapeMark, String] # Return a "const char*" variable associated to the classname of the dynamic type of an object @@ -1274,7 +1293,7 @@ abstract class AbstractCompilerVisitor # Variables handling - protected var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable] + protected var variables = new HashMap[Variable, RuntimeVariable] # Return the local runtime_variable associated to a Nit local variable fun variable(variable: Variable): RuntimeVariable @@ -1353,6 +1372,18 @@ abstract class AbstractCompilerVisitor return res end + # Generate an integer value + fun bool_instance(value: Bool): RuntimeVariable + do + var res = self.new_var(self.get_class("Bool").mclass_type) + if value then + self.add("{res} = 1;") + else + self.add("{res} = 0;") + end + return res + end + # Generate a string value fun string_instance(string: String): RuntimeVariable do @@ -1373,6 +1404,19 @@ abstract class AbstractCompilerVisitor return res end + fun value_instance(object: Object): RuntimeVariable + do + if object isa Int then + return int_instance(object) + else if object isa Bool then + return bool_instance(object) + else if object isa String then + return string_instance(object) + else + abort + end + end + # Generate an array value fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable is abstract @@ -1813,6 +1857,7 @@ redef class MMethodDef fun compile_inside_to_c(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do var modelbuilder = v.compiler.modelbuilder + var val = constant_value if modelbuilder.mpropdef2npropdef.has_key(self) then var npropdef = modelbuilder.mpropdef2npropdef[self] var oldnode = v.current_node @@ -1827,6 +1872,8 @@ redef class MMethodDef self.compile_parameter_check(v, arguments) nclassdef.compile_to_c(v, self, arguments) v.current_node = oldnode + else if val != null then + v.ret(v.value_instance(val)) else abort end @@ -2356,8 +2403,7 @@ redef class AExpr # Do not call this method directly, use `v.stmt` instead private fun stmt(v: AbstractCompilerVisitor) do - var res = expr(v) - if res != null then v.add("{res};") + expr(v) end end @@ -2399,12 +2445,6 @@ redef class AVarExpr end redef class AVarAssignExpr - redef fun stmt(v) - do - var variable = self.variable.as(not null) - var i = v.expr(self.n_value, variable.declared_type) - v.assign(v.variable(variable), i) - end redef fun expr(v) do var variable = self.variable.as(not null) @@ -2430,11 +2470,7 @@ redef class ASelfExpr redef fun expr(v) do return v.frame.arguments.first end -redef class AContinueExpr - redef fun stmt(v) do v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};") -end - -redef class ABreakExpr +redef class AEscapeExpr redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};") end @@ -2497,10 +2533,7 @@ redef class ADoExpr redef fun stmt(v) do v.stmt(self.n_block) - var escapemark = self.escapemark - if escapemark != null then - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - end + v.add_escape_label(break_mark) end end @@ -2511,9 +2544,9 @@ redef class AWhileExpr var cond = v.expr_bool(self.n_expr) v.add("if (!{cond}) break;") v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) end end @@ -2522,47 +2555,15 @@ redef class ALoopExpr do v.add("for(;;) \{") v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) end end redef class AForExpr redef fun stmt(v) do - # Shortcut on explicit range - # Avoid the instantiation of the range and the iterator - var nexpr = self.n_expr - if self.variables.length == 1 and nexpr isa ARangeExpr and not v.compiler.modelbuilder.toolcontext.opt_no_shortcut_range.value then - var from = v.expr(nexpr.n_expr, null) - var to = v.expr(nexpr.n_expr2, null) - var variable = v.variable(variables.first) - var one = v.new_expr("1", v.get_class("Int").mclass_type) - - v.assign(variable, from) - v.add("for(;;) \{ /* shortcut range */") - - var ok - if nexpr isa AOrangeExpr then - ok = v.send(v.get_property("<", variable.mtype), [variable, to]) - else - ok = v.send(v.get_property("<=", variable.mtype), [variable, to]) - end - assert ok != null - v.add("if(!{ok}) break;") - - v.stmt(self.n_block) - - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") - var succ = v.send(v.get_property("successor", variable.mtype), [variable, one]) - assert succ != null - v.assign(variable, succ) - v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - return - end - var cl = v.expr(self.n_expr, null) var it_meth = self.method_iterator assert it_meth != null @@ -2595,12 +2596,18 @@ redef class AForExpr abort end v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(continue_mark) var next_meth = self.method_next assert next_meth != null v.compile_callsite(next_meth, [it]) v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") + v.add_escape_label(break_mark) + + var method_finish = self.method_finish + if method_finish != null then + # TODO: Find a way to call this also in long escape (e.g. return) + v.compile_callsite(method_finish, [it]) + end end end @@ -2741,7 +2748,7 @@ redef class ACrangeExpr var i2 = v.expr(self.n_expr2, null) var mtype = self.mtype.as(MClassType) var res = v.init_instance(mtype) - var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) + v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) return res end end @@ -2753,7 +2760,7 @@ redef class AOrangeExpr var i2 = v.expr(self.n_expr2, null) var mtype = self.mtype.as(MClassType) var res = v.init_instance(mtype) - var it = v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) + v.compile_callsite(init_callsite.as(not null), [res, i1, i2]) return res end end @@ -2833,11 +2840,9 @@ redef class ASendExpr redef fun expr(v) do var recv = v.expr(self.n_expr, null) - var args = [recv] - for a in self.raw_arguments do - args.add(v.expr(a, null)) - end - return v.compile_callsite(self.callsite.as(not null), args) + var callsite = self.callsite.as(not null) + var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + return v.compile_callsite(callsite, args) end end @@ -2845,13 +2850,12 @@ redef class ASendReassignFormExpr redef fun stmt(v) do var recv = v.expr(self.n_expr, null) - var args = [recv] - for a in self.raw_arguments do - args.add(v.expr(a, null)) - end + var callsite = self.callsite.as(not null) + var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + var value = v.expr(self.n_value, null) - var left = v.compile_callsite(self.callsite.as(not null), args) + var left = v.compile_callsite(callsite, args) assert left != null var res = v.compile_callsite(self.reassign_callsite.as(not null), [left, value]) @@ -2866,14 +2870,12 @@ redef class ASuperExpr redef fun expr(v) do var recv = v.frame.arguments.first - var args = [recv] - for a in self.n_args.n_exprs do - args.add(v.expr(a, null)) - end var callsite = self.callsite if callsite != null then - # Add additionnals arguments for the super init call + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + + # Add additional arguments for the super init call if args.length == 1 then for i in [0..callsite.msignature.arity[ do args.add(v.frame.arguments[i+1]) @@ -2884,12 +2886,14 @@ redef class ASuperExpr return res end + var mpropdef = self.mpropdef.as(not null) + var args = v.varargize(mpropdef, recv, self.n_args.n_exprs) if args.length == 1 then args = v.frame.arguments end # stantard call-next-method - return v.supercall(mpropdef.as(not null), recv.mtype.as(MClassType), args) + return v.supercall(mpropdef, recv.mtype.as(MClassType), args) end end @@ -2912,11 +2916,10 @@ redef class ANewExpr else recv = v.new_expr("({ctype})0/*special!*/", mtype) end - var args = [recv] - for a in self.n_args.n_exprs do - args.add(v.expr(a, null)) - end - var res2 = v.compile_callsite(self.callsite.as(not null), args) + + var callsite = self.callsite.as(not null) + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + var res2 = v.compile_callsite(callsite, args) if res2 != null then #self.debug("got {res2} from {mproperty}. drop {recv}") return res2 @@ -2935,12 +2938,13 @@ redef class AAttrExpr end redef class AAttrAssignExpr - redef fun stmt(v) + redef fun expr(v) do var recv = v.expr(self.n_expr, null) var i = v.expr(self.n_value, null) var mproperty = self.mproperty.as(not null) v.write_attribute(mproperty, recv, i) + return i end end @@ -3019,9 +3023,6 @@ end # Create a tool context to handle options and paths var toolcontext = new ToolContext -var opt_mixins = new OptionArray("Additionals module to min-in", "-m") -toolcontext.option_context.add_option(opt_mixins) - toolcontext.tooldescription = "Usage: nitg [OPTION]... file.nit...\nCompiles Nit programs." # We do not add other options, so process them now! @@ -3040,7 +3041,6 @@ end # Here we load an process all modules passed on the command line var mmodules = modelbuilder.parse(arguments) -var mixins = modelbuilder.parse(opt_mixins.value) if mmodules.is_empty then return modelbuilder.run_phases @@ -3048,8 +3048,5 @@ modelbuilder.run_phases for mmodule in mmodules do toolcontext.info("*** PROCESS {mmodule} ***", 1) var ms = [mmodule] - if not mixins.is_empty then - ms.add_all mixins - end toolcontext.run_global_phases(ms) end diff --git a/src/compiler/android_annotations.nit b/src/compiler/android_annotations.nit index a6fc4e2..cc24d6e 100644 --- a/src/compiler/android_annotations.nit +++ b/src/compiler/android_annotations.nit @@ -125,11 +125,9 @@ redef class AAnnotation # revision number. If the working tree is dirty, it will append another field with "d" for dirty. private fun as_version(modelbuilder: ModelBuilder): String do - var annotation_name = n_atid.n_id.text var version_fields = new Array[Object] var args = n_args - var platform_name if args.length < 1 then modelbuilder.error(self, "Annotation error: \"{name}\" expects at least a single argument.") return "" diff --git a/src/compiler/coloring.nit b/src/compiler/coloring.nit index ed9b466..e447080 100644 --- a/src/compiler/coloring.nit +++ b/src/compiler/coloring.nit @@ -279,8 +279,6 @@ class BucketsColorer[H: Object, E: Object] private var colors = new HashMap[E, Int] private var conflicts = new HashMap[E, Set[E]] - init do end - # Start bucket coloring fun colorize(buckets: Map[H, Set[E]]): Map[E, Int] do compute_conflicts(buckets) diff --git a/src/compiler/compiler_ffi.nit b/src/compiler/compiler_ffi.nit index 37689d8..e9c740a 100644 --- a/src/compiler/compiler_ffi.nit +++ b/src/compiler/compiler_ffi.nit @@ -82,7 +82,7 @@ extern void nitni_global_ref_decr(void*); return res end - private var compiled_callbacks: Array[NitniCallback] = new Array[NitniCallback] + private var compiled_callbacks = new Array[NitniCallback] # Returns true if callbacks has yet to be generated and register it as being generated private fun check_callback_compilation(cb: NitniCallback): Bool @@ -98,7 +98,6 @@ redef class AMethPropdef do var mmodule = mpropdef.mclassdef.mmodule var mainmodule = v.compiler.mainmodule - var amainmodule = v.compiler.modelbuilder.mmodule2nmodule[mainmodule] var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule] var mclass_type = mpropdef.mclassdef.bound_mtype @@ -428,7 +427,6 @@ redef class MExplicitSuper var mproperty = from.mproperty assert mproperty isa MMethod var mclass_type = from.mclassdef.mclass.mclass_type - var mmodule = from.mclassdef.mmodule # In nitni files, declare internal function as extern var internal_csignature = mproperty.build_csignature(mclass_type, v.compiler.mainmodule, "___super", long_signature, internal_call_context) diff --git a/src/compiler/global_compiler.nit b/src/compiler/global_compiler.nit index 2196071..7652368 100644 --- a/src/compiler/global_compiler.nit +++ b/src/compiler/global_compiler.nit @@ -342,7 +342,7 @@ class GlobalCompilerVisitor var valtype = value.mtype.as(MClassType) var res = self.new_var(mtype) - if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then + if not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */") self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);") return res @@ -520,45 +520,21 @@ class GlobalCompilerVisitor return recvtype end - # Subpart of old call function - # Gets the receiver boxed and casted if necessary - private fun get_recv(recvtype: MClassType, args: Array[RuntimeVariable]): RuntimeVariable + redef fun call(m, recvtype, args) do - return self.autoadapt(self.autobox(args.first, recvtype), recvtype) - end + var recv_type = get_recvtype(m, recvtype, args) + var recv = self.autoadapt(self.autobox(args.first, recvtype), recvtype) + if m.is_extern then recv = unbox_extern(recv, recv_type) + + args = args.to_a + args.first = recv - # Finalizes a call to a method ´m´ on type ´recvtype´ with arguments ´args´ - private fun finalize_call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable - do assert args.length == m.msignature.arity + 1 else debug("Invalid arity for {m}. {args.length} arguments given.") var rm = new CustomizedRuntimeFunction(m, recvtype) return rm.call(self, args) end - redef fun call(m, recvtype, args) - do - var recv_type = get_recvtype(m, recvtype, args) - var recv = get_recv(recv_type, args) - if m.is_extern then recv = unbox_extern(recv, recv_type) - var new_args = args.to_a - self.varargize(m, m.msignature.as(not null), new_args) - new_args.first = recv - return finalize_call(m, recv_type, new_args) - end - - # Does a call without encapsulating varargs into an array - # Avoids multiple encapsulation when calling a super in a variadic function - fun call_without_varargize(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable - do - var recv_type = get_recvtype(m, recvtype, args) - var recv = get_recv(recv_type, args) - if m.is_extern then recv = unbox_extern(recv, recv_type) - var new_args = args.to_a - new_args.first = recv - return finalize_call(m, recv_type, new_args) - end - redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable do var types = self.collect_types(args.first) @@ -580,7 +556,7 @@ class GlobalCompilerVisitor return res end var propdef = m.lookup_next_definition(self.compiler.mainmodule, mclasstype) - var res2 = self.call_without_varargize(propdef, mclasstype, args) + var res2 = self.call(propdef, mclasstype, args) if res != null then self.assign(res, res2.as(not null)) return res end @@ -602,7 +578,7 @@ class GlobalCompilerVisitor else self.add("case {self.compiler.classid(t)}: /* test {t} */") end - var res2 = self.call_without_varargize(propdef, t, args) + var res2 = self.call(propdef, t, args) if res != null then self.assign(res, res2.as(not null)) self.add "break;" end diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index 8c400f9..3645f54 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -22,19 +22,19 @@ import rapid_type_analysis # Add separate compiler specific options redef class ToolContext # --separate - var opt_separate: OptionBool = new OptionBool("Use separate compilation", "--separate") + var opt_separate = new OptionBool("Use separate compilation", "--separate") # --no-inline-intern - var opt_no_inline_intern: OptionBool = new OptionBool("Do not inline call to intern methods", "--no-inline-intern") + var opt_no_inline_intern = new OptionBool("Do not inline call to intern methods", "--no-inline-intern") # --no-union-attribute - var opt_no_union_attribute: OptionBool = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute") + var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute") # --no-shortcut-equate - var opt_no_shortcut_equate: OptionBool = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal") + var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal") # --inline-coloring-numbers - var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers") + var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers") # --inline-some-methods - var opt_inline_some_methods: OptionBool = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods") + var opt_inline_some_methods = new OptionBool("Allow the separate compiler to inline some methods (semi-global)", "--inline-some-methods") # --direct-call-monomorph - var opt_direct_call_monomorph: OptionBool = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph") + var opt_direct_call_monomorph = new OptionBool("Allow the separate compiler to direct call monomorph sites (semi-global)", "--direct-call-monomorph") # --skip-dead-methods var opt_skip_dead_methods = new OptionBool("Do not compile dead methods (semi-global)", "--skip-dead-methods") # --semi-global @@ -42,7 +42,7 @@ redef class ToolContext # --no-colo-dead-methods var opt_colo_dead_methods = new OptionBool("Force colorization of dead methods", "--colo-dead-methods") # --tables-metrics - var opt_tables_metrics: OptionBool = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics") + var opt_tables_metrics = new OptionBool("Enable static size measuring of tables used for vft, typing and resolution", "--tables-metrics") redef init do @@ -963,7 +963,6 @@ class SeparateCompilerVisitor redef fun unbox_signature_extern(m, args) do var msignature = m.msignature.resolve_for(m.mclassdef.bound_mtype, m.mclassdef.bound_mtype, m.mclassdef.mmodule, true) - var recv = args.first if not m.mproperty.is_init and m.is_extern then args.first = self.unbox_extern(args.first, m.mclassdef.mclass.mclass_type) end @@ -1063,14 +1062,12 @@ class SeparateCompilerVisitor redef fun compile_callsite(callsite, args) do var rta = compiler.runtime_type_analysis - var recv = args.first.mtype var mmethod = callsite.mproperty # TODO: Inlining of new-style constructors if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and not mmethod.is_root_init then var tgs = rta.live_targets(callsite) if tgs.length == 1 then # DIRECT CALL - self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args) var res0 = before_send(mmethod, args) var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args) if res0 != null then @@ -1086,8 +1083,6 @@ class SeparateCompilerVisitor end redef fun send(mmethod, arguments) do - self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments) - if arguments.first.mcasttype.ctype != "val*" then # In order to shortcut the primitive, we need to find the most specific method # Howverr, because of performance (no flattening), we always work on the realmainmodule diff --git a/src/compiler/separate_erasure_compiler.nit b/src/compiler/separate_erasure_compiler.nit index af3a43c..c23476d 100644 --- a/src/compiler/separate_erasure_compiler.nit +++ b/src/compiler/separate_erasure_compiler.nit @@ -20,11 +20,11 @@ intrude import separate_compiler # Add separate erased compiler specific options redef class ToolContext # --erasure - var opt_erasure: OptionBool = new OptionBool("Erase generic types", "--erasure") + var opt_erasure = new OptionBool("Erase generic types", "--erasure") # --rta var opt_rta = new OptionBool("Activate RTA (implicit with --global and --separate)", "--rta") # --no-check-erasure-cast - var opt_no_check_erasure_cast: OptionBool = new OptionBool("Disable implicit casts on unsafe return with erasure-typing policy (dangerous)", "--no-check-erasure-cast") + var opt_no_check_erasure_cast = new OptionBool("Disable implicit casts on unsafe return with erasure-typing policy (dangerous)", "--no-check-erasure-cast") redef init do @@ -549,7 +549,6 @@ class SeparateErasureCompilerVisitor end var class_ptr - var type_table if value.mtype.ctype == "val*" then class_ptr = "{value}->class->" else diff --git a/src/doc/doc_model.nit b/src/doc/doc_model.nit index f13d69f..41386d2 100644 --- a/src/doc/doc_model.nit +++ b/src/doc/doc_model.nit @@ -190,12 +190,8 @@ redef class MGroup redef fun tpl_namespace do var tpl = new Template - if mproject != null then - tpl.add mproject.tpl_namespace - else if parent != null then - tpl.add parent.tpl_namespace - end - if mproject != null and mproject.root != self then + tpl.add mproject.tpl_namespace + if mproject.root != self then tpl.add "::" tpl.add tpl_link end diff --git a/src/doc/doc_templates.nit b/src/doc/doc_templates.nit index 200e650..516b17e 100644 --- a/src/doc/doc_templates.nit +++ b/src/doc/doc_templates.nit @@ -240,7 +240,9 @@ end # Comparator used to sort boxes by order private class OrderComparator - super Comparator[TplSidebarElt] + super Comparator + + redef type COMPARED: TplSidebarElt redef fun compare(a, b) do if a.order < b.order then return -1 diff --git a/src/ffi/java.nit b/src/ffi/java.nit index 7be54ce..49a880b 100644 --- a/src/ffi/java.nit +++ b/src/ffi/java.nit @@ -96,7 +96,6 @@ class JavaLanguage var jni_signature_alt var return_type - var c_return_type var params = new Array[String] params.add "nit_ffi_jni_env" params.add "java_class" @@ -105,19 +104,16 @@ class JavaLanguage if mproperty.is_init then jni_signature_alt = mclass_type.jni_signature_alt return_type = mclass_type - c_return_type = mclass_type.cname else params.add "recv" if signature.return_mtype != null then var ret_mtype = signature.return_mtype ret_mtype = ret_mtype.resolve_for(mclass_type, mclass_type, mmodule, true) return_type = signature.return_mtype - c_return_type = mclass_type.cname jni_signature_alt = return_type.jni_signature_alt else jni_signature_alt = "Void" return_type = null - c_return_type = null end end @@ -262,16 +258,15 @@ redef class AMethPropdef end end - # Insert additionnal explicit calls to get the current `JNIEnv` + # Insert additional explicit calls to get the current `JNIEnv` # # This forces declaration of callbacks to Nit. The callbacks will be available in Java # but will be used mainly by the FFI itself. # - # The developper can aso customize the JNIEnv used by the FFI by redefing `Sys::jni_env`. + # The developer can also customize the JNIEnv used by the FFI by redefining `Sys::jni_env`. private fun insert_artificial_callbacks(toolcontext: ToolContext) do var fcc = foreign_callbacks - assert fcc != null var modelbuilder = toolcontext.modelbuilder var mmodule = mpropdef.mclassdef.mmodule @@ -450,7 +445,7 @@ redef class MType # Type name in Java # # * Primitives common to both languages use their Java primitive type - # * Nit extern Java classes are reprensented by their full Java type + # * Nit extern Java classes are represented by their full Java type # * Other Nit objects are represented by `int` in Java. It holds the # pointer to the underlying C structure. # TODO create static Java types to store and hide the pointer @@ -502,7 +497,6 @@ redef class MClassType do var ftype = mclass.ftype if ftype isa ForeignJavaType then - var ori_jni_type = jni_type var jni_type = ftype.java_type. replace('.', "/").replace(' ', "").replace('\n', "") diff --git a/src/frontend/simple_misc_analysis.nit b/src/frontend/simple_misc_analysis.nit index a50b45b..9e3775d 100644 --- a/src/frontend/simple_misc_analysis.nit +++ b/src/frontend/simple_misc_analysis.nit @@ -117,17 +117,7 @@ redef class AReturnExpr end end -redef class AContinueExpr - redef fun after_simple_misc(v) - do - var e = n_expr - if e != null then - e.warn_parentheses(v) - end - end -end - -redef class ABreakExpr +redef class AEscapeExpr redef fun after_simple_misc(v) do var e = n_expr diff --git a/src/highlight.nit b/src/highlight.nit index 06075b4..fe5375c 100644 --- a/src/highlight.nit +++ b/src/highlight.nit @@ -52,7 +52,6 @@ class HighlightVisitor do var stack2 = new Array[HTMLTag] var stack = new Array[Prod] - var closes = new Array[Prod] var line = 0 var c: nullable Token = first_token var hv = new HighlightVisitor @@ -932,4 +931,3 @@ redef class AExpr return t.infobox(v) end end - diff --git a/src/interpreter/breakpoint.nit b/src/interpreter/breakpoint.nit deleted file mode 100644 index ef04a3b..0000000 --- a/src/interpreter/breakpoint.nit +++ /dev/null @@ -1,60 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2013 Lucas Bajolet -# -# 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 diff --git a/src/interpreter/debugger.nit b/src/interpreter/debugger.nit index 586c04c..771d4a2 100644 --- a/src/interpreter/debugger.nit +++ b/src/interpreter/debugger.nit @@ -17,7 +17,6 @@ # Debugging of a nit program using the NaiveInterpreter module debugger -import breakpoint intrude import naive_interpreter import nitx intrude import semantize::local_var_init @@ -120,9 +119,9 @@ redef class ToolContext end # -d - var opt_debugger_mode: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d") + var opt_debugger_mode = new OptionBool("Launches the target program with the debugger attached to it", "-d") # -c - var opt_debugger_autorun: OptionBool = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c") + var opt_debugger_autorun = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c") redef init do @@ -167,6 +166,20 @@ redef class ModelBuilder end end +# Contains all the informations of a Breakpoint for the Debugger +class Breakpoint + + # Line to break on + var line: Int + + # File concerned by the breakpoint + var file: String + + redef init do + if not file.has_suffix(".nit") then file += ".nit" + end +end + # The class extending `NaiveInterpreter` by adding debugging methods class Debugger super NaiveInterpreter @@ -257,19 +270,8 @@ class Debugger end # Same as a regular call but for a runtime injected module - # fun rt_call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance do - args = call_commons(mpropdef, args) - return rt_call_without_varargs(mpropdef, args) - end - - # Common code to call and this function - # - # Call only executes the variadic part, this avoids - # double encapsulation of variadic parameters into an Array - fun rt_call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance - do if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then self.discover_call_trace.add mpropdef self.debug("Discovered {mpropdef}") @@ -324,7 +326,6 @@ class Debugger var mmod = e.mmodule if mmod != null then self.mainmodule = mmod - var local_classdefs = mmod.mclassdefs var sys_type = mmod.sys_type if sys_type == null then print "Fatal error, cannot find Class Sys !\nAborting" @@ -379,14 +380,6 @@ class Debugger var breakpoint = find_breakpoint(curr_file, n.location.line_start) if breakpoints.keys.has(curr_file) and breakpoint != null then - - breakpoint.check_in - - if not breakpoint.is_valid - then - remove_breakpoint(curr_file, n.location.line_start) - end - n.debug("Execute stmt {n.to_s}") while read_cmd do end end @@ -399,7 +392,6 @@ class Debugger var identifiers_in_instruction = get_identifiers_in_current_instruction(n.location.text) for i in identifiers_in_instruction do - var variable = seek_variable(i, frame) for j in self.traces do if j.is_variable_traced_in_frame(i, frame) then n.debug("Traced variable {i} used") @@ -465,7 +457,7 @@ class Debugger # # Returns a boolean value, representing whether or not to # continue reading commands from the console input - fun process_debug_command(command:String): Bool + fun process_debug_command(command: String): Bool do # Step-out command if command == "finish" @@ -478,10 +470,17 @@ class Debugger # Step-over command else if command == "n" then return step_over + # Shows help + else if command == "help" then + help + return true # Opens a new NitIndex prompt on current model else if command == "nitx" then new NitIndex.with_infos(modelbuilder, self.mainmodule).prompt return true + else if command == "bt" or command == "backtrack" then + print stack_trace + return true # Continues execution until the end else if command == "c" then return continue_exec @@ -503,41 +502,72 @@ class Debugger print stack_trace exit(0) else - var parts_of_command = command.split_with(' ') + var parts = command.split_with(' ') + var cname = parts.first # Shows the value of a variable in the current frame - if parts_of_command[0] == "p" or parts_of_command[0] == "print" then - print_command(parts_of_command) + if cname == "p" or cname == "print" then + print_command(parts) # Places a breakpoint on line x of file y - else if parts_of_command[0] == "break" or parts_of_command[0] == "b" - then - process_place_break_fun(parts_of_command) - # Places a temporary breakpoint on line x of file y - else if parts_of_command[0] == "tbreak" and (parts_of_command.length == 2 or parts_of_command.length == 3) - then - process_place_tbreak_fun(parts_of_command) + else if cname == "break" or cname == "b" then + process_place_break_fun(parts) # Removes a breakpoint on line x of file y - else if parts_of_command[0] == "d" or parts_of_command[0] == "delete" then - process_remove_break_fun(parts_of_command) + else if cname == "d" or cname == "delete" then + process_remove_break_fun(parts) # Sets an alias for a variable - else if parts_of_command.length == 3 and parts_of_command[1] == "as" - then - add_alias(parts_of_command[0], parts_of_command[2]) + else if parts.length == 2 and parts[1] == "as" then + process_alias(parts) # Modifies the value of a variable in the current frame - else if parts_of_command.length >= 3 and parts_of_command[1] == "=" then - process_mod_function(parts_of_command) + else if parts.length == 3 and parts[1] == "=" then + process_mod_function(parts) # Traces the modifications on a variable - else if parts_of_command.length >= 2 and parts_of_command[0] == "trace" then - process_trace_command(parts_of_command) + else if cname == "trace" then + process_trace_command(parts) # Untraces the modifications on a variable - else if parts_of_command.length == 2 and parts_of_command[0] == "untrace" then - process_untrace_command(parts_of_command) + else if cname == "untrace" then + process_untrace_command(parts) else - print "Unknown command \"{command}\"" + bad_command(command) end end return true end + # Produces help for the commands of the debugger + fun help do + print "" + print "Help :" + print "-----------------------------------" + print "" + print "Variables" + print " * Modification: var_name = value (Warning: var_name must be primitive)" + print " * Alias: var_name as alias" + print "" + print "Printing" + print " * Variables: p(rint) var_name (Use * to print all local variables)" + print " * Collections: p(rint) var_name '[' start_index (.. end_index) ']'" + print "" + print "Breakpoints" + print " * File/line: b(reak) file_name line_number" + print " * Remove: d(elete) id" + print "" + print "Tracepoints" + print " * Variable: trace var_name break/print" + print " * Untrace variable: untrace var_name" + print "" + print "Flow control" + print " * Next instruction (same-level): n" + print " * Next instruction: s" + print " * Finish current method: finish" + print " * Continue until next breakpoint or end: c" + print "" + print "General commands" + print " * quit: Quits the debugger" + print " * abort: Aborts the interpretation, prints the stack trace before leaving" + print " * nitx: Ask questions to the model about its entities (classes, methods, etc.)" + print " * nit: Inject dynamic code for interpretation" + print "" + end + ####################################################################### ## Processing specific command functions ## ####################################################################### @@ -580,75 +610,96 @@ class Debugger return false end + fun bad_command(cmd: String) do + print "Unrecognized command {cmd}. Use 'help' to show help." + end + # Prints the demanded variable in the command # # The name of the variable in in position 1 of the array 'parts_of_command' - fun print_command(parts_of_command: Array[String]) + fun print_command(parts: Array[String]) do - if parts_of_command[1] == "*" then + if parts.length != 2 then + bad_command(parts.join(" ")) + return + end + if parts[1] == "*" then var map_of_instances = frame.map - var keys = map_of_instances.iterator - var self_var = seek_variable("self", frame) print "self: {self_var.to_s}" for instance in map_of_instances.keys do print "{instance.to_s}: {map_of_instances[instance].to_s}" end - else if parts_of_command[1] == "stack" then - print self.stack_trace - else if parts_of_command[1].chars.has('[') and parts_of_command[1].chars.has(']') then - process_array_command(parts_of_command) + else if parts[1].chars.has('[') and parts[1].chars.has(']') then + process_array_command(parts) else - var instance = seek_variable(get_real_variable_name(parts_of_command[1]), frame) + var instance = seek_variable(get_real_variable_name(parts[1]), frame) if instance != null then print_instance(instance) else - print "Cannot find variable {parts_of_command[1]}" + print "Cannot find variable {parts[1]}" end end end + # Process the input command to set an alias for a variable + fun process_alias(parts: Array[String]) do + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + add_alias(parts.first, parts.last) + end + # Processes the input string to know where to put a breakpoint - fun process_place_break_fun(parts_of_command: Array[String]) + fun process_place_break_fun(parts: Array[String]) do - var bp = get_breakpoint_from_command(parts_of_command) + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + var bp = get_breakpoint_from_command(parts) if bp != null then place_breakpoint(bp) end end # Returns a breakpoint containing the informations stored in the command - fun get_breakpoint_from_command(parts_of_command: Array[String]): nullable Breakpoint + fun get_breakpoint_from_command(parts: Array[String]): nullable Breakpoint do - if parts_of_command[1].is_numeric then - return new Breakpoint(parts_of_command[1].to_i, curr_file) - else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then - return new Breakpoint(parts_of_command[2].to_i, parts_of_command[1]) + if parts[1].is_numeric then + return new Breakpoint(parts[1].to_i, curr_file) + else if parts.length >= 3 and parts[2].is_numeric then + return new Breakpoint(parts[2].to_i, parts[1]) else return null end end # Processes the command of removing a breakpoint on specified line and file - fun process_remove_break_fun(parts_of_command: Array[String]) + fun process_remove_break_fun(parts: Array[String]) do - if parts_of_command[1].is_numeric then - remove_breakpoint(self.curr_file, parts_of_command[1].to_i) - else if parts_of_command.length >= 3 and parts_of_command[2].is_numeric then - remove_breakpoint(parts_of_command[1], parts_of_command[2].to_i) + if parts.length != 2 then + bad_command(parts.join(" ")) + return + end + if parts[1].is_numeric then + remove_breakpoint(self.curr_file, parts[1].to_i) + else if parts.length >= 3 and parts[2].is_numeric then + remove_breakpoint(parts[1], parts[2].to_i) end end # Processes an array print command - fun process_array_command(parts_of_command: Array[String]) + fun process_array_command(parts: Array[String]) do - var index_of_first_brace = parts_of_command[1].chars.index_of('[') - var variable_name = get_real_variable_name(parts_of_command[1].substring(0,index_of_first_brace)) - var braces = parts_of_command[1].substring_from(index_of_first_brace) + var index_of_first_brace = parts[1].chars.index_of('[') + var variable_name = get_real_variable_name(parts[1].substring(0,index_of_first_brace)) + var braces = parts[1].substring_from(index_of_first_brace) var indexes = remove_braces(braces) @@ -676,27 +727,32 @@ class Debugger # Processes the modification function to modify a variable dynamically # # Command of type variable = value - fun process_mod_function(parts_of_command: Array[String]) + fun process_mod_function(parts: Array[String]) do - parts_of_command[0] = get_real_variable_name(parts_of_command[0]) - var parts_of_variable = parts_of_command[0].split_with(".") + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + var p0 = parts[0] + p0 = get_real_variable_name(p0) + var parts_of_variable = p0.split_with(".") if parts_of_variable.length > 1 then var last_part = parts_of_variable.pop - var first_part = parts_of_command[0].substring(0,parts_of_command[0].length - last_part.length - 1) + var first_part = p0.substring(0,p0.length - last_part.length - 1) var papa = seek_variable(first_part, frame) if papa != null and papa isa MutableInstance then var attribute = get_attribute_in_mutable_instance(papa, last_part) if attribute != null then - modify_argument_of_complex_type(papa, attribute, parts_of_command[2]) + modify_argument_of_complex_type(papa, attribute, parts[2]) end end else var target = seek_variable(parts_of_variable[0], frame) if target != null then - modify_in_frame(target, parts_of_command[2]) + modify_in_frame(target, parts[2]) end end end @@ -704,42 +760,46 @@ class Debugger # Processes the untrace variable command # # Command pattern : "untrace variable" - fun process_untrace_command(parts_of_command: Array[String]) + fun process_untrace_command(parts: Array[String]) do - var variable_name = get_real_variable_name(parts_of_command[1]) + if parts.length != 2 then + bad_command(parts.join(" ")) + return + end + var variable_name = get_real_variable_name(parts[1]) if untrace_variable(variable_name) then - print "Untraced variable {parts_of_command[1]}" + print "Untraced variable {parts[1]}" else - print "{parts_of_command[1]} is not traced" + print "{parts[1]} is not traced" end end # Processes the trace variable command # # Command pattern : "trace variable [break/print]" - fun process_trace_command(parts_of_command: Array[String]) + fun process_trace_command(parts: Array[String]) do - var variable_name = get_real_variable_name(parts_of_command[1]) + if parts.length != 3 then + bad_command(parts.join(" ")) + return + end + var variable_name = get_real_variable_name(parts[1]) var breaker:Bool if seek_variable(variable_name, frame) == null then - print "Cannot find a variable called {parts_of_command[1]}" + print "Cannot find a variable called {parts[1]}" return end - if parts_of_command.length == 3 then - if parts_of_command[2] == "break" then - breaker = true - else - breaker = false - end + if parts[2] == "break" then + breaker = true else breaker = false end trace_variable(variable_name, breaker) - print "Successfully tracing {parts_of_command[1]}" + print "Successfully tracing {parts[1]}" end ####################################################################### @@ -832,14 +892,12 @@ class Debugger var trigger_char_escape = false var trigger_string_escape = false - var trigger_concat_in_string = false for i in instruction.chars do if trigger_char_escape then if i == '\'' then trigger_char_escape = false else if trigger_string_escape then if i == '{' then - trigger_concat_in_string = true trigger_string_escape = false else if i == '\"' then trigger_string_escape = false else @@ -857,7 +915,6 @@ class Debugger else if i == '\"' then trigger_string_escape = true else if i == '}' then - trigger_concat_in_string = false trigger_string_escape = true else if instruction_buffer.length > 0 and not instruction_buffer.is_numeric and not (instruction_buffer.chars[0] >= 'A' and instruction_buffer.chars[0] <= 'Z') then result_array.push(instruction_buffer.to_s) @@ -1084,8 +1141,6 @@ class Debugger do var collection_length_attribute = get_attribute_in_mutable_instance(collection, "length") - var real_collection_length: nullable Int = null - if collection_length_attribute != null then var primitive_length_instance = collection.attributes[collection_length_attribute] if primitive_length_instance isa PrimitiveInstance[Int] then @@ -1215,17 +1270,6 @@ class Debugger end end - #Places a breakpoint that will trigger once and be destroyed afterwards - fun process_place_tbreak_fun(parts_of_command: Array[String]) - do - var bp = get_breakpoint_from_command(parts_of_command) - if bp != null - then - bp.set_max_breaks(1) - place_breakpoint(bp) - end - end - ####################################################################### ## Breakpoint removing functions ## ####################################################################### diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index 1121ed0..e16f060 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -20,10 +20,11 @@ module naive_interpreter import literal import semantize private import parser::tables +import mixin redef class ToolContext # --discover-call-trace - var opt_discover_call_trace: OptionBool = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace") + var opt_discover_call_trace = new OptionBool("Trace calls of the first invocation of a method", "--discover-call-trace") redef init do @@ -57,7 +58,7 @@ class NaiveInterpreter # The modelbuilder that know the AST and its associations with the model var modelbuilder: ModelBuilder - # The main moduleof the program (used to lookup methoda + # The main module of the program (used to lookup method) var mainmodule: MModule # The command line arguments of the interpreted program @@ -65,6 +66,7 @@ class NaiveInterpreter # arguments[1] is the first argument var arguments: Array[String] + # The main Sys instance var mainobj: nullable Instance init(modelbuilder: ModelBuilder, mainmodule: MModule, arguments: Array[String]) @@ -112,43 +114,26 @@ class NaiveInterpreter # Set this mark to skip the evaluation until the end of the specified method frame var returnmark: nullable Frame = null - # Is a break executed? - # Set this mark to skip the evaluation until a labeled statement catch it with `is_break` - var breakmark: nullable EscapeMark = null - - # Is a continue executed? - # Set this mark to skip the evaluation until a labeled statement catch it with `is_continue` - var continuemark: nullable EscapeMark = null + # Is a break or a continue executed? + # Set this mark to skip the evaluation until a labeled statement catch it with `is_escape` + var escapemark: nullable EscapeMark = null # Is a return or a break or a continue executed? # Use this function to know if you must skip the evaluation of statements - fun is_escaping: Bool do return returnmark != null or breakmark != null or continuemark != null + fun is_escaping: Bool do return returnmark != null or escapemark != null # The value associated with the current return/break/continue, if any. # Set the value when you set a escapemark. # Read the value when you catch a mark or reach the end of a method var escapevalue: nullable Instance = null - # If there is a break and is associated with `escapemark`, then return true an clear the mark. - # If there is no break or if `escapemark` is null then return false. - # Use this function to catch a potential break. - fun is_break(escapemark: nullable EscapeMark): Bool - do - if escapemark != null and self.breakmark == escapemark then - self.breakmark = null - return true - else - return false - end - end - - # If there is a continue and is associated with `escapemark`, then return true an clear the mark. - # If there is no continue or if `escapemark` is null then return false. - # Use this function to catch a potential continue. - fun is_continue(escapemark: nullable EscapeMark): Bool + # If there is a break/continue and is associated with `escapemark`, then return true and clear the mark. + # If there is no break/continue or if `escapemark` is null then return false. + # Use this function to catch a potential break/continue. + fun is_escape(escapemark: nullable EscapeMark): Bool do - if escapemark != null and self.continuemark == escapemark then - self.continuemark = null + if escapemark != null and self.escapemark == escapemark then + self.escapemark = null return true else return false @@ -225,13 +210,13 @@ class NaiveInterpreter return new PrimitiveInstance[Float](ic.mclass_type, val) end - # The unique intance of the `true` value. + # The unique instance of the `true` value. var true_instance: Instance - # The unique intance of the `false` value. + # The unique instance of the `false` value. var false_instance: Instance - # The unique intance of the `null` value. + # The unique instance of the `null` value. var null_instance: Instance # Return a new array made of `values`. @@ -247,6 +232,19 @@ class NaiveInterpreter return res end + fun value_instance(object: Object): Instance + do + if object isa Int then + return int_instance(object) + else if object isa Bool then + return bool_instance(object) + else if object isa String then + return string_instance(object) + else + abort + end + end + # Return a new native string initialized with `txt` fun native_string_instance(txt: String): Instance do @@ -256,13 +254,22 @@ class NaiveInterpreter return new PrimitiveInstance[Buffer](ic.mclass_type, val) end + # Return a new String instance for `txt` + fun string_instance(txt: String): Instance + do + var nat = native_string_instance(txt) + var res = self.send(self.force_get_primitive_method("to_s_with_length", nat.mtype), [nat, self.int_instance(txt.length)]) + assert res != null + return res + end + # The current frame used to store local variables of the current method executed fun frame: Frame do return frames.first # The stack of all frames. The first one is the current one. - var frames: List[Frame] = new List[Frame] + var frames = new List[Frame] - # Return a stack stace. One line per function + # Return a stack trace. One line per function fun stack_trace: String do var b = new FlatBuffer @@ -309,55 +316,58 @@ class NaiveInterpreter f.map[v] = value end - # Store known method, used to trace methods as thez are reached + # Store known methods, used to trace methods as they are reached var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef] - # Common code for calls to injected methods and normal methods - fun call_commons(mpropdef: MMethodDef, args: Array[Instance]): Array[Instance] + # Evaluate `args` as expressions in the call of `mpropdef` on `recv`. + # This method is used to manage varargs in signatures and returns the real array + # of instances to use in the call. + # Return `null` if one of the evaluation of the arguments return null. + fun varargize(mpropdef: MMethodDef, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance] do - var vararg_rank = mpropdef.msignature.vararg_rank - if vararg_rank >= 0 then - assert args.length >= mpropdef.msignature.arity + 1 # because of self - var rawargs = args - args = new Array[Instance] + var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null) + var res = new Array[Instance] + res.add(recv) - args.add(rawargs.first) # recv + if args.is_empty then return res - for i in [0..vararg_rank[ do - args.add(rawargs[i+1]) - end - - var vararg_lastrank = vararg_rank + rawargs.length-1-mpropdef.msignature.arity - var vararg = new Array[Instance] - for i in [vararg_rank..vararg_lastrank] do - vararg.add(rawargs[i+1]) - end - # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis - var elttype = mpropdef.msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, args.first.mtype.as(MClassType)) - args.add(self.array_instance(vararg, elttype)) + var vararg_rank = msignature.vararg_rank + var vararg_len = args.length - msignature.arity + if vararg_len < 0 then vararg_len = 0 - for i in [vararg_lastrank+1..rawargs.length-1[ do - args.add(rawargs[i+1]) + for i in [0..msignature.arity[ do + if i == vararg_rank then + var ne = args[i] + if ne isa AVarargExpr then + var e = self.expr(ne.n_expr) + if e == null then return null + res.add(e) + continue + end + var vararg = new Array[Instance] + for j in [vararg_rank..vararg_rank+vararg_len] do + var e = self.expr(args[j]) + if e == null then return null + vararg.add(e) + end + var elttype = msignature.mparameters[vararg_rank].mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType)) + res.add(self.array_instance(vararg, elttype)) + else + var j = i + if i > vararg_rank then j += vararg_len + var e = self.expr(args[j]) + if e == null then return null + res.add(e) end end - return args + return res end # Execute `mpropdef` for a `args` (where `args[0]` is the receiver). - # Return a falue if `mpropdef` is a function, or null if it is a procedure. - # The call is direct/static. There is no message-seding/late-binding. + # Return a value if `mpropdef` is a function, or null if it is a procedure. + # The call is direct/static. There is no message-sending/late-binding. fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance do - args = call_commons(mpropdef, args) - return call_without_varargs(mpropdef, args) - end - - # Common code to call and this function - # - # Call only executes the variadic part, this avoids - # double encapsulation of variadic parameters into an Array - fun call_without_varargs(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance - do if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then self.discover_call_trace.add mpropdef self.debug("Discovered {mpropdef}") @@ -366,6 +376,7 @@ class NaiveInterpreter # Look for the AST node that implements the property var mproperty = mpropdef.mproperty + var val = mpropdef.constant_value if self.modelbuilder.mpropdef2npropdef.has_key(mpropdef) then var npropdef = self.modelbuilder.mpropdef2npropdef[mpropdef] self.parameter_check(npropdef, mpropdef, args) @@ -374,6 +385,8 @@ class NaiveInterpreter var nclassdef = self.modelbuilder.mclassdef2nclassdef[mpropdef.mclassdef] self.parameter_check(nclassdef, mpropdef, args) return nclassdef.call(self, mpropdef, args) + else if val != null then + return value_instance(val) else fatal("Fatal Error: method {mpropdef} not found in the AST") abort @@ -418,7 +431,7 @@ class NaiveInterpreter end # Execute a full `callsite` for given `args` - # Use this method, instead of `send` to execute and control the aditionnal behavior of the call-sites + # Use this method, instead of `send` to execute and control the additional behavior of the call-sites fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance do var initializers = callsite.mpropdef.initializers @@ -447,8 +460,8 @@ class NaiveInterpreter end # Execute `mproperty` for a `args` (where `args[0]` is the receiver). - # Return a falue if `mproperty` is a function, or null if it is a procedure. - # The call is polimotphic. There is a message-seding/late-bindng according to te receiver (args[0]). + # Return a value if `mproperty` is a function, or null if it is a procedure. + # The call is polymorphic. There is a message-sending/late-binding according to the receiver (args[0]). fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance do var recv = args.first @@ -495,6 +508,7 @@ class NaiveInterpreter var cds = mtype.collect_mclassdefs(self.mainmodule).to_a self.mainmodule.linearize_mclassdefs(cds) for cd in cds do + if not self.modelbuilder.mclassdef2nclassdef.has_key(cd) then continue var n = self.modelbuilder.mclassdef2nclassdef[cd] for npropdef in n.n_propdefs do if npropdef isa AAttrPropdef then @@ -507,7 +521,7 @@ class NaiveInterpreter return res end - var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]] + private var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]] # Fill the initial values of the newly created instance `recv`. # `recv.mtype` is used to know what must be filled. @@ -694,7 +708,7 @@ redef class AMethPropdef if auto_super_call then # standard call-next-method var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype) - v.call_without_varargs(superpd, arguments) + v.call(superpd, arguments) end if mpropdef.is_intern or mpropdef.is_extern then @@ -743,14 +757,14 @@ redef class AMethPropdef var txt = recv.mtype.to_s return v.native_string_instance(txt) else if pname == "==" then - # == is correclt redefined for instances + # == is correctly redefined for instances return v.bool_instance(args[0] == args[1]) else if pname == "!=" then return v.bool_instance(args[0] != args[1]) else if pname == "is_same_type" then return v.bool_instance(args[0].mtype == args[1].mtype) else if pname == "is_same_instance" then - return v.bool_instance(args[1] != null and args[0].eq_is(args[1])) + return v.bool_instance(args[0].eq_is(args[1])) else if pname == "exit" then exit(args[1].to_i) abort @@ -797,6 +811,8 @@ redef class AMethPropdef return v.int_instance(args[0].to_i.bin_or(args[1].to_i)) else if pname == "bin_xor" then return v.int_instance(args[0].to_i.bin_xor(args[1].to_i)) + else if pname == "bin_not" then + return v.int_instance(args[0].to_i.bin_not) else if pname == "native_int_to_s" then return v.native_string_instance(recvval.to_s) else if pname == "strerror_ext" then @@ -1097,7 +1113,7 @@ redef class AClassdef if not mpropdef.is_intro then # standard call-next-method var superpd = mpropdef.lookup_next_definition(v.mainmodule, args.first.mtype) - v.call_without_varargs(superpd, args) + v.call(superpd, args) end return null else @@ -1197,7 +1213,7 @@ redef class ASelfExpr end end -redef class AContinueExpr +redef class AEscapeExpr redef fun stmt(v) do var ne = self.n_expr @@ -1206,20 +1222,7 @@ redef class AContinueExpr if i == null then return v.escapevalue = i end - v.continuemark = self.escapemark - end -end - -redef class ABreakExpr - redef fun stmt(v) - do - var ne = self.n_expr - if ne != null then - var i = v.expr(ne) - if i == null then return - v.escapevalue = i - end - v.breakmark = self.escapemark + v.escapemark = self.escapemark end end @@ -1285,7 +1288,7 @@ redef class ADoExpr redef fun stmt(v) do v.stmt(self.n_block) - v.is_break(self.escapemark) # Clear the break (if any) + v.is_escape(self.break_mark) # Clear the break (if any) end end @@ -1297,8 +1300,8 @@ redef class AWhileExpr if cond == null then return if not cond.is_true then return v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break + if v.is_escape(self.break_mark) then return + v.is_escape(self.continue_mark) # Clear the break if v.is_escaping then return end end @@ -1309,8 +1312,8 @@ redef class ALoopExpr do loop v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break + if v.is_escape(self.break_mark) then return + v.is_escape(self.continue_mark) # Clear the break if v.is_escaping then return end end @@ -1328,7 +1331,7 @@ redef class AForExpr #self.debug("iter {iter}") loop var isok = v.callsite(method_is_ok, [iter]).as(not null) - if not isok.is_true then return + if not isok.is_true then break if self.variables.length == 1 then var item = v.callsite(method_item, [iter]).as(not null) #self.debug("item {item}") @@ -1342,11 +1345,15 @@ redef class AForExpr abort end v.stmt(self.n_block) - if v.is_break(self.escapemark) then return - v.is_continue(self.escapemark) # Clear the break - if v.is_escaping then return + if v.is_escape(self.break_mark) then break + v.is_escape(self.continue_mark) # Clear the break + if v.is_escaping then break v.callsite(method_next, [iter]) end + var method_finish = self.method_finish + if method_finish != null then + v.callsite(method_finish, [iter]) + end end end @@ -1458,9 +1465,7 @@ redef class AStringFormExpr redef fun expr(v) do var txt = self.value.as(not null) - var nat = v.native_string_instance(txt) - var res = v.send(v.force_get_primitive_method("to_s", nat.mtype), [nat]).as(not null) - return res + return v.string_instance(txt) end end @@ -1560,7 +1565,6 @@ redef class AAsNotnullExpr do var i = v.expr(self.n_expr) if i == null then return null - var mtype = v.unanchor_type(self.mtype.as(not null)) if i.mtype isa MNullType then fatal(v, "Cast failed") end @@ -1594,12 +1598,8 @@ redef class ASendExpr do var recv = v.expr(self.n_expr) if recv == null then return null - var args = [recv] - for a in self.raw_arguments do - var i = v.expr(a) - if i == null then return null - args.add(i) - end + var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + if args == null then return null var res = v.callsite(callsite, args) return res @@ -1611,12 +1611,8 @@ redef class ASendReassignFormExpr do var recv = v.expr(self.n_expr) if recv == null then return - var args = [recv] - for a in self.raw_arguments do - var i = v.expr(a) - if i == null then return - args.add(i) - end + var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments) + if args == null then return var value = v.expr(self.n_value) if value == null then return @@ -1636,16 +1632,12 @@ redef class ASuperExpr redef fun expr(v) do var recv = v.frame.arguments.first - var args = [recv] - for a in self.n_args.n_exprs do - var i = v.expr(a) - if i == null then return null - args.add(i) - end var callsite = self.callsite if callsite != null then - # Add additionnals arguments for the super init call + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + if args == null then return null + # Add additional arguments for the super init call if args.length == 1 then for i in [0..callsite.msignature.arity[ do args.add(v.frame.arguments[i+1]) @@ -1656,14 +1648,17 @@ redef class ASuperExpr return res end + # standard call-next-method + var mpropdef = self.mpropdef + mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype) + + var args = v.varargize(mpropdef, recv, self.n_args.n_exprs) + if args == null then return null + if args.length == 1 then args = v.frame.arguments end - - # stantard call-next-method - var mpropdef = self.mpropdef - mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype) - var res = v.call_without_varargs(mpropdef, args) + var res = v.call(mpropdef, args) return res end end @@ -1674,12 +1669,8 @@ redef class ANewExpr var mtype = v.unanchor_type(self.mtype.as(not null)) var recv: Instance = new MutableInstance(mtype) v.init_instance(recv) - var args = [recv] - for a in self.n_args.n_exprs do - var i = v.expr(a) - if i == null then return null - args.add(i) - end + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) + if args == null then return null var res2 = v.callsite(callsite, args) if res2 != null then #self.debug("got {res2} from {mproperty}. drop {recv}") diff --git a/src/metrics/tables_metrics.nit b/src/metrics/tables_metrics.nit index 664415a..eac5853 100644 --- a/src/metrics/tables_metrics.nit +++ b/src/metrics/tables_metrics.nit @@ -35,8 +35,6 @@ end # Print class tables metrics for the classes of the program main fun compute_tables_metrics(main: MModule) do - var model = main.model - var nc = 0 # Number of runtime classes var nl = 0 # Number of usages of class definitions (a class definition can be used more than once) var nhp = 0 # Number of usages of properties (a property can be used more than once) diff --git a/src/mixin.nit b/src/mixin.nit new file mode 100644 index 0000000..94d0c32 --- /dev/null +++ b/src/mixin.nit @@ -0,0 +1,110 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Loading and additional module refinements at link-time. +# +# Used to factorize some code used by the engines. +module mixin + +import modelbuilder + +redef class ToolContext + # --mixin + var opt_mixins = new OptionArray("Additionals module to min-in", "-m", "--mixin") + # --define + var opt_defines = new OptionArray("Define a specific property", "-D", "--define") + + redef init + do + super + option_context.add_option(opt_mixins, opt_defines) + end + + redef fun make_main_module(mmodules) + do + var mixins = opt_mixins.value + if not mixins.is_empty then + mmodules.add_all modelbuilder.parse(opt_mixins.value) + modelbuilder.run_phases + end + + var mainmodule = super + + var defines = opt_defines.value + if not defines.is_empty then + var location = mainmodule.location + var model = mainmodule.model + + if mainmodule == mmodules.first then + mainmodule = new MModule(model, null, mainmodule.name + "-d", location) + mainmodule.set_imported_mmodules(mmodules) + mainmodule.is_fictive = true + end + + var recv = mainmodule.object_type + var mclassdef = new MClassDef(mainmodule, recv, location) + mclassdef.add_in_hierarchy + + for define in defines do + var spl = define.split_once_on('=') + var name = spl.first + var val = null + if spl.length > 1 then val = spl[1] + var prop = mainmodule.try_get_primitive_method(name, recv.mclass) + if prop == null then + error(null, "Error: --define: no top-level function `{name}`") + continue + end + var ret = prop.intro.msignature.return_mtype + var v + if ret == null then + error(null, "Error: --define: Method `{prop}` is not a function") + continue + else if ret.to_s == "Bool" then + if val == null or val == "true" then + v = true + else if val == "false" then + v = false + else + error(null, "Error: --define: Method `{prop}` need a Bool.") + continue + end + else if ret.to_s == "Int" then + if val != null and val.is_numeric then + v = val.to_i + else + error(null, "Error: --define: Method `{prop}` need a Int.") + continue + end + else if ret.to_s == "String" then + if val != null then + v = val + else + error(null, "Error: --define: Method `{prop}` need a String.") + continue + end + else + error(null, "Error: --define: Method `{prop}` return an unmanaged type {ret}.") + continue + end + var pd = new MMethodDef(mclassdef, prop, location) + pd.msignature = prop.intro.msignature + pd.constant_value = v + end + check_errors + end + + return mainmodule + end +end diff --git a/src/model/model.nit b/src/model/model.nit index f989302..64e82c7 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -293,7 +293,8 @@ redef class MModule end private class MClassDefSorter - super AbstractSorter[MClassDef] + super Comparator + redef type COMPARED: MClassDef var mmodule: MModule redef fun compare(a, b) do @@ -305,7 +306,8 @@ private class MClassDefSorter end private class MPropDefSorter - super AbstractSorter[MPropDef] + super Comparator + redef type COMPARED: MPropDef var mmodule: MModule redef fun compare(pa, pb) do @@ -1224,7 +1226,6 @@ class MVirtualType if is_fixed(mmodule, resolved_reciever) then return res # If the resolved type isa intern class, then there is no possible valid redefinition in any potential subclass. self is just fixed. so simply return the resolution if res isa MClassType and res.mclass.kind == enum_kind then return res - # TODO: Add 'fixed' virtual type in the specification. # TODO: What if bound to a MParameterType? # Note that Nullable types can always be redefined by the non nullable version, so there is no specific case on it. @@ -1273,7 +1274,6 @@ end # # Note that parameter types are shared among class refinements. # Therefore parameter only have an internal name (see `to_s` for details). -# TODO: Add a `name_for` to get better messages. class MParameterType super MType @@ -1975,6 +1975,16 @@ class MMethodDef # Is the method definition extern? var is_extern = false is writable + + # An optional constant value returned in functions. + # + # Only some specific primitife value are accepted by engines. + # Is used when there is no better implementation available. + # + # Currently used only for the implementation of the `--define` + # command-line option. + # SEE: module `mixin`. + var constant_value: nullable Object = null is writable end # A local definition of an attribute diff --git a/src/model/model_viz.nit b/src/model/model_viz.nit index bc324b7..03ca3eb 100644 --- a/src/model/model_viz.nit +++ b/src/model/model_viz.nit @@ -62,16 +62,19 @@ end # Compare modules and groups using the # FIXME do not use Object, but a better common interface of MModule and MGroup private class LinexComparator - super Comparator[Object] + super Comparator + + redef type COMPARED: Object + var mins = new HashMap [MGroup, nullable MModule] var maxs = new HashMap [MGroup, nullable MModule] - fun min(o: Object): nullable MModule do + fun mini(o: Object): nullable MModule do if o isa MModule then return o assert o isa MGroup if not mins.has_key(o) then computeminmax(o) return mins[o] end - fun max(o: Object): nullable MModule do + fun maxi(o: Object): nullable MModule do if o isa MModule then return o assert o isa MGroup if not maxs.has_key(o) then computeminmax(o) @@ -84,14 +87,14 @@ private class LinexComparator return end var subs = tree.sub[o] - var minres = min(subs.first) - var maxres = max(subs.first) + var minres = mini(subs.first) + var maxres = maxi(subs.first) var order = minres.model.mmodule_importation_hierarchy for o2 in subs do - var c = min(o2) + var c = mini(o2) if c == null then continue if minres == null or order.compare(minres, c) > 0 then minres = c - c = max(o2) + c = maxi(o2) if c == null then continue if maxres == null or order.compare(maxres, c) < 0 then maxres = c end @@ -100,8 +103,8 @@ private class LinexComparator #if minres != maxres then print "* {o} {minres}..{maxres}" end redef fun compare(a,b) do - var ma = min(a) - var mb = min(b) + var ma = mini(a) + var mb = mini(b) if ma == null then if mb == null then return 0 else return -1 else if mb == null then diff --git a/src/model_utils.nit b/src/model_utils.nit index ff1a8d9..9297793 100644 --- a/src/model_utils.nit +++ b/src/model_utils.nit @@ -525,9 +525,9 @@ end # Sort mentities by their name class MEntityNameSorter - super AbstractSorter[MEntity] + super Comparator + redef type COMPARED: MEntity redef fun compare(a, b) do return a.name <=> b.name - init do end end # Sort MConcerns based on the module importation hierarchy ranking @@ -540,9 +540,8 @@ end # If both `a` and `b` have the same ranking, # ordering is based on lexicographic comparison of `a.name` and `b.name` class MConcernRankSorter - super AbstractSorter[MConcern] - - init do end + super Comparator + redef type COMPARED: MConcern redef fun compare(a, b) do if a.concern_rank == b.concern_rank then diff --git a/src/modelbuilder.nit b/src/modelbuilder.nit index d702369..30a0617 100644 --- a/src/modelbuilder.nit +++ b/src/modelbuilder.nit @@ -53,8 +53,8 @@ redef class ToolContext private var modelbuilder_real: nullable ModelBuilder = null - # Run `process_mainmodule` on all phases - fun run_global_phases(mmodules: Array[MModule]) + # Combine module to make a single one if required. + fun make_main_module(mmodules: Array[MModule]): MModule do assert not mmodules.is_empty var mainmodule @@ -62,10 +62,17 @@ redef class ToolContext mainmodule = mmodules.first else # We need a main module, so we build it by importing all modules - mainmodule = new MModule(modelbuilder.model, null, mmodules.first.name, new Location(mmodules.first.location.file, 0, 0, 0, 0)) + mainmodule = new MModule(modelbuilder.model, null, mmodules.first.name + "-m", new Location(mmodules.first.location.file, 0, 0, 0, 0)) mainmodule.is_fictive = true mainmodule.set_imported_mmodules(mmodules) end + return mainmodule + end + + # Run `process_mainmodule` on all phases + fun run_global_phases(mmodules: Array[MModule]) + do + var mainmodule = make_main_module(mmodules) for phase in phases_list do if phase.disabled then continue phase.process_mainmodule(mainmodule, mmodules) @@ -738,11 +745,13 @@ class ModelBuilder end # Force to get the primitive method named `name` on the type `recv` or do a fatal error on `n` - fun force_get_primitive_method(n: ANode, name: String, recv: MClass, mmodule: MModule): MMethod + fun force_get_primitive_method(n: nullable ANode, name: String, recv: MClass, mmodule: MModule): MMethod do var res = mmodule.try_get_primitive_method(name, recv) if res == null then - self.toolcontext.fatal_error(n.hot_location, "Fatal Error: {recv} must have a property named {name}.") + var l = null + if n != null then l = n.hot_location + self.toolcontext.fatal_error(l, "Fatal Error: {recv} must have a property named {name}.") abort end return res diff --git a/src/modelize/modelize_class.nit b/src/modelize/modelize_class.nit index 2a5e9fb..441a6c2 100644 --- a/src/modelize/modelize_class.nit +++ b/src/modelize/modelize_class.nit @@ -258,7 +258,6 @@ redef class ModelBuilder private fun check_supertypes(nmodule: AModule, nclassdef: AClassdef) do var mmodule = nmodule.mmodule.as(not null) - var objectclass = try_get_mclass_by_name(nmodule, mmodule, "Object") var mclass = nclassdef.mclass.as(not null) var mclassdef = nclassdef.mclassdef.as(not null) @@ -410,7 +409,7 @@ redef class ModelBuilder # Register the nclassdef associated to each mclassdef # FIXME: why not refine the `MClassDef` class with a nullable attribute? - var mclassdef2nclassdef: HashMap[MClassDef, AClassdef] = new HashMap[MClassDef, AClassdef] + var mclassdef2nclassdef = new HashMap[MClassDef, AClassdef] # Return the static type associated to the node `ntype`. # `mmodule` and `mclassdef` is the context where the call is made (used to understand formal types) diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index 74e4932..6a34e2e 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -38,7 +38,7 @@ end redef class ModelBuilder # Register the npropdef associated to each mpropdef # FIXME: why not refine the `MPropDef` class with a nullable attribute? - var mpropdef2npropdef: HashMap[MPropDef, APropdef] = new HashMap[MPropDef, APropdef] + var mpropdef2npropdef = new HashMap[MPropDef, APropdef] # Build the properties of `nclassdef`. # REQUIRE: all superclasses are built. @@ -202,6 +202,7 @@ redef class ModelBuilder # Can we just inherit? if spropdefs.length == 1 and mparameters.is_empty and defined_init == null then self.toolcontext.info("{mclassdef} inherits the basic constructor {longest}", 3) + mclassdef.mclass.root_init = longest return end @@ -217,6 +218,7 @@ redef class ModelBuilder var msignature = new MSignature(mparameters, null) defined_init.new_msignature = msignature self.toolcontext.info("{mclassdef} extends its basic constructor signature to {defined_init}{msignature}", 3) + mclassdef.mclass.root_init = defined_init return end @@ -230,6 +232,7 @@ redef class ModelBuilder mpropdef.msignature = new MSignature(new Array[MParameter], null) # always an empty real signature nclassdef.mfree_init = mpropdef self.toolcontext.info("{mclassdef} gets a free constructor for attributes {mpropdef}{msignature}", 3) + mclassdef.mclass.root_init = mpropdef end # Check the visibility of `mtype` as an element of the signature of `mpropdef`. @@ -241,7 +244,7 @@ redef class ModelBuilder # Extract visibility information of the main part of `mtype` # It is a case-by case var vis_type: nullable MVisibility = null # The own visibility of the type - var mmodule_type: nullable MModule = null # The origial module of the type + var mmodule_type: nullable MModule = null # The original module of the type mtype = mtype.as_notnullable if mtype isa MClassType then vis_type = mtype.mclass.visibility @@ -288,12 +291,21 @@ redef class MPropDef end redef class AClassdef - var build_properties_is_done: Bool = false + var build_properties_is_done = false # The free init (implicitely constructed by the class if required) var mfree_init: nullable MMethodDef = null end +redef class MClass + # The base init of the class. + # Used to get the common new_msignature and initializers + # + # TODO: Where to put this information is not clear because unlike other + # informations, the initialisers are stable in a same class. + var root_init: nullable MMethodDef = null +end + redef class MClassDef # What is the `APropdef` associated to a `MProperty`? # Used to check multiple definition of a property. @@ -440,7 +452,7 @@ redef class ASignature var ntype = self.n_type if ntype != null then self.ret_type = modelbuilder.resolve_mtype(mmodule, mclassdef, ntype) - if self.ret_type == null then return false # Skip errir + if self.ret_type == null then return false # Skip error end self.is_visited = true @@ -582,6 +594,16 @@ redef class AMethPropdef var mmodule = mclassdef.mmodule var nsig = self.n_signature + if mpropdef.mproperty.is_root_init and not mclassdef.is_intro then + var root_init = mclassdef.mclass.root_init + if root_init != null then + # Inherit the initializers by refinement + mpropdef.new_msignature = root_init.new_msignature + assert mpropdef.initializers.is_empty + mpropdef.initializers.add_all root_init.initializers + end + end + # Retrieve info from the signature AST var param_names = new Array[String] # Names of parameters from the AST var param_types = new Array[MType] # Types of parameters from the AST @@ -718,12 +740,12 @@ redef class AAttrPropdef # Is the node tagged `noinit`? var noinit = false - # Is the node taggeg lazy? + # Is the node tagged lazy? var is_lazy = false - # The guard associated to a lasy attribute. + # The guard associated to a lazy attribute. # Because some engines does not have a working `isset`, - # this additionnal attribute is used to guard the lazy initialization. + # this additional attribute is used to guard the lazy initialization. # TODO: to remove once isset is correctly implemented var mlazypropdef: nullable MAttributeDef @@ -830,7 +852,7 @@ redef class AAttrPropdef redef fun build_signature(modelbuilder) do var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped + if mpropdef == null then return # Error thus skipped var mclassdef = mpropdef.mclassdef var mmodule = mclassdef.mmodule var mtype: nullable MType = null @@ -843,10 +865,10 @@ redef class AAttrPropdef if mtype == null then return end - # Inherit the type from the getter (usually an abstact getter) + # Inherit the type from the getter (usually an abstract getter) if mtype == null and mreadpropdef != null and not mreadpropdef.is_intro then var msignature = mreadpropdef.mproperty.intro.msignature - if msignature == null then return # Error, thus skiped + if msignature == null then return # Error, thus skipped mtype = msignature.return_mtype end @@ -918,12 +940,10 @@ redef class AAttrPropdef redef fun check_signature(modelbuilder) do var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped - var mclassdef = mpropdef.mclassdef - var mmodule = mclassdef.mmodule + if mpropdef == null then return # Error thus skipped var ntype = self.n_type var mtype = self.mpropdef.static_mtype - if mtype == null then return # Error thus skiped + if mtype == null then return # Error thus skipped # Lookup for signature in the precursor # FIXME all precursors should be considered @@ -1046,7 +1066,7 @@ redef class ATypePropdef redef fun build_signature(modelbuilder) do var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped + if mpropdef == null then return # Error thus skipped var mclassdef = mpropdef.mclassdef var mmodule = mclassdef.mmodule var mtype: nullable MType = null @@ -1062,10 +1082,10 @@ redef class ATypePropdef redef fun check_signature(modelbuilder) do var mpropdef = self.mpropdef - if mpropdef == null then return # Error thus skiped + if mpropdef == null then return # Error thus skipped var bound = self.mpropdef.bound - if bound == null then return # Error thus skiped + if bound == null then return # Error thus skipped modelbuilder.check_visibility(n_type, bound, mpropdef) diff --git a/src/neo.nit b/src/neo.nit index b44d034..f9d3c9a 100644 --- a/src/neo.nit +++ b/src/neo.nit @@ -12,17 +12,17 @@ # 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. # @@ -67,9 +67,10 @@ # # * labels: `MClass`, `model_name` and `MEntity`. # * `full_name`: fully qualified name. -# * `arity`: number of generic formal parameters. 0 if the class is not generic. # * `kind`: kind of the class (`interface`, `abstract class`, etc.) # * `visibility`: visibility of the class. +# * `parameter_names`: JSON array listing the name of each formal generic +# parameter (in order of declaration). # * `(:MClass)-[:CLASSTYPE]->(:MClassType)`: SEE: `MClass.mclass_type` # # Arguments in the `CLASSTYPE` are named following the `parameter_names` @@ -82,8 +83,6 @@ # * labels: `MClassDef`, `model_name` and `MEntity`. # * `is_intro`: Does this definition introduce the class? # * `location`: origin of the definition. SEE: `Location.to_s` -# * `parameter_names`: JSON array listing the name of each formal generic -# parameter (in order of declaration). # * `(:MClassDef)-[:BOUNDTYPE]->(:MClassType)`: bounded type associated to the # classdef. # * `(:MClassDef)-[:MCLASS]->(:MClass)`: associated `MClass`. @@ -122,6 +121,11 @@ # * `(: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 @@ -141,7 +145,7 @@ # * `(:MClassType)-[:ARGUMENT]->(:MType)`: type arguments. # # Arguments are named following the `parameter_names` attribute of the -# `MClassDef` that introduces the class referred by `CLASS`. +# `MClass` referred by `CLASS`. # # Additional relationship for `MVirtualType`: # @@ -446,7 +450,6 @@ class NeoModel node.labels.add "MModule" node["full_name"] = mmodule.full_name node["location"] = mmodule.location.to_s - var mgroup = mmodule.mgroup for parent in mmodule.in_importation.direct_greaters do node.out_edges.add(new NeoEdge(node, "IMPORTS", to_node(parent))) end @@ -633,6 +636,10 @@ class NeoModel end else if mpropdef isa MAttributeDef then node.labels.add "MAttributeDef" + var static_mtype = mpropdef.static_mtype + if static_mtype != null then + node.out_edges.add(new NeoEdge(node, "TYPE", to_node(static_mtype))) + end else if mpropdef isa MVirtualTypeDef then node.labels.add "MVirtualTypeDef" var bound = mpropdef.bound @@ -663,6 +670,8 @@ class NeoModel else if node.labels.has("MAttributeDef") then mpropdef = new MAttributeDef(mclassdef, mproperty.as(MAttribute), location) mentities[node] = mpropdef + var static_mtype = node.out_nodes("TYPE") + if not static_mtype.is_empty then mpropdef.static_mtype = to_mtype(model, static_mtype.first) else if node.labels.has("MVirtualTypeDef") then mpropdef = new MVirtualTypeDef(mclassdef, mproperty.as(MVirtualTypeProp), location) mentities[node] = mpropdef diff --git a/src/nit.nit b/src/nit.nit index 24bb00e..9a95194 100644 --- a/src/nit.nit +++ b/src/nit.nit @@ -27,10 +27,9 @@ toolcontext.tooldescription = "Usage: nit [OPTION]... ...\nInterprets # Add an option "-o" to enable compatibilit with the tests.sh script var opt = new OptionString("compatibility (does noting)", "-o") toolcontext.option_context.add_option(opt) -var opt_mixins = new OptionArray("Additionals module to min-in", "-m") var opt_eval = new OptionBool("Specifies the program from command-line", "-e") var opt_loop = new OptionBool("Repeatedly run the program for each line in file-name arguments", "-n") -toolcontext.option_context.add_option(opt_mixins, opt_eval, opt_loop) +toolcontext.option_context.add_option(opt_eval, opt_loop) # We do not add other options, so process them now! toolcontext.process_options(args) @@ -66,20 +65,11 @@ else mmodules = modelbuilder.parse([progname]) end -mmodules.add_all modelbuilder.parse(opt_mixins.value) modelbuilder.run_phases if toolcontext.opt_only_metamodel.value then exit(0) -var mainmodule: nullable MModule - -# Here we launch the interpreter on the main module -if mmodules.length == 1 then - mainmodule = mmodules.first -else - mainmodule = new MModule(model, null, mmodules.first.name, mmodules.first.location) - mainmodule.set_imported_mmodules(mmodules) -end +var mainmodule = toolcontext.make_main_module(mmodules) var self_mm = mainmodule var self_args = arguments diff --git a/src/nitpretty.nit b/src/nitpretty.nit index f43c8d1..32294c2 100644 --- a/src/nitpretty.nit +++ b/src/nitpretty.nit @@ -2248,7 +2248,6 @@ end redef class ASuperstringExpr redef fun accept_pretty_printer(v) do - var force_inline = self.force_inline for n_expr in n_exprs do v.visit n_expr end diff --git a/src/nitserial.nit b/src/nitserial.nit index 5ad0aed..2fd0f82 100644 --- a/src/nitserial.nit +++ b/src/nitserial.nit @@ -53,7 +53,7 @@ class NitModule if header != null then add header var name = name - if name != null then add "module {name}\n\n" + add "module {name}\n\n" for i in imports do add "import {i}\n" add "\n" diff --git a/src/parser/parser_nodes.nit b/src/parser/parser_nodes.nit index f7f4b60..a4d6d44 100644 --- a/src/parser/parser_nodes.nit +++ b/src/parser/parser_nodes.nit @@ -1347,14 +1347,19 @@ abstract class ALabelable var n_label: nullable ALabel = null is writable end -# A `break` statement. -class ABreakExpr +# A `break` or a `continue` +abstract class AEscapeExpr super AExpr super ALabelable - var n_kwbreak: TKwbreak is writable, noinit var n_expr: nullable AExpr = null is writable end +# A `break` statement. +class ABreakExpr + super AEscapeExpr + var n_kwbreak: TKwbreak is writable, noinit +end + # An `abort` statement class AAbortExpr super AExpr @@ -1363,10 +1368,8 @@ end # A `continue` statement class AContinueExpr - super AExpr - super ALabelable + super AEscapeExpr var n_kwcontinue: nullable TKwcontinue = null is writable - var n_expr: nullable AExpr = null is writable end # A `do` statement diff --git a/src/parser/parser_work.nit b/src/parser/parser_work.nit index 2a83ee0..4d67908 100644 --- a/src/parser/parser_work.nit +++ b/src/parser/parser_work.nit @@ -246,8 +246,6 @@ private class ComputeProdLocationVisitor end end end - - init do end end private class TextCollectorVisitor diff --git a/src/rapid_type_analysis.nit b/src/rapid_type_analysis.nit index a6adfee..897d7b7 100644 --- a/src/rapid_type_analysis.nit +++ b/src/rapid_type_analysis.nit @@ -244,6 +244,9 @@ class RapidTypeAnalysis if mmethoddef.mproperty.is_root_init and not mmethoddef.is_intro then self.add_super_send(v.receiver, mmethoddef) end + else if mmethoddef.constant_value != null then + # Make the return type live + v.add_type(mmethoddef.msignature.return_mtype.as(MClassType)) else abort end @@ -675,6 +678,8 @@ redef class AForExpr abort end v.add_callsite(self.method_next) + var mf = self.method_finish + if mf != null then v.add_callsite(mf) end end diff --git a/src/semantize/auto_super_init.nit b/src/semantize/auto_super_init.nit index 95b2acb..25874e2 100644 --- a/src/semantize/auto_super_init.nit +++ b/src/semantize/auto_super_init.nit @@ -32,10 +32,6 @@ end private class AutoSuperInitVisitor super Visitor - init - do - end - redef fun visit(n) do n.accept_auto_super_init(self) @@ -210,7 +206,6 @@ redef class ASendExpr redef fun accept_auto_super_init(v) do var mproperty = self.callsite.mproperty - if mproperty == null then return if mproperty.is_init then v.has_explicit_super_init = self end diff --git a/src/semantize/flow.nit b/src/semantize/flow.nit index d4f6cb9..522a347 100644 --- a/src/semantize/flow.nit +++ b/src/semantize/flow.nit @@ -165,7 +165,7 @@ private class FlowVisitor fun merge_continues_to(before_loop: FlowContext, escapemark: nullable EscapeMark) do if escapemark == null then return - for b in escapemark.continues do + for b in escapemark.escapes do var before = b.before_flow_context if before == null then continue # Forward error before_loop.add_loop(before) @@ -175,7 +175,7 @@ private class FlowVisitor fun merge_breaks(escapemark: nullable EscapeMark) do if escapemark == null then return - for b in escapemark.breaks do + for b in escapemark.escapes do var before = b.before_flow_context if before == null then continue # Forward error self.make_merge_flow(self.current_flow_context, before) @@ -319,22 +319,7 @@ redef class AReturnExpr end end -redef class AContinueExpr - # The flow just before it become unreachable - fun before_flow_context: nullable FlowContext - do - var after = self.after_flow_context - if after == null then return null - return after.previous.first - end - redef fun accept_flow_visitor(v) - do - super - v.make_unreachable_flow - end -end - -redef class ABreakExpr +redef class AEscapeExpr # The flow just before it become unreachable fun before_flow_context: nullable FlowContext do @@ -361,7 +346,7 @@ redef class ADoExpr redef fun accept_flow_visitor(v) do super - v.merge_breaks(self.escapemark) + v.merge_breaks(self.break_mark) end end @@ -411,10 +396,10 @@ redef class AWhileExpr var after_block = v.current_flow_context before_loop.add_loop(after_block) - v.merge_continues_to(after_block, self.escapemark) + v.merge_continues_to(after_block, self.continue_mark) v.current_flow_context = after_expr.when_false - v.merge_breaks(self.escapemark) + v.merge_breaks(self.break_mark) end end @@ -428,10 +413,10 @@ redef class ALoopExpr var after_block = v.current_flow_context before_loop.add_loop(after_block) - v.merge_continues_to(after_block, self.escapemark) + v.merge_continues_to(after_block, self.continue_mark) v.make_unreachable_flow - v.merge_breaks(self.escapemark) + v.merge_breaks(self.break_mark) end end @@ -447,10 +432,10 @@ redef class AForExpr var after_block = v.current_flow_context before_loop.add_loop(after_block) - v.merge_continues_to(after_block, self.escapemark) + v.merge_continues_to(after_block, self.continue_mark) v.make_merge_flow(v.current_flow_context, before_loop) - v.merge_breaks(self.escapemark) + v.merge_breaks(self.break_mark) end end diff --git a/src/semantize/scope.nit b/src/semantize/scope.nit index 3039745..00702d0 100644 --- a/src/semantize/scope.nit +++ b/src/semantize/scope.nit @@ -50,15 +50,12 @@ class EscapeMark # The name of the label (unless the mark is an anonymous loop mark) var name: nullable String - # Is the mark attached to a loop (loop, while, for) - # Such a mark is a candidate to a labelless 'continue' or 'break' - var for_loop: Bool + # The associated `continue` mark, if any. + # If the mark attached to a loop (loop, while, for), a distinct mark is used. + private var continue_mark: nullable EscapeMark = null - # Each 'continue' attached to the mark - var continues = new Array[AContinueExpr] - - # Each 'break' attached to the mark - var breaks = new Array[ABreakExpr] + # Each break/continue attached to the mark + var escapes = new Array[AEscapeExpr] end # Visit a npropdef and: @@ -81,7 +78,7 @@ private class ScopeVisitor end # All stacked scope. `scopes.first` is the current scope - private var scopes = new List[Scope] + var scopes = new List[Scope] # Shift and check the last scope fun shift_scope @@ -176,7 +173,8 @@ private class ScopeVisitor else name = null end - var res = new EscapeMark(name, for_loop) + var res = new EscapeMark(name) + if for_loop then res.continue_mark = new EscapeMark(name) return res end @@ -292,43 +290,47 @@ redef class ASelfExpr end end -redef class AContinueExpr - # The escape mark associated with the continue +redef class AEscapeExpr + # The escape mark associated with the break/continue var escapemark: nullable EscapeMark +end + +redef class AContinueExpr redef fun accept_scope_visitor(v) do super var escapemark = v.get_escapemark(self, self.n_label) if escapemark == null then return # Skip error - if not escapemark.for_loop then + escapemark = escapemark.continue_mark + if escapemark == null then v.error(self, "Error: cannot 'continue', only 'break'.") + return end - escapemark.continues.add(self) + escapemark.escapes.add(self) self.escapemark = escapemark end end redef class ABreakExpr - # The escape mark associated with the break - var escapemark: nullable EscapeMark redef fun accept_scope_visitor(v) do super var escapemark = v.get_escapemark(self, self.n_label) if escapemark == null then return # Skip error - escapemark.breaks.add(self) + escapemark.escapes.add(self) self.escapemark = escapemark end end redef class ADoExpr - # The escape mark associated with the 'do' block - var escapemark: nullable EscapeMark + # The break escape mark associated with the 'do' block + var break_mark: nullable EscapeMark + redef fun accept_scope_visitor(v) do - self.escapemark = v.make_escape_mark(n_label, false) - v.enter_visit_block(n_block, self.escapemark) + self.break_mark = v.make_escape_mark(n_label, false) + v.enter_visit_block(n_block, self.break_mark) end end @@ -342,24 +344,34 @@ redef class AIfExpr end redef class AWhileExpr - # The escape mark associated with the 'while' - var escapemark: nullable EscapeMark + # The break escape mark associated with the 'while' + var break_mark: nullable EscapeMark + + # The continue escape mark associated with the 'while' + var continue_mark: nullable EscapeMark + redef fun accept_scope_visitor(v) do var escapemark = v.make_escape_mark(n_label, true) - self.escapemark = escapemark + self.break_mark = escapemark + self.continue_mark = escapemark.continue_mark v.enter_visit(n_expr) v.enter_visit_block(n_block, escapemark) end end redef class ALoopExpr - # The escape mark associated with the 'loop' - var escapemark: nullable EscapeMark + # The break escape mark associated with the 'loop' + var break_mark: nullable EscapeMark + + # The continue escape mark associated with the 'loop' + var continue_mark: nullable EscapeMark + redef fun accept_scope_visitor(v) do var escapemark = v.make_escape_mark(n_label, true) - self.escapemark = escapemark + self.break_mark = escapemark + self.continue_mark = escapemark.continue_mark v.enter_visit_block(n_block, escapemark) end end @@ -368,8 +380,11 @@ redef class AForExpr # The automatic variables in order var variables: nullable Array[Variable] - # The escape mark associated with the 'for' - var escapemark: nullable EscapeMark + # The break escape mark associated with the 'for' + var break_mark: nullable EscapeMark + + # The continue escape mark associated with the 'for' + var continue_mark: nullable EscapeMark redef fun accept_scope_visitor(v) do @@ -388,7 +403,8 @@ redef class AForExpr end var escapemark = v.make_escape_mark(n_label, true) - self.escapemark = escapemark + self.break_mark = escapemark + self.continue_mark = escapemark.continue_mark v.enter_visit_block(n_block, escapemark) v.shift_scope diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 95d7b3a..d3d4da7 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -47,7 +47,7 @@ private class TypeVisitor # The analyzed property var mpropdef: nullable MPropDef - var selfvariable: Variable = new Variable("self") + var selfvariable = new Variable("self") # Is `self` use restricted? # * no explicit `self` @@ -195,6 +195,40 @@ private class TypeVisitor return sup end + # Special verification on != and == for null + # Return true + fun null_test(anode: ABinopExpr) + do + var mtype = anode.n_expr.mtype + var mtype2 = anode.n_expr2.mtype + + if mtype == null or mtype2 == null then return + + if not mtype2 isa MNullType then return + + # Check of useless null + if not mtype isa MNullableType then + if not anchor_to(mtype) isa MNullableType then + modelbuilder.warning(anode, "useless-null-test", "Warning: expression is not null, since it is a `{mtype}`.") + end + return + end + + # Check for type adaptation + var variable = anode.n_expr.its_variable + if variable == null then return + + if anode isa AEqExpr then + anode.after_flow_context.when_true.set_var(variable, mtype2) + anode.after_flow_context.when_false.set_var(variable, mtype.mtype) + else if anode isa ANeExpr then + anode.after_flow_context.when_false.set_var(variable, mtype2) + anode.after_flow_context.when_true.set_var(variable, mtype.mtype) + else + abort + end + end + fun try_get_mproperty_by_name2(anode: ANode, mtype: MType, name: String): nullable MProperty do return self.modelbuilder.try_get_mproperty_by_name2(anode, mmodule, mtype, name) @@ -304,6 +338,15 @@ private class TypeVisitor return callsite end + fun try_get_method(node: ANode, recvtype: MType, name: String, recv_is_self: Bool): nullable CallSite + do + var unsafe_type = self.anchor_to(recvtype) + var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) + if mproperty == null then return null + return get_method(node, recvtype, name, recv_is_self) + end + + # Visit the expressions of args and check their conformity with the corresponding type in signature # The point of this method is to handle varargs correctly # Note: The signature must be correctly adapted @@ -334,11 +377,18 @@ private class TypeVisitor self.visit_expr_subtype(args[j], paramtype) end if vararg_rank >= 0 then - var varargs = new Array[AExpr] var paramtype = msignature.mparameters[vararg_rank].mtype - for j in [vararg_rank..vararg_rank+vararg_decl] do - varargs.add(args[j]) - self.visit_expr_subtype(args[j], paramtype) + var first = args[vararg_rank] + if vararg_decl == 0 and first isa AVarargExpr then + var mclass = get_mclass(node, "Array") + if mclass == null then return false # Forward error + var array_mtype = mclass.get_mtype([paramtype]) + self.visit_expr_subtype(first.n_expr, array_mtype) + first.mtype = first.n_expr.mtype + else + for j in [vararg_rank..vararg_rank+vararg_decl] do + self.visit_expr_subtype(args[j], paramtype) + end end end return true @@ -386,7 +436,6 @@ private class TypeVisitor fun merge_types(node: ANode, col: Array[nullable MType]): nullable MType do if col.length == 1 then return col.first - var res = new Array[nullable MType] for t1 in col do if t1 == null then continue # return null var found = true @@ -452,8 +501,8 @@ end redef class FlowContext # Store changes of types because of type evolution - private var vars: HashMap[Variable, nullable MType] = new HashMap[Variable, nullable MType] - private var cache: HashMap[Variable, nullable Array[nullable MType]] = new HashMap[Variable, nullable Array[nullable MType]] + private var vars = new HashMap[Variable, nullable MType] + private var cache = new HashMap[Variable, nullable Array[nullable MType]] # Adapt the variable to a static type # Warning1: do not modify vars directly. @@ -738,7 +787,7 @@ redef class AContinueExpr do var nexpr = self.n_expr if nexpr != null then - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) end self.is_typed = true end @@ -749,7 +798,7 @@ redef class ABreakExpr do var nexpr = self.n_expr if nexpr != null then - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) end self.is_typed = true end @@ -762,9 +811,9 @@ redef class AReturnExpr var ret_type = v.mpropdef.as(MMethodDef).msignature.return_mtype if nexpr != null then if ret_type != null then - var mtype = v.visit_expr_subtype(nexpr, ret_type) + v.visit_expr_subtype(nexpr, ret_type) else - var mtype = v.visit_expr(nexpr) + v.visit_expr(nexpr) v.error(self, "Error: Return with value in a procedure.") end else if ret_type != null then @@ -846,6 +895,10 @@ redef class AForExpr var method_item: nullable CallSite var method_next: nullable CallSite var method_key: nullable CallSite + var method_finish: nullable CallSite + + var method_lt: nullable CallSite + var method_successor: nullable CallSite private fun do_type_iterator(v: TypeVisitor, mtype: MType) do @@ -937,6 +990,8 @@ redef class AForExpr end self.method_next = nextdef + self.method_finish = v.try_get_method(self, ittype, "finish", false) + if is_map then var keydef = v.get_method(self, ittype, "key", false) if keydef == null then @@ -945,6 +1000,19 @@ redef class AForExpr end self.method_key = keydef end + + if self.variables.length == 1 and n_expr isa ARangeExpr then + var variable = variables.first + var vtype = variable.declared_type.as(not null) + + if n_expr isa AOrangeExpr then + self.method_lt = v.get_method(self, vtype, "<", false) + else + self.method_lt = v.get_method(self, vtype, "<=", false) + end + + self.method_successor = v.get_method(self, vtype, "successor", false) + end end redef fun accept_typing(v) @@ -1195,9 +1263,9 @@ redef class AIsaExpr var variable = self.n_expr.its_variable if variable != null then - var orig = self.n_expr.mtype - var from = if orig != null then orig.to_s else "invalid" - var to = if mtype != null then mtype.to_s else "invalid" + #var orig = self.n_expr.mtype + #var from = if orig != null then orig.to_s else "invalid" + #var to = if mtype != null then mtype.to_s else "invalid" #debug("adapt {variable}: {from} -> {to}") self.after_flow_context.when_true.set_var(variable, mtype) end @@ -1331,16 +1399,7 @@ redef class AEqExpr redef fun accept_typing(v) do super - - var variable = self.n_expr.its_variable - if variable == null then return - var mtype = self.n_expr2.mtype - if not mtype isa MNullType then return - var vartype = v.get_variable(self, variable) - if not vartype isa MNullableType then return - self.after_flow_context.when_true.set_var(variable, mtype) - self.after_flow_context.when_false.set_var(variable, vartype.mtype) - #debug("adapt {variable}:{vartype} ; true->{mtype} false->{vartype.mtype}") + v.null_test(self) end end redef class ANeExpr @@ -1348,16 +1407,7 @@ redef class ANeExpr redef fun accept_typing(v) do super - - var variable = self.n_expr.its_variable - if variable == null then return - var mtype = self.n_expr2.mtype - if not mtype isa MNullType then return - var vartype = v.get_variable(self, variable) - if not vartype isa MNullableType then return - self.after_flow_context.when_false.set_var(variable, mtype) - self.after_flow_context.when_true.set_var(variable, vartype.mtype) - #debug("adapt {variable}:{vartype} ; true->{vartype.mtype} false->{mtype}") + v.null_test(self) end end redef class ALtExpr @@ -1758,6 +1808,16 @@ redef class AIssetAttrExpr end end +redef class AVarargExpr + redef fun accept_typing(v) + do + # This kind of pseudo-expression can be only processed trough a signature + # See `check_signature` + # Other cases are a syntax error. + v.error(self, "Syntax error: unexpected `...`") + end +end + ### redef class ADebugTypeExpr diff --git a/src/testing/testing_doc.nit b/src/testing/testing_doc.nit index 9f31805..566e014 100644 --- a/src/testing/testing_doc.nit +++ b/src/testing/testing_doc.nit @@ -136,7 +136,7 @@ class NitUnitExecutor var res = sys.system(cmd) var res2 = 0 if res == 0 then - res2 = sys.system("./{file}.bin >>'{file}.out1' 2>&1 >'{file}.out1' 2>&1 '{file}.out1' 2>&1 '{file}.out1' 2>&1 """ + exit 0 + end + var errors = option_context.get_errors if not errors.is_empty then for e in errors do print "Error: {e}" @@ -438,7 +472,7 @@ class BashCompletion addn " COMPREPLY=()" addn " cur=\"$\{COMP_WORDS[COMP_CWORD]\}\"" addn " prev=\"$\{COMP_WORDS[COMP_CWORD-1]\}\"" - if option_names != null then + if not option_names.is_empty then addn " opts=\"{option_names.join(" ")}\"" addn " if [[ $\{cur\} == -* ]] ; then" addn " COMPREPLY=( $(compgen -W \"$\{opts\}\" -- $\{cur\}) )" diff --git a/src/transform.nit b/src/transform.nit index 6a209c3..6c8208a 100644 --- a/src/transform.nit +++ b/src/transform.nit @@ -19,9 +19,19 @@ module transform import astbuilder import astvalidation import semantize +intrude import semantize::scope redef class ToolContext var transform_phase: Phase = new TransformPhase(self, [typing_phase, auto_super_init_phase]) + + # --no-shortcut-range + var opt_no_shortcut_range: OptionBool = new OptionBool("Always insantiate a range and its iterator on 'for' loops", "--no-shortcut-range") + + redef init + do + super + self.option_context.add_option(self.opt_no_shortcut_range) + end end private class TransformPhase @@ -151,14 +161,113 @@ end redef class AWhileExpr redef fun accept_transform_visitor(v) do - # TODO + var nloop = v.builder.make_loop + var nif = v.builder.make_if(n_expr, null) + nloop.add nif + + var nblock = n_block + if nblock != null then nif.n_then.add nblock + + var escapemark = self.break_mark.as(not null) + var nbreak = v.builder.make_break(escapemark) + nif.n_else.add nbreak + + nloop.break_mark = self.break_mark + nloop.continue_mark = self.continue_mark + + replace_with(nloop) end end redef class AForExpr redef fun accept_transform_visitor(v) do - # TODO + var escapemark = self.break_mark + assert escapemark != null + + var nblock = v.builder.make_block + + var nexpr = n_expr + + # Shortcut on explicit range + # Avoid the instantiation of the range and the iterator + if self.variables.length == 1 and nexpr isa ARangeExpr and not v.phase.toolcontext.opt_no_shortcut_range.value then + var variable = variables.first + nblock.add v.builder.make_var_assign(variable, nexpr.n_expr) + var to = nexpr.n_expr2 + nblock.add to + + var nloop = v.builder.make_loop + nloop.break_mark = escapemark + nblock.add nloop + + var is_ok = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_lt.as(not null), [to.make_var_read]) + + var nif = v.builder.make_if(is_ok, null) + nloop.add nif + + var nthen = nif.n_then + var ndo = v.builder.make_do + ndo.break_mark = escapemark.continue_mark + nthen.add ndo + + ndo.add self.n_block.as(not null) + + var one = v.builder.make_int(1) + var succ = v.builder.make_call(v.builder.make_var_read(variable, variable.declared_type.as(not null)), method_successor.as(not null), [one]) + nthen.add v.builder.make_var_assign(variable, succ) + + var nbreak = v.builder.make_break(escapemark) + nif.n_else.add nbreak + + replace_with(nblock) + return + end + + nblock.add nexpr + + var iter = v.builder.make_call(nexpr.make_var_read, method_iterator.as(not null), null) + nblock.add iter + + var nloop = v.builder.make_loop + nloop.break_mark = escapemark + nblock.add nloop + + var is_ok = v.builder.make_call(iter.make_var_read, method_is_ok.as(not null), null) + + var nif = v.builder.make_if(is_ok, null) + nloop.add nif + + var nthen = nif.n_then + var ndo = v.builder.make_do + ndo.break_mark = escapemark.continue_mark + nthen.add ndo + + if self.variables.length == 1 then + var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null) + ndo.add v.builder.make_var_assign(variables.first, item) + else if self.variables.length == 2 then + var key = v.builder.make_call(iter.make_var_read, method_key.as(not null), null) + ndo.add v.builder.make_var_assign(variables[0], key) + var item = v.builder.make_call(iter.make_var_read, method_item.as(not null), null) + ndo.add v.builder.make_var_assign(variables[1], item) + else + abort + end + + ndo.add self.n_block.as(not null) + + nthen.add v.builder.make_call(iter.make_var_read, method_next.as(not null), null) + + var nbreak = v.builder.make_break(escapemark) + nif.n_else.add nbreak + + var method_finish = self.method_finish + if method_finish != null then + nblock.add v.builder.make_call(iter.make_var_read, method_finish, null) + end + + replace_with(nblock) end end diff --git a/src/vm.nit b/src/vm.nit index 0779a6a..3807ab7 100644 --- a/src/vm.nit +++ b/src/vm.nit @@ -106,7 +106,6 @@ class VirtualMachine super NaiveInterpreter # Sub can be discovered inside a Generic type during the subtyping test if not sub.mclass.loaded then create_class(sub.mclass) - if anchor == null then anchor = sub if sup isa MGenericType then var sub2 = sub.supertype_to(mainmodule, anchor, sup.mclass) assert sub2.mclass == sup.mclass diff --git a/tests/base_for_finish.nit b/tests/base_for_finish.nit new file mode 100644 index 0000000..baaf29e --- /dev/null +++ b/tests/base_for_finish.nit @@ -0,0 +1,60 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import array + +class MyC + var data: Collection[Object] + fun iterator: MyI do return new MyI(data.iterator) +end + +class MyI + super Iterator[Object] + var iter: Iterator[Object] + redef fun is_ok do return iter.is_ok + redef fun item do return iter.item + redef fun next do iter.next + redef fun finish do 0.output +end + +fun test(a: MyC) +do + for x in a do + x.output + for y in [10,20] do + y.output + if x == 2 then return + end + 100.output + end + 200.output +end + +var a = new MyC([1,2,3]) + +for x in a do + x.output +end + +'\n'.output + +for x in a do + x.output + if x == 2 then break + 100.output +end + +'\n'.output + +test(a) diff --git a/tests/base_init_auto_refine.nit b/tests/base_init_auto_refine.nit new file mode 100644 index 0000000..afa534f --- /dev/null +++ b/tests/base_init_auto_refine.nit @@ -0,0 +1,22 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base_init_auto + +redef class A + redef init do 'A'.output +end + +var a = new A(2) +a.work diff --git a/tests/base_vararg3.nit b/tests/base_vararg3.nit new file mode 100644 index 0000000..6918fef --- /dev/null +++ b/tests/base_vararg3.nit @@ -0,0 +1,45 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import array + +class A + fun foo(a: Int...) do a.first.output +end + +class B + super A + redef fun foo(a) + do + 'a'.output + super + 'b'.output + super(a...) + 'c'.output + super(4,5,6) + 'd'.output + super([5,6,7]...) + #alt3#super(a) + #alt4#super(1...) + end +end + +var a = new A +a.foo(1,2,3) +a.foo([2,3,4]...) +#alt1#a.foo([1,2,3]) +#alt2#a.foo(1...) + +var b = new B +b.foo(3,4,5) diff --git a/tests/bench_complex_sort.nit b/tests/bench_complex_sort.nit index 116d7d7..2eef2cb 100644 --- a/tests/bench_complex_sort.nit +++ b/tests/bench_complex_sort.nit @@ -74,8 +74,9 @@ class E end class EltComparator - super Comparator[Elt] - redef fun compare(a: Elt, b: Elt): Int + super Comparator + redef type COMPARED: Elt + redef fun compare(a, b) do if _is_val1 then return a.val1 <=> b.val1 diff --git a/tests/example_sorter.nit b/tests/example_sorter.nit index 32b661c..ae839ed 100644 --- a/tests/example_sorter.nit +++ b/tests/example_sorter.nit @@ -16,7 +16,8 @@ class BackIntComparator - super Comparator[Int] + super Comparator + redef type COMPARED: Int redef fun compare(a: Int, b: Int): Int do return b <=> a @@ -26,7 +27,8 @@ class BackIntComparator end class DecimalComparator - super Comparator[Int] + super Comparator + redef type COMPARED: Int redef fun compare(a: Int, b: Int): Int do return (a%10) <=> (b%10) @@ -51,7 +53,7 @@ end var q = get_an_array(50) print(q.join(" ")) -(new DefaultComparator[Int]).sort(q) +(default_comparator).sort(q) print(q.join(" ")) (new DecimalComparator).sort(q) print(q.join(" ")) diff --git a/tests/nit.args b/tests/nit.args index 3011fc1..0c14c1c 100644 --- a/tests/nit.args +++ b/tests/nit.args @@ -1,3 +1,4 @@ --log --log-dir out/test_nitc_logs ../examples/hello_world.nit base_simple3.nit -m test_mixin.nit ../examples/hello_world.nit +test_define.nit -D text=hello -D num=42 -D flag diff --git a/tests/nitg.args b/tests/nitg.args index 3b38c54..6a47146 100644 --- a/tests/nitg.args +++ b/tests/nitg.args @@ -5,3 +5,4 @@ --global ../examples/hello_world.nit -m test_mixin.nit -o out/nitg-hello_world_mixed ; out/nitg-hello_world_mixed --separate ../examples/hello_world.nit -m test_mixin.nit -o out/nitgs-hello_world_mixed ; out/nitgs-hello_world_mixed base_simple_import.nit base_simple.nit --dir out/ ; out/base_simple ; out/base_simple_import +test_define.nit -D text=hello -D num=42 -D flag --dir out/ ; out/test_define diff --git a/tests/niti.skip b/tests/niti.skip index c052cff..baf5b76 100644 --- a/tests/niti.skip +++ b/tests/niti.skip @@ -5,6 +5,7 @@ shoot_logic bench_ nit_args1 nit_args3 +nit_args4 nitvm_args1 nitvm_args3 nitc_args1 @@ -12,6 +13,7 @@ nitg_args1 nitg_args3 nitg_args5 nitg_args6 +nitg_args8 test_markdown_args1 pep8analysis nitcc_parser_gen diff --git a/tests/sav/base_eq_null_notnull.res b/tests/sav/base_eq_null_notnull.res index 41a85bf..b4226bc 100644 --- a/tests/sav/base_eq_null_notnull.res +++ b/tests/sav/base_eq_null_notnull.res @@ -1,3 +1,5 @@ +base_eq_null_notnull.nit:36,6--14: Warning: expression is not null, since it is a `A`. +base_eq_null_notnull.nit:43,2--10: Warning: expression is not null, since it is a `A`. true true true diff --git a/tests/sav/base_for_finish.res b/tests/sav/base_for_finish.res new file mode 100644 index 0000000..07ea0b2 --- /dev/null +++ b/tests/sav/base_for_finish.res @@ -0,0 +1,17 @@ +1 +2 +3 +0 + +1 +100 +2 +0 + +1 +10 +20 +100 +2 +10 +0 diff --git a/tests/sav/base_init_auto_refine.res b/tests/sav/base_init_auto_refine.res new file mode 100644 index 0000000..445bff9 --- /dev/null +++ b/tests/sav/base_init_auto_refine.res @@ -0,0 +1,2 @@ +A2 +-2 diff --git a/tests/sav/base_var_type_evolution_null3.res b/tests/sav/base_var_type_evolution_null3.res index a556a0a..46069e8 100644 --- a/tests/sav/base_var_type_evolution_null3.res +++ b/tests/sav/base_var_type_evolution_null3.res @@ -1,3 +1,4 @@ +base_var_type_evolution_null3.nit:52,5--13: Warning: expression is not null, since it is a `Object`. 1 1 5 diff --git a/tests/sav/base_var_type_evolution_null3_alt1.res b/tests/sav/base_var_type_evolution_null3_alt1.res index a556a0a..a10a5e3 100644 --- a/tests/sav/base_var_type_evolution_null3_alt1.res +++ b/tests/sav/base_var_type_evolution_null3_alt1.res @@ -1,3 +1,4 @@ +alt/base_var_type_evolution_null3_alt1.nit:52,5--13: Warning: expression is not null, since it is a `Object`. 1 1 5 diff --git a/tests/sav/base_vararg3.res b/tests/sav/base_vararg3.res new file mode 100644 index 0000000..7d434e8 --- /dev/null +++ b/tests/sav/base_vararg3.res @@ -0,0 +1,6 @@ +1 +2 +a3 +b3 +c4 +d5 diff --git a/tests/sav/base_vararg3_alt1.res b/tests/sav/base_vararg3_alt1.res new file mode 100644 index 0000000..1e24ee6 --- /dev/null +++ b/tests/sav/base_vararg3_alt1.res @@ -0,0 +1 @@ +alt/base_vararg3_alt1.nit:41,7--13: Type error: expected Int, got Array[Int] diff --git a/tests/sav/base_vararg3_alt2.res b/tests/sav/base_vararg3_alt2.res new file mode 100644 index 0000000..52bce29 --- /dev/null +++ b/tests/sav/base_vararg3_alt2.res @@ -0,0 +1 @@ +alt/base_vararg3_alt2.nit:42,7: Type error: expected Array[Int], got Int diff --git a/tests/sav/base_vararg3_alt3.res b/tests/sav/base_vararg3_alt3.res new file mode 100644 index 0000000..e7e44bc --- /dev/null +++ b/tests/sav/base_vararg3_alt3.res @@ -0,0 +1 @@ +alt/base_vararg3_alt3.nit:33,9: Type error: expected Int, got Array[Int] diff --git a/tests/sav/base_vararg3_alt4.res b/tests/sav/base_vararg3_alt4.res new file mode 100644 index 0000000..caca43b --- /dev/null +++ b/tests/sav/base_vararg3_alt4.res @@ -0,0 +1 @@ +alt/base_vararg3_alt4.nit:34,9: Type error: expected Array[Int], got Int diff --git a/tests/sav/nit_args4.res b/tests/sav/nit_args4.res new file mode 100644 index 0000000..483d841 --- /dev/null +++ b/tests/sav/nit_args4.res @@ -0,0 +1,3 @@ +hello +42 +true diff --git a/tests/sav/nitg-e/fixme/base_for_finish.res b/tests/sav/nitg-e/fixme/base_for_finish.res new file mode 100644 index 0000000..390c4e4 --- /dev/null +++ b/tests/sav/nitg-e/fixme/base_for_finish.res @@ -0,0 +1,16 @@ +1 +2 +3 +0 + +1 +100 +2 +0 + +1 +10 +20 +100 +2 +10 diff --git a/tests/sav/nitg-g/fixme/base_for_finish.res b/tests/sav/nitg-g/fixme/base_for_finish.res new file mode 100644 index 0000000..390c4e4 --- /dev/null +++ b/tests/sav/nitg-g/fixme/base_for_finish.res @@ -0,0 +1,16 @@ +1 +2 +3 +0 + +1 +100 +2 +0 + +1 +10 +20 +100 +2 +10 diff --git a/tests/sav/nitg-s/fixme/base_for_finish.res b/tests/sav/nitg-s/fixme/base_for_finish.res new file mode 100644 index 0000000..390c4e4 --- /dev/null +++ b/tests/sav/nitg-s/fixme/base_for_finish.res @@ -0,0 +1,16 @@ +1 +2 +3 +0 + +1 +100 +2 +0 + +1 +10 +20 +100 +2 +10 diff --git a/tests/sav/nitg-sg/fixme/base_for_finish.res b/tests/sav/nitg-sg/fixme/base_for_finish.res new file mode 100644 index 0000000..390c4e4 --- /dev/null +++ b/tests/sav/nitg-sg/fixme/base_for_finish.res @@ -0,0 +1,16 @@ +1 +2 +3 +0 + +1 +100 +2 +0 + +1 +10 +20 +100 +2 +10 diff --git a/tests/sav/nitg_args8.res b/tests/sav/nitg_args8.res new file mode 100644 index 0000000..483d841 --- /dev/null +++ b/tests/sav/nitg_args8.res @@ -0,0 +1,3 @@ +hello +42 +true diff --git a/tests/sav/niti/error_needed_method_alt3.res b/tests/sav/niti/error_needed_method_alt3.res deleted file mode 100644 index 34dd8c9..0000000 --- a/tests/sav/niti/error_needed_method_alt3.res +++ /dev/null @@ -1 +0,0 @@ -alt/error_needed_method_alt3.nit:48,9--13: Fatal Error: NativeString must have a property named to_s. diff --git a/tests/sav/niti/error_needed_method_alt4.res b/tests/sav/niti/error_needed_method_alt4.res index 4daa384..d1e2d76 100644 --- a/tests/sav/niti/error_needed_method_alt4.res +++ b/tests/sav/niti/error_needed_method_alt4.res @@ -1 +1 @@ -alt/error_needed_method_alt4.nit:49,10--14: Fatal Error: NativeString must have a property named to_s. +alt/error_needed_method_alt4.nit:49,10--14: Fatal Error: NativeString must have a property named to_s_with_length. diff --git a/tests/sav/nitmetrics_args1.res b/tests/sav/nitmetrics_args1.res index e706f2c..481e468 100644 --- a/tests/sav/nitmetrics_args1.res +++ b/tests/sav/nitmetrics_args1.res @@ -257,7 +257,7 @@ ## Module importation hierarchy Number of nodes: 1 Number of edges: 1 (1.00 per node) -Number of direct edges: 0 (0.0 per node) +Number of direct edges: 0 (0.00 per node) Distribution of greaters population: 1 minimum value: 1 @@ -271,7 +271,7 @@ Distribution of direct greaters minimum value: 0 maximum value: 0 total value: 0 - average value: 0.0 + average value: 0.00 distribution: <=0: sub-population=1 (100.00%); cumulated value=0 (na%) Distribution of smallers @@ -287,7 +287,7 @@ Distribution of direct smallers minimum value: 0 maximum value: 0 total value: 0 - average value: 0.0 + average value: 0.00 distribution: <=0: sub-population=1 (100.00%); cumulated value=0 (na%) ## Classdef hierarchy @@ -310,7 +310,7 @@ Distribution of direct greaters total value: 6 average value: 0.85 distribution: - <=0: sub-population=1 (14.28%); cumulated value=0 (0.0%) + <=0: sub-population=1 (14.28%); cumulated value=0 (0.00%) <=1: sub-population=6 (85.71%); cumulated value=6 (100.00%) Distribution of smallers population: 7 @@ -328,7 +328,7 @@ Distribution of direct smallers total value: 6 average value: 0.85 distribution: - <=0: sub-population=6 (85.71%); cumulated value=0 (0.0%) + <=0: sub-population=6 (85.71%); cumulated value=0 (0.00%) <=8: sub-population=1 (14.28%); cumulated value=6 (100.00%) ## Class hierarchy Number of nodes: 7 @@ -350,7 +350,7 @@ Distribution of direct greaters total value: 6 average value: 0.85 distribution: - <=0: sub-population=1 (14.28%); cumulated value=0 (0.0%) + <=0: sub-population=1 (14.28%); cumulated value=0 (0.00%) <=1: sub-population=6 (85.71%); cumulated value=6 (100.00%) Distribution of smallers population: 7 @@ -368,7 +368,7 @@ Distribution of direct smallers total value: 6 average value: 0.85 distribution: - <=0: sub-population=6 (85.71%); cumulated value=0 (0.0%) + <=0: sub-population=6 (85.71%); cumulated value=0 (0.00%) <=8: sub-population=1 (14.28%); cumulated value=6 (100.00%) --- AST Metrics --- ## All nodes of the AST @@ -449,8 +449,8 @@ Number of classes: 7 Number of class kind: 4 (57.14%) Number of class definitions: 7 -Number of refined classes: 0 (0.0%) -Average number of class refinments by classes: 0.0 +Number of refined classes: 0 (0.00%) +Average number of class refinments by classes: 0.00 Average number of class refinments by refined classes: na Number of properties: 18 @@ -714,8 +714,8 @@ Statistics of type usage: sum: 0 --- Sends on Nullable Receiver --- Total number of sends: 19 -Number of sends on a nullable receiver: 0 (0.0%) -Number of buggy sends (cannot determine the type of the receiver): 0 (0.0%) +Number of sends on a nullable receiver: 0 (0.00%) +Number of buggy sends (cannot determine the type of the receiver): 0 (0.00%) # RTA metrics diff --git a/tests/sav/test_define.res b/tests/sav/test_define.res new file mode 100644 index 0000000..8d40171 --- /dev/null +++ b/tests/sav/test_define.res @@ -0,0 +1,3 @@ +some text +1 +false diff --git a/tests/sav/test_for_abuse.res b/tests/sav/test_for_abuse.res index 51ef296..f2856be 100644 --- a/tests/sav/test_for_abuse.res +++ b/tests/sav/test_for_abuse.res @@ -1 +1,4 @@ # This file is part of NIT ( http://www.nitlanguage.org ). +f is closed? false +f is closed? true +* ** **** ******* diff --git a/tests/sav/test_hash_debug.res b/tests/sav/test_hash_debug.res index 9748c49..b52a9c5 100644 --- a/tests/sav/test_hash_debug.res +++ b/tests/sav/test_hash_debug.res @@ -6,9 +6,9 @@ false ~~~Hash statistics~~~ GET: number of get and has_key: 1 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA -average length of considered collections: 0.0 +average length of considered collections: 0.00 average capacity of considered collections: 1.00 (NA%) STORE: @@ -21,47 +21,47 @@ average capacity or considered collections: NA (NA%) ~~~Hash statistics~~~ GET: number of get and has_key: 2 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA -average length of considered collections: 0.0 +average length of considered collections: 0.00 average capacity of considered collections: 1.00 (NA%) STORE: number of stores: 1 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA -average length of considered collections: 0.0 +average length of considered collections: 0.00 average capacity or considered collections: 1.00 (NA%) ~~~~~~ ~~~Hash statistics~~~ GET: number of get and has_key: 3 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA average length of considered collections: 0.33 average capacity of considered collections: 6.33 (1900.00%) STORE: number of stores: 1 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA -average length of considered collections: 0.0 +average length of considered collections: 0.00 average capacity or considered collections: 1.00 (NA%) ~~~~~~ true ~~~Hash statistics~~~ GET: number of get and has_key: 4 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA average length of considered collections: 0.50 average capacity of considered collections: 9.00 (1800.00%) STORE: number of stores: 1 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA -average length of considered collections: 0.0 +average length of considered collections: 0.00 average capacity or considered collections: 1.00 (NA%) ~~~~~~ @@ -70,22 +70,22 @@ false ~~~Hash statistics~~~ GET: number of get and has_key: 5 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA average length of considered collections: 0.60 average capacity of considered collections: 10.60 (1766.67%) STORE: number of stores: 1 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA -average length of considered collections: 0.0 +average length of considered collections: 0.00 average capacity or considered collections: 1.00 (NA%) ~~~~~~ ~~~Hash statistics~~~ GET: number of get and has_key: 6 -number of collisions: 0 (0.0%) +number of collisions: 0 (0.00%) average length of collisions: NA average length of considered collections: 0.67 average capacity of considered collections: 11.67 (1750.00%) diff --git a/tests/sav/test_toolcontext_args1.res b/tests/sav/test_toolcontext_args1.res index 5e997e3..800327a 100644 --- a/tests/sav/test_toolcontext_args1.res +++ b/tests/sav/test_toolcontext_args1.res @@ -5,7 +5,7 @@ _DUMMY_TOOL() COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="--warn --warning --quiet --stop-on-first-error --no-color --log --log-dir --help --version --set-dummy-tool --verbose --bash-completion --option-a --option-b" + opts="--warn --warning --quiet --stop-on-first-error --no-color --log --log-dir --help --version --set-dummy-tool --verbose --bash-completion --stub-man --option-a --option-b" if [[ ${cur} == -* ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 diff --git a/tests/sav/test_toolcontext_args2.res b/tests/sav/test_toolcontext_args2.res index 8757721..91fa2fa 100644 --- a/tests/sav/test_toolcontext_args2.res +++ b/tests/sav/test_toolcontext_args2.res @@ -12,6 +12,7 @@ Test for ToolContext, try --bash-completion. --set-dummy-tool Set toolname and version to DUMMY. Useful for testing -v, --verbose Verbose --bash-completion Generate bash_completion file for this program + --stub-man Generate a stub manpage in pandoc markdown format -a, --option-a option a, do nothing -b, --option-b option b, do nothing -c option c, do nothing diff --git a/tests/test_define.nit b/tests/test_define.nit new file mode 100644 index 0000000..4b8ebc7 --- /dev/null +++ b/tests/test_define.nit @@ -0,0 +1,20 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +fun text: String do return "some text" +fun num: Int do return 1 +fun flag: Bool do return false +print text +print num +print flag diff --git a/tests/test_for_abuse.nit b/tests/test_for_abuse.nit index 545440b..cae1cd1 100644 --- a/tests/test_for_abuse.nit +++ b/tests/test_for_abuse.nit @@ -14,12 +14,21 @@ 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(" ") diff --git a/tests/test_jvm.nit b/tests/test_jvm.nit index b070f63..8b49b8b 100644 --- a/tests/test_jvm.nit +++ b/tests/test_jvm.nit @@ -128,15 +128,10 @@ if test.address_is_null then env.print_error("object test not initialized") # Retrieve field value with field ids var v_bool = env.get_boolean_field(test, f_bool) -if v_bool == null then env.print_error("vbool not found") var v_char = env.get_char_field(test, f_char) -if v_char == null then env.print_error("vchar not found") var v_i = env.get_int_field(test, f_i) -if v_i == null then env.print_error("vi not found") var v_f = env.get_float_field(test, f_f) -if v_f == null then env.print_error("vf not found") var v_test1 = env.get_object_field(test, f_test) -if v_test1 == null then env.print_error("vtest1 not found") # Set the new values for the fields env.set_boolean_field(test, f_bool, true) @@ -151,7 +146,6 @@ v_char = env.call_char_method(test, m_char, null) v_i = env.call_int_method(test, m_i, null) v_f = env.call_float_method(test, m_f, null) var v_test2 = env.call_object_method(test, m_test, null) -if v_test2 == null then env.print_error("vtest2 not found") # assert the values of the fields print v_bool diff --git a/tests/tests.sh b/tests/tests.sh index 32afdf0..14be6d4 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -36,6 +36,9 @@ paths=($paths) JNI_LIB_PATH=${paths[0]} shopt -u nullglob +outdir="out" +compdir=".nit_compile" + usage() { e=`basename "$0"` @@ -46,9 +49,74 @@ Usage: $e [options] modulenames -h This help --engine Use a specific engine (default=nitg) --noskip Do not skip a test even if the .skip file matches +--outdir Use a specific output folder (default=out/) +--compdir Use a specific temporary compilation folder (default=.nit_compile) +--node Run as a node in parallel, will not output context information END } +# Run a command with a timeout and a time count. +# Options: +# -o file write the user time into file (REQUIRED). see `-o` in `man time` +# -a append the time to the file (instead of overwriting it). see `-a` in `man time` +saferun() +{ + local stop=false + local o= + local a= + while [ $stop = false ]; do + case $1 in + -o) o="$2"; shift; shift;; + -a) a="-a"; shift;; + *) stop=true + esac + done + if test -n "$TIME"; then + $TIME -o "$o" $a $TIMEOUT "$@" + else + $TIMEOUT "$@" + if test -n "$a"; then echo 0 >> "$o"; else echo 0 > "$o"; fi + fi +} + +# Output a timestamp attribute for XML, or an empty line +timestamp() +{ + if test -n "$TIMESTAMP"; then + echo "timestamp='`$TIMESTAMP`'" + else + echo "" + fi + +} + +# Get platform specific commands ########################## + +# Detect a working timeout +if sh -c "timelimit echo" 1>/dev/null 2>&1; then + TIMEOUT="timelimit -t 600" +elif sh -c "timeout 1 echo" 1>/dev/null 2>&1; then + TIMEOUT="timeout 600s" +else + echo "No timelimit or timeout command detected. Tests may hang :(" +fi + +# Detect a working time command +if env time --quiet -f%U true 2>/dev/null; then + TIME="env time --quiet -f%U" +elif env time -f%U true 2>/dev/null; then + TIME="env time -f%U" +else + TIME= +fi + +# Detect a working date command +if date -Iseconds >/dev/null 2>&1; then + TIMESTAMP="date -Iseconds" +else + TIMESTAMP= +fi + # $1 is the pattern of the test # $2 is the file to compare to # the result is: @@ -62,15 +130,15 @@ function compare_to_result() local sav="$2" if [ ! -r "$sav" ]; then return 0; fi test "`cat "$sav"`" = "UNDEFINED" && return 1 - diff -u "$sav" "out/$pattern.res" > "out/$pattern.diff.sav.log" + diff -u "$sav" "$outdir/$pattern.res" > "$outdir/$pattern.diff.sav.log" if [ "$?" == 0 ]; then return 1 fi - sed '/[Ww]arning/d;/[Ee]rror/d' "out/$pattern.res" > "out/$pattern.res2" - sed '/[Ww]arning/d;/[Ee]rror/d' "$sav" > "out/$pattern.sav2" - grep '[Ee]rror' "out/$pattern.res" >/dev/null && echo "Error" >> "out/$pattern.res2" - grep '[Ee]rror' "$sav" >/dev/null && echo "Error" >> "out/$pattern.sav2" - diff -u "out/$pattern.sav2" "out/$pattern.res2" > "out/$pattern.diff.sav.log2" + sed '/[Ww]arning/d;/[Ee]rror/d' "$outdir/$pattern.res" > "$outdir/$pattern.res2" + sed '/[Ww]arning/d;/[Ee]rror/d' "$sav" > "$outdir/$pattern.sav2" + grep '[Ee]rror' "$outdir/$pattern.res" >/dev/null && echo "Error" >> "$outdir/$pattern.res2" + grep '[Ee]rror' "$sav" >/dev/null && echo "Error" >> "$outdir/$pattern.sav2" + diff -u "$outdir/$pattern.sav2" "$outdir/$pattern.res2" > "$outdir/$pattern.diff.sav.log2" if [ "$?" == 0 ]; then return 2 else @@ -96,7 +164,7 @@ function process_result() OLD="" LIST="" FIRST="" - echo >>$xml "" + echo >>$xml "" #for sav in "sav/$engine/fixme/$pattern.res" "sav/$engine/$pattern.res" "sav/fixme/$pattern.res" "sav/$pattern.res" "sav/$pattern.sav"; do for savdir in $savdirs; do sav=$savdir/fixme/$pattern.res @@ -152,79 +220,79 @@ function process_result() esac done OLD=`echo "$OLD" | sed -e 's/ */ /g' -e 's/^ //' -e 's/ $//'` - grep 'NOT YET IMPLEMENTED' "out/$pattern.res" >/dev/null + grep 'NOT YET IMPLEMENTED' "$outdir/$pattern.res" >/dev/null NYI="$?" if [ -n "$SAV" ]; then if [ -n "$OLD" ]; then - echo "[*ok*] out/$pattern.res $SAV - but $OLD remains!" - echo >>$xml "" + echo "[*ok*] $outdir/$pattern.res $SAV - but $OLD remains!" + echo >>$xml "" 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 "" + echo "[*fixme*] $outdir/$pattern.res $FIXME - but $OLD remains!" + echo >>$xml "" remains="$remains $OLD" else - echo "[fixme] out/$pattern.res $FIXME" + echo "[fixme] $outdir/$pattern.res $FIXME" echo >>$xml "" 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 "" todos="$todos $pattern" elif [ -n "$SOSO" ]; then - echo "[======= soso out/$pattern.res $SOSO =======]" - echo >>$xml "" + echo "[======= soso $outdir/$pattern.res $SOSO =======]" + echo >>$xml "" echo >>$xml ">$xml -n 50 + cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50 echo >>$xml "]]>" nok="$nok $pattern" echo "$ii" >> "$ERRLIST" elif [ -n "$SOSOF" ]; then - echo "[======= fixme soso out/$pattern.res $SOSOF =======]" - echo >>$xml "" + echo "[======= fixme soso $outdir/$pattern.res $SOSOF =======]" + echo >>$xml "" echo >>$xml ">$xml -n 50 + cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50 echo >>$xml "]]>" nok="$nok $pattern" echo "$ii" >> "$ERRLIST" elif [ -n "$NSAV" ]; then - echo "[======= fail out/$pattern.res $NSAV =======]" - echo >>$xml "" + echo "[======= fail $outdir/$pattern.res $NSAV =======]" + echo >>$xml "" echo >>$xml ">$xml -n 50 + cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50 echo >>$xml "]]>" nok="$nok $pattern" echo "$ii" >> "$ERRLIST" elif [ -n "$NFIXME" ]; then - echo "[======= changed out/$pattern.res $NFIXME ======]" - echo >>$xml "" + echo "[======= changed $outdir/$pattern.res $NFIXME ======]" + echo >>$xml "" echo >>$xml ">$xml -n 50 + cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50 echo >>$xml "]]>" 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 "" echo >>$xml ">$xml out/$pattern.res + cat -v >>$xml $outdir/$pattern.res echo >>$xml "]]>" 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 ">$xml out/$pattern.cmp.err + cat -v >>$xml $outdir/$pattern.cmp.err echo >>$xml "]]>" fi echo >>$xml "" @@ -235,12 +303,12 @@ need_skip() test "$noskip" = true && return 1 if echo "$1" | grep -f "$engine.skip" >/dev/null 2>&1; then echo "=> $2: [skip]" - echo >>$xml "" + echo >>$xml "" return 0 fi if test -n "$isinterpret" && echo "$1" | grep -f "exec.skip" >/dev/null 2>&1; then echo "=> $2: [skip exec]" - echo >>$xml "" + echo >>$xml "" return 0 fi return 1 @@ -273,11 +341,14 @@ find_nitc() echo "Could not find binary for engine $engine, aborting" exit 1 fi - echo "Find binary for engine $engine: $recent $OPT" + if [ "x$isnode" = "xfalse" ]; then + echo "Found binary for engine $engine: $recent $OPT" + fi NITC=$recent } verbose=false +isnode=false stop=false engine=nitg noskip= @@ -289,6 +360,9 @@ while [ $stop = false ]; do -h) usage; exit;; --engine) engine="$2"; shift; shift;; --noskip) noskip=true; shift;; + --outdir) outdir="$2"; shift; shift;; + --compdir) compdir="$2"; shift; shift;; + --node) isnode=true; shift;; *) stop=true esac done @@ -298,23 +372,23 @@ case $engine in nitg) engine=nitg-s; enginebinname=nitg; - OPT="--separate $OPT" + OPT="--separate $OPT --compile-dir $compdir" ;; nitg-s) enginebinname=nitg; - OPT="--separate $OPT" + OPT="--separate $OPT --compile-dir $compdir" ;; nitg-e) enginebinname=nitg; - OPT="--erasure $OPT" + OPT="--erasure $OPT --compile-dir $compdir" ;; nitg-sg) enginebinname=nitg; - OPT="--semi-global $OPT" + OPT="--semi-global $OPT --compile-dir $compdir" ;; nitg-g) enginebinname=nitg; - OPT="--global $OPT" + OPT="--global $OPT --compile-dir $compdir" ;; nit) engine=niti @@ -330,7 +404,7 @@ case $engine in ;; emscripten) enginebinname=nitg - OPT="-m emscripten_nodejs.nit --semi-global $OPT" + OPT="-m emscripten_nodejs.nit --semi-global $OPT --compile-dir $compdir" savdirs="sav/nitg-sg/" ;; nitc) @@ -351,30 +425,26 @@ savdirs="sav/$engine $savdirs sav/" # Set NIT_DIR if needed [ -z "$NIT_DIR" ] && export NIT_DIR=.. -if sh -c "timelimit echo" 1>/dev/null 2>&1; then - TIMEOUT="timelimit -t 600" -elif sh -c "timeout 1 echo" 1>/dev/null 2>&1; then - TIMEOUT="timeout 600s" -else - echo "No timelimit or timeout command detected. Tests may hang :(" -fi - # Mark to distinguish files among tests # MARK= -# File where error tests are outputed -# Old ERRLIST is backuped -ERRLIST=${ERRLIST:-errlist} -ERRLIST_TARGET=$ERRLIST - if [ $# = 0 ]; then usage; exit fi +# CLEAN the out directory +rm -rf "$outdir/" 2>/dev/null +mkdir "$outdir" 2>/dev/null + +# File where error tests are outputed +# Old ERRLIST is backuped +ERRLIST=${ERRLIST:-errlist} +ERRLIST_TARGET=$ERRLIST + # Initiate new ERRLIST if [ "x$ERRLIST" = "x" ]; then - ERRLIST=/dev=null + ERRLIST=/dev/null else ERRLIST=$ERRLIST.tmp > "$ERRLIST" @@ -383,12 +453,14 @@ fi ok="" nok="" todos="" -xml="tests-$engine.xml" -echo >$xml "" -# 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 "" for ii in "$@"; do if [ ! -f $ii ]; then @@ -411,7 +483,7 @@ for ii in "$@"; do for i in "$ii" `./alterner.pl --start '#' --altsep '_' $ii`; do bf=`basename $i .nit` - ff="out/$bf" + ff="$outdir/$bf" # Sould we skip the alternative for this engine? need_skip $bf $bf $pack && continue @@ -432,10 +504,10 @@ for ii in "$@"; do fi if [ -n "$isinterpret" ]; then - cat > "./$ff.bin" < "$ff.bin" < "$ff.cmp.err" > "$ff.compile.log" ERR=0 @@ -452,7 +524,7 @@ END echo $NITC --no-color $OPT -o "$ffout" "$i" "$includes" $nocc fi NIT_NO_STACK=1 JNI_LIB_PATH=$JNI_LIB_PATH JAVA_HOME=$JAVA_HOME \ - /usr/bin/time --quiet -f%U -o "$ff.time.out" $TIMEOUT $NITC --no-color $OPT -o "$ffout" "$i" $includes $nocc 2> "$ff.cmp.err" > "$ff.compile.log" + saferun -o "$ff.time.out" $NITC --no-color $OPT -o "$ffout" "$i" $includes $nocc 2> "$ff.cmp.err" > "$ff.compile.log" ERR=$? if [ "x$verbose" = "xtrue" ]; then cat "$ff.compile.log" @@ -460,11 +532,11 @@ END fi fi if [ "$engine" = "emscripten" ]; then - echo > "./$ff.bin" "nodejs $ffout \"\$@\"" + echo > "$ff.bin" "nodejs $ffout \"\$@\"" chmod +x "$ff.bin" if grep "Fatal Error: more than one primitive class" "$ff.compile.log" > /dev/null; then echo " [skip] do no not imports kernel" - echo >>$xml "" + echo >>$xml "" continue fi fi @@ -477,7 +549,7 @@ END echo -n "nocc " > "$ff.res" process_result $bf $bf $pack - elif [ -x "./$ff.bin" ]; then + elif [ -x "$ff.bin" ]; then if skip_exec "$bf"; then # No exec > "$ff.res" @@ -489,10 +561,10 @@ END args="" if [ "x$verbose" = "xtrue" ]; then echo "" - echo "NIT_NO_STACK=1 ./$ff.bin" $args - fi + echo "NIT_NO_STACK=1 $ff.bin" $args + fi NIT_NO_STACK=1 LD_LIBRARY_PATH=$JNI_LIB_PATH \ - /usr/bin/time --quiet -f%U -a -o "$ff.time.out" $TIMEOUT "./$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err" + saferun -a -o "$ff.time.out" "$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err" mv $ff.time.out $ff.times.out awk '{ SUM += $1} END { print SUM }' $ff.times.out > $ff.time.out @@ -532,12 +604,12 @@ END rm -rf "$fff.res" "$fff.err" "$fff.write" 2> /dev/null if [ "x$verbose" = "xtrue" ]; then echo "" - echo "NIT_NO_STACK=1 ./$ff.bin" $args + echo "NIT_NO_STACK=1 $ff.bin" $args fi echo -n "==> $name " - echo "./$ff.bin $args" > "./$fff.bin" - chmod +x "./$fff.bin" - WRITE="$fff.write" /usr/bin/time --quiet -f%U -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $TIMEOUT ./$fff.bin < $ffinputs > $fff.res 2>$fff.err" + echo "$ff.bin $args" > "$fff.bin" + chmod +x "$fff.bin" + WRITE="$fff.write" saferun -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $fff.bin < $ffinputs > $fff.res 2>$fff.err" if [ "x$verbose" = "xtrue" ]; then cat "$fff.res" cat >&2 "$fff.err" @@ -554,7 +626,7 @@ END process_result $bff " $name" $pack done < $fargs fi - elif [ -f "./$ff.bin" ]; then + elif [ -f "$ff.bin" ]; then #Not executable (platform?)" > "$ff.res" process_result $bf "$bf" $pack @@ -567,21 +639,23 @@ END done done -echo "engine: $engine ($enginebinname $OPT)" -echo "ok: " `echo $ok | wc -w` "/" `echo $ok $nok $nos $todos | wc -w` +if [ "x$isnode" = "xfalse" ]; then + echo "engine: $engine ($enginebinname $OPT)" + echo "ok: " `echo $ok | wc -w` "/" `echo $ok $nok $nos $todos | wc -w` -if [ -n "$nok" ]; then - echo "fail: $nok" - echo "There were $(echo $nok | wc -w) errors ! (see file $ERRLIST)" -fi -if [ -n "$nos" ]; then - echo "no sav: $nos" -fi -if [ -n "$todos" ]; then - echo "todo/fixme: $todos" -fi -if [ -n "$remains" ]; then - echo "sav that remains: $remains" + if [ -n "$nok" ]; then + echo "fail: $nok" + echo "There were $(echo $nok | wc -w) errors ! (see file $ERRLIST)" + fi + if [ -n "$nos" ]; then + echo "no sav: $nos" + fi + if [ -n "$todos" ]; then + echo "todo/fixme: $todos" + fi + if [ -n "$remains" ]; then + echo "sav that remains: $remains" + fi fi # write $ERRLIST