Merge: Nitmd benches
authorJean Privat <jean@pryen.org>
Tue, 27 Jan 2015 01:29:04 +0000 (08:29 +0700)
committerJean Privat <jean@pryen.org>
Tue, 27 Jan 2015 01:29:04 +0000 (08:29 +0700)
Copied from README:

# Bench Markdown

Benches markdown parsers.

## Usage

        ./bench_markdown.sh all

## Engines

* nitmd
* txtmark 0.11 (https://github.com/rjeschke/txtmark)
* markdown4j 2.2 (https://code.google.com/p/markdown4j/)

## Benches

Benches are inspired from the [Henkelmann's Actuarius benches](http://henkelmann.eu/2011/01/10/performance_comparison_of_markdown_processor_for_the_jvm).

Benches are variations of the same text generate from `benches/plain.md`
This text is De finibus bonorum et malorum which is said to be the basis for
the commonly used Lorem Ipsum test text.

The generator rudely chops it up in a configurable number of paragraphs,
lines per paragraph and approximate chars per line.
Depending on the variation, it “decorates” parts of the text with Markdown syntax.
All tests consist of 30 Paragraphs with 20 lines each and approx. 80 chars per line (before “decoration”).

Here are the descriptions of the variations:

* Plain Paragraphs: No modifications, just the plain text.

* Emphasis: Every word emphasized,

  so the input `foo` is decorated as `*foo*` which should render as `<em>foo</em>`

* Strong: Every word emphasized,

  `foo` → `**foo**` → `<strong>foo</strong>`

* Inline Code: Every word marked as inline code:

  `foo` → ````foo```` → `<code>foo</code>`

* Fast Links: Every word a link without title or wrapped text:

  `foo` → `<foo>` → `<a href="foo">foo</a>`

* Special XML Chars: Every word is replaced by chars that need to be escaped in XML:

  `foo` → `"><&` → `&quot;&gt;&lt;'&amp;`

* Inline HTML: Every word is wrapped in verbatim HTML:

  `foo` → `<blink>foo</blink>` → `<blink>foo</blink>`

* Manual Line Breaks: Every line gets appended with `\n`:

  `some line` → `some line\n` → `some line<br/>\n`

* Full Links: Every word is turned into a link with wrapped text, url and title:

  `foo` → `[foo](http://example.com/foo "foo Title")` → `<a href="http://example.com/foo" title="foo Title">foo</a>`

* Full Images: Like full links, every word turned into an image reference

* Reference Links: Every word is turned into a link reference with an increasing id counter.
  The link reference definition is added after the paragraph the link occurs in:

  `foo` → `[foo][id123]` → `<a href="http://example.com/foo" title="foo Title">foo</a>`

* Block Quotes: Every paragraph is turned into a block quote by prepending a `>` to each line.

* Code Blocks: Every paragraph is turned into a code block by prepending four spaces to each line.

* Unordered Lists: Every paragraph is turned into a list with an item for each line by prepending `*` to each line.

* Mixed Test: A mix of all of the above: Some paragraphs lists, some code, some word emphasized etc.

Variation details can be found in `benches/gen_bench.nit`.

Pull-Request: #1116
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

163 files changed:
.gitignore
contrib/nitiwiki/src/wiki_html.nit
contrib/nitiwiki/tests/res/nitiwiki_render.res
contrib/nitiwiki/tests/res/nitiwiki_status.res
contrib/pep8analysis/src/pep8analysis_web.nit
examples/mnit_simple/assets/testsound.ogg [moved from examples/mnit_simple/assets/sound.ogg with 100% similarity]
examples/mnit_simple/res/raw/testsound.ogg [moved from examples/mnit_simple/res/raw/sound.ogg with 100% similarity]
examples/mnit_simple/res/values/dimens.xml
examples/mnit_simple/src/test_audio.nit
examples/rosettacode/doors_with_classes.nit [new file with mode: 0644]
lib/android/README.md [new file with mode: 0644]
lib/android/android.nit
lib/android/assets_and_resources.nit
lib/android/audio.nit
lib/android/native_app_glue.nit
lib/android/ui.nit
lib/app/audio.nit [new file with mode: 0644]
lib/array_debug.nit
lib/bucketed_game.nit
lib/c.nit
lib/cocoa/app_kit.nit
lib/cocoa/cocoa.nit
lib/cocoa/examples/cocoa_message_box.nit
lib/cocoa/examples/hello_cocoa.nit
lib/cocoa/foundation.nit
lib/cpp.nit
lib/curl/curl.nit
lib/filter_stream.nit
lib/github/api.nit
lib/github/cache.nit [new file with mode: 0644]
lib/github/events.nit [new file with mode: 0644]
lib/github/github.nit
lib/github/hooks.nit [new file with mode: 0644]
lib/glesv2/examples/opengles2_hello_triangle.nit
lib/glesv2/glesv2.nit
lib/ini.nit
lib/java/java.nit
lib/json/static.nit
lib/jvm.nit
lib/more_collections.nit
lib/mpi.nit
lib/neo4j/graph/graph.nit [new file with mode: 0644]
lib/neo4j/graph/json_graph_store.nit [new file with mode: 0644]
lib/neo4j/graph/sequential_id.nit [new file with mode: 0644]
lib/neo4j/json_store.nit [deleted file]
lib/nitcc_runtime.nit
lib/opts.nit
lib/pipeline.nit
lib/progression.nit [new file with mode: 0644]
lib/pthreads/extra.nit
lib/pthreads/pthreads.nit
lib/realtime.nit
lib/scene2d.nit
lib/sdl.nit
lib/sdl2/image.nit
lib/socket/socket.nit
lib/socket/socket_c.nit
lib/standard/collection/abstract_collection.nit
lib/standard/collection/array.nit
lib/standard/collection/hash_collection.nit
lib/standard/gc.nit
lib/standard/stream.nit
lib/standard/string.nit
lib/symbol.nit
lib/websocket/examples/websocket_server.nit
lib/websocket/websocket.nit
share/libgc/.gitignore [new file with mode: 0644]
share/libgc/android-setup-libgc.sh [new file with mode: 0755]
share/man/nitc.md
src/changelog.sh
src/compiler/abstract_compiler.nit
src/compiler/compiler.nit
src/compiler/compiler_ffi.nit
src/compiler/separate_compiler.nit
src/compiler/separate_erasure_compiler.nit
src/doc/doc_down.nit
src/doc/doc_pages.nit
src/ffi/c.nit
src/ffi/c_compiler_options.nit
src/ffi/cpp.nit
src/ffi/ffi.nit
src/ffi/ffi_base.nit
src/ffi/header_dependency.nit
src/ffi/java.nit
src/ffi/objc.nit
src/frontend/check_annotation.nit
src/interpreter/debugger_socket.nit
src/interpreter/primitive_types.nit
src/loader.nit
src/model/mmodule.nit
src/model/model.nit
src/model/mproject.nit
src/nitni/nitni_base.nit
src/nitpretty.nit
src/platform/android.nit [moved from src/compiler/android_platform.nit with 82% similarity]
src/platform/android_annotations.nit [moved from src/compiler/android_annotations.nit with 100% similarity]
src/platform/emscripten.nit [moved from src/compiler/emscripten_platform.nit with 94% similarity]
src/platform/platform.nit [moved from src/platform.nit with 97% similarity]
src/platform/pnacl.nit [moved from src/compiler/pnacl_platform.nit with 99% similarity]
src/pretty.nit
src/semantize/typing.nit
src/toolcontext.nit
tests/base_attr_init_val_block.nit
tests/error_annot_c_compiler.nit
tests/nitpretty.args
tests/sav/base_attr_init_val_block_alt1.res [new file with mode: 0644]
tests/sav/base_attr_init_val_block_alt2.res [new file with mode: 0644]
tests/sav/doors_with_classes.res [new file with mode: 0644]
tests/sav/error_annot_c_compiler_alt1.res
tests/sav/error_annot_c_compiler_alt2.res
tests/sav/error_annot_c_compiler_alt3.res
tests/sav/error_annot_c_compiler_alt4.res
tests/sav/error_annot_c_compiler_alt5.res
tests/sav/error_annot_c_compiler_alt6.res
tests/sav/fixme/nitpretty_args22.res [deleted file]
tests/sav/fixme/nitpretty_args48.res [new file with mode: 0644]
tests/sav/fixme/nitpretty_args58.res [new file with mode: 0644]
tests/sav/nitpretty_args21.res
tests/sav/nitpretty_args25.res
tests/sav/nitpretty_args27.res
tests/sav/nitpretty_args28.res [new file with mode: 0644]
tests/sav/nitpretty_args29.res [new file with mode: 0644]
tests/sav/nitpretty_args30.res [new file with mode: 0644]
tests/sav/nitpretty_args31.res [new file with mode: 0644]
tests/sav/nitpretty_args32.res [new file with mode: 0644]
tests/sav/nitpretty_args33.res [new file with mode: 0644]
tests/sav/nitpretty_args34.res [new file with mode: 0644]
tests/sav/nitpretty_args35.res [new file with mode: 0644]
tests/sav/nitpretty_args36.res [new file with mode: 0644]
tests/sav/nitpretty_args37.res [new file with mode: 0644]
tests/sav/nitpretty_args38.res [new file with mode: 0644]
tests/sav/nitpretty_args39.res [new file with mode: 0644]
tests/sav/nitpretty_args40.res [new file with mode: 0644]
tests/sav/nitpretty_args41.res [new file with mode: 0644]
tests/sav/nitpretty_args42.res [new file with mode: 0644]
tests/sav/nitpretty_args43.res [new file with mode: 0644]
tests/sav/nitpretty_args44.res [new file with mode: 0644]
tests/sav/nitpretty_args45.res [new file with mode: 0644]
tests/sav/nitpretty_args46.res [new file with mode: 0644]
tests/sav/nitpretty_args47.res [new file with mode: 0644]
tests/sav/nitpretty_args48.res [new file with mode: 0644]
tests/sav/nitpretty_args49.res [new file with mode: 0644]
tests/sav/nitpretty_args50.res [new file with mode: 0644]
tests/sav/nitpretty_args51.res [new file with mode: 0644]
tests/sav/nitpretty_args52.res [moved from tests/sav/fixme/nitpretty_args21.res with 93% similarity]
tests/sav/nitpretty_args53.res [new file with mode: 0644]
tests/sav/nitpretty_args54.res [new file with mode: 0644]
tests/sav/nitpretty_args55.res [new file with mode: 0644]
tests/sav/nitpretty_args56.res [new file with mode: 0644]
tests/sav/nitpretty_args57.res [new file with mode: 0644]
tests/sav/nitpretty_args58.res [new file with mode: 0644]
tests/sav/nitpretty_args59.res [new file with mode: 0644]
tests/sav/nitpretty_args60.res [new file with mode: 0644]
tests/sav/nitpretty_args61.res [new file with mode: 0644]
tests/sav/nitpretty_args62.res [new file with mode: 0644]
tests/sav/nitpretty_args7.res
tests/sav/nitpretty_args9.res
tests/sav/nitunit_args4.res
tests/sav/nitunit_args5.res
tests/sav/opengles2_hello_triangle.res [deleted file]
tests/test_annot_c_compiler.nit
tests/test_pretty/test_annot1.nit
tests/test_pretty/test_prop1.nit

index 5cd49a0..2156619 100644 (file)
@@ -55,3 +55,4 @@ nitunit.xml
 *.stub.nit.[ch]
 
 .metadata/*
+.github_data
index 7f213b7..89546ce 100644 (file)
@@ -81,6 +81,7 @@ redef class WikiSection
                end
                var index = self.index
                if index isa WikiSectionIndex then
+                       wiki.message("Render auto-index for section {out_path}", 1)
                        index.is_dirty = true
                        add_child index
                end
@@ -322,6 +323,10 @@ class WikiSectionIndex
        # The section described by `self`.
        var section: WikiSection
 
+       redef fun title do return section.title
+
+       redef fun url do return section.url
+
        redef var is_dirty = false
 
        redef fun tpl_article do
index adf25a7..25f17ab 100644 (file)
@@ -1 +1,6 @@
 Render section out
+Render section out/sec1
+Render section out/sec2
+Render auto-index for section out/sec2
+Render section out/sec2/sub-sec21
+Render section out/sec2/sub-sec22
index 936a764..9eb2262 100644 (file)
@@ -6,5 +6,12 @@ url: http://localhost/
 There is modified files:
  + pages
  + /pages/index.md
+ + pages/sec1
+ + /pages/sec1/index.md
+ + pages/sec2
+ + pages/sec2/sub-sec21
+ + /pages/sec2/sub-sec21/index.md
+ + pages/sec2/sub-sec22
+ + /pages/sec2/sub-sec22/index.md
 
 Use nitiwiki --render to render modified files
index dacc9ff..b991952 100644 (file)
@@ -20,8 +20,8 @@
 # analysis results. The result graph will be sent to the JavaScript function
 # `show_graph` with the source of the graph in Graphviz's dot.
 module pep8analysis_web is
-       cpp_compiler_option("--std=c++11 --bind")
-       c_linker_option("--bind")
+       cppflags "--std=c++11 --bind"
+       ldflags "--bind"
 end
 
 import emscripten
index da43c8f..fa1d020 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <dimen name="test_dimen_1">25dp</dimen>
-    <dimen name="test_dimen_2">150dp</dimen>
-</resources> 
+    <dimen name="test_dimen_1">25px</dimen>
+    <dimen name="test_dimen_2">150px</dimen>
+</resources>
index decf7c5..88a2812 100644 (file)
@@ -21,27 +21,29 @@ import simple_android
 import android::audio
 
 redef class App
-       var soundsp: Sound
-       var soundmp: Sound
+       var soundsp: nullable Sound
+       var soundmp: nullable Sound
+       var test_assets = false
+       var test_ressources = true
 
-       redef fun init_window
+       redef fun window_created
        do
                super
-
-               default_mediaplayer.reset
-               manage_audio_mode
-
-               # Retrieve sound
-               soundsp = load_sound("sound.ogg")
-               soundmp = load_music("xylofon.ogg")
+               if test_assets then
+                       soundsp = load_sound("testsound.ogg")
+                       soundmp = load_music("xylofon.ogg")
+               end
+               if test_ressources then
+                       soundsp = load_sound_from_res("testsound")
+                       soundmp = load_music_from_res("xylofon")
+               end
                default_mediaplayer.looping = true
-               default_mediaplayer.prepare
                soundmp.play
        end
 
        redef fun input( ie )
        do
-               if ie isa PointerEvent and ie.depressed then 
+               if ie isa PointerEvent and ie.depressed then
                        soundsp.play
                end
                return super
diff --git a/examples/rosettacode/doors_with_classes.nit b/examples/rosettacode/doors_with_classes.nit
new file mode 100644 (file)
index 0000000..62c5f14
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: 100 doors
+# SEE: <http://rosettacode.org/wiki/100_doors>
+
+# A door with two states: open or closed
+class Door
+       # Is this door open?
+       var open = false
+
+       # Toggle bool value of open
+       fun toggle do open = not open
+
+       redef fun to_s: String
+       do
+               return if open then "Open" else "Closed"
+       end
+end
+
+var doors = new Array[Door]
+for door in [0..100[ do doors.add(new Door)
+
+var n = 100
+for visit in [0..n[ do
+       var i = visit
+       while i < n do
+               doors[i].toggle
+               i += visit+1
+       end
+end
+for i in [0..n[ do print "Door {i+1}: {doors[i]}"
diff --git a/lib/android/README.md b/lib/android/README.md
new file mode 100644 (file)
index 0000000..1f4e659
--- /dev/null
@@ -0,0 +1,108 @@
+The `android` module provides support for the Android platform
+
+# Compilation for Android
+
+The compiler generates an APK file as the output when the `android`
+module is imported by the compilation target. The path to the generated
+file can be specified using the `-o` and `--dir` options.
+
+# Host system configuration
+
+To compile for Android, you must install the Android SDK and NDK.
+The tools `android`, `ndk-build` and `ant` must be in your PATH.
+
+# Configure your Android application
+
+The `app.nit` framework and this project offers some services to
+customized the generated Android application.
+
+## Module annotations
+
+* `app_version` specifies the version of the generated APK file.
+It takes 3 arguments: the major, minor and revision version numbers.
+The special function `git_revision` will use the prefix of the hash of the
+latest git commit. The default version is 1.0.
+
+    Example: `app_version(1, 0, git_revision)`
+
+* `app_name` takes a single argument, the visible name of the Android
+application. By default, the compiler would use the name of the target
+module. This name will be used as the name of the main activity and
+as the launcher name.
+
+    Example: `app_name "My App"`
+
+* `java_package` specifies the package used by the generated Java
+classes and the APK file. Once the application is published, this
+value should not be changed. By default, the compiler will use
+the package `org.nitlanguage.{module_name}`.
+
+* Custom information can be added to the Android manifest file
+using the annotations `android_manifest`, `android_manifest_application`
+and `android_manifest_activity`.
+
+    Example usage to specify an extra permission:
+
+    ~~~
+    android_manifest """<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>"""
+    ~~~
+
+* The API version target can be specified with `min_api_version`,
+`max_api_version` and `target_api_version`. These take a single
+integer as argument. They are applied in the Android manifest as
+`minSdkVesion`, `targetSdkVersion` and `maxSdkVersion`.
+
+    See http://developer.android.com/guide/topics/manifest/uses-sdk-element.html
+
+## Project entry points
+
+Importing `android::landscape` or `android::portrait` locks the generated
+application in the specified orientation. This can be useful for games and
+other multimedia applications.
+
+# Compilation modes
+
+There are two compilation modes for the Android platform, debug and release.
+Theses modes are also applied to the generated Android projects.
+The compilation mode is specified as an argument to `nitc`, only
+`--release` can be specified as debug is the default behavior.
+
+## Debug mode
+
+Debug mode enables compiling to an APK file without handling signing keys
+and their password. The APK file can be installed to a local device with
+USB debugging enabled, but it cannot be published on the Play Store.
+
+By default, `nitc` will compile Android applications in debug mode.
+
+## Release mode
+
+Building in release mode will use your private key to sign the
+APK file, it can then be published on the Play Store.
+
+1. Have a keystore with a valid key to sign your APK file.
+
+    To create a new keystore, avoid using the default values of `jarsigner`
+as they change between versions of the Java SDK. You should instead use a
+command similar to the following, replacing `KEYSTORE_PATH` and `KEY_ALIAS`
+with the desired values.
+
+    ~~~
+    keytool -genkey -keystore KEYSTORE_PATH -alias KEY_ALIAS -sigalg MD5withRSA -keyalg RSA -keysize 1024 -validity 10000
+    ~~~
+
+2. Set the environment variables used by `nitc`: `KEYSTORE`, `KEY_ALIAS` and
+optionally `TSA_SERVER`. These settings can be set in a startup script such as
+`~/.bashrc` or in a local Makefile.
+
+    You can use the following commands by replacing the right hand values
+to your own configuration.
+
+    ~~~
+    export KEYSTORE=keystore_path
+    export KEY_ALIAS=key_alias
+    export TSA_SERVER=timestamp_authority_server_url # Optional
+    ~~~
+
+3. Call `nitc` with the `--release` options. You will be prompted for the
+required passwords as needed by `jarsigner`.
index 5523f30..584b89b 100644 (file)
 
 # Android services and implementation of app.nit
 #
-# To use this module and compile for Android, you must install the
-# Android SDK (with API level 10) and NDK (with the API level 9).
-# The tools `android`, `ndk-build` and `ant` must be in your PATH.
-#
 # This module provides basic logging facilities, advanced logging can be
 # achieved by importing `android::log`.
 module android
index 82ae3d1..8195e2c 100644 (file)
@@ -102,6 +102,13 @@ extern class NativeAssetManager in "Java" `{ android.content.res.AssetManager `}
                }
                return afd;
        `}
+
+       # HACK for bug #845
+       redef fun new_global_ref import sys, Sys.jni_env `{
+               Sys sys = NativeResources_sys(recv);
+               JNIEnv *env = Sys_jni_env(sys);
+               return (*env)->NewGlobalRef(env, recv);
+       `}
 end
 
 # Assets manager using a `NativeAssetManager` to manage android assets
@@ -138,14 +145,18 @@ class AssetManager
 
        # Open an asset using ACCESS_STREAMING mode, returning a NativeInputStream
        fun open(file_name: String): NativeInputStream do
+               sys.jni_env.push_local_frame(1)
                var return_value =  native_assets_manager.open(file_name.to_java_string)
+               sys.jni_env.pop_local_frame
                return return_value
        end
 
        # Open an asset using it's name and returning a NativeAssetFileDescriptor
        # `file_name` is
        fun open_fd(file_name: String): NativeAssetFileDescriptor do
-               var return_value = native_assets_manager.open_fd(file_name.to_java_string)
+               sys.jni_env.push_local_frame(1)
+               var return_value = native_assets_manager.open_fd(file_name.to_java_string).new_global_ref
+               sys.jni_env.pop_local_frame
                return return_value
        end
 
@@ -158,7 +169,7 @@ class AssetManager
        # Return a bitmap from the assets
        fun bitmap(name: String): NativeBitmap do
                sys.jni_env.push_local_frame(1)
-               var return_value = new NativeBitmap.from_stream(native_assets_manager.open(name.to_java_string))
+               var return_value = new NativeBitmap.from_stream(native_assets_manager.open(name.to_java_string)).new_global_ref
                sys.jni_env.pop_local_frame
                return return_value
        end
@@ -291,6 +302,14 @@ extern class NativeBitmap in "Java" `{ android.graphics.Bitmap `}
        new from_resources(res: NativeResources, id: Int) in "Java" `{ return BitmapFactory.decodeResource(res, (int)id); `}
        fun width: Int in "Java" `{ return recv.getWidth(); `}
        fun height: Int in "Java" `{ return recv.getHeight(); `}
+
+       # HACK for bug #845
+       redef fun new_global_ref import sys, Sys.jni_env `{
+               Sys sys = NativeResources_sys(recv);
+               JNIEnv *env = Sys_jni_env(sys);
+               return (*env)->NewGlobalRef(env, recv);
+       `}
+
 end
 
 # Android AssetFileDescriptor, can be retrieve by AssetManager and used to load a sound in a SoundPool
@@ -337,6 +356,13 @@ extern class NativeAssetFileDescriptor in "Java" `{ android.content.res.AssetFil
        fun length: Int in "Java" `{ return (int)recv.getLength(); `}
        fun start_offset: Int in "Java" `{ return (int)recv.getStartOffset(); `}
        redef fun to_s: String import JavaString.to_s in "Java" `{ return JavaString_to_s(recv.toString()); `}
+
+       # HACK for bug #845
+       redef fun new_global_ref import sys, Sys.jni_env `{
+               Sys sys = NativeResources_sys(recv);
+               JNIEnv *env = Sys_jni_env(sys);
+               return (*env)->NewGlobalRef(env, recv);
+       `}
 end
 
 # Native class representing something drawable, can be retrieved from the resources
index 366c7d4..2301bd0 100644 (file)
@@ -1,4 +1,4 @@
-# this file is part of NIT ( http://www.nitlanguage.org ).
+# This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2014 Romain Chanoir <romain.chanoir@viacesi.fr>
 #
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Android audio services
+# Android audio services, wraps a part of android audio API
 #
-# You can get a sound by loading it with a `SoundPool` or a `MediaPlayer`
-# the recommended way to load a sound is by it's resource ID or it's name
-# other ways are for advanced use
+# For this example, the sounds "test_sound" and "test_music" are located in the "assets/sounds" folder,
+# they both have ".ogg" extension. "test_sound" is a short sound and "test_music" a music track
+# ~~~nitish
+# # Note that you need to specify the path from "assets" folder and the extension
+# var s = app.load_sound("sounds/test_sound.ogg")
+# var m = app.load_music("sounds/test_music.ogg")
+# s.play
+# m.play
+# ~~~
+#
+# Now, the sounds are in "res/raw"
+# ~~~nitish
+# s = app.load_sound_from_res("test_sound")
+# m = app.load_music_from_res("test_sound")
+# s.play
+# m.play
+# ~~~
+#
+# See http://developer.android.com/reference/android/media/package-summary.html for more infos
 module audio
 
 import java
 import java::io
 import assets_and_resources
-import app
+import app::audio
 
 in "Java" `{
        import android.media.MediaPlayer;
        import android.media.SoundPool;
        import java.io.IOException;
        import android.media.AudioManager;
+       import android.media.AudioManager.OnAudioFocusChangeListener;
        import android.content.Context;
        import android.util.Log;
 `}
 
+# FIXME: This listener is not working at the moment, but is needed to gain or give up the audio focus
+# of the application
+in "Java inner" `{
+       static OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
+               public void onAudioFocusChange(int focusChange) {
+                       if(focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+                       }else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+                       }else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+                       }
+               }
+       };
+`}
+
 # AudioManager of the application, used to manage the audio mode
 extern class NativeAudioManager in "Java" `{ android.media.AudioManager `}
        super JavaObject
 
+       # Current audio mode.
+       # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 )
        fun mode: Int in "Java" `{ return recv.getMode(); `}
+
+       # Sets the audio mode.
+       # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 )
        fun mode=(i: Int) in "Java" `{ recv.setMode((int)i); `}
-       fun wired_headset_on: Bool in "Java" `{ return recv.isWiredHeadsetOn(); `}
-       fun wired_headset_on=(b: Bool) in "Java" `{ recv.setWiredHeadsetOn(b); `}
-       fun speakerphone_on: Bool in "Java" `{ return recv.isSpeakerphoneOn(); `}
-       fun speakerphone_on=(b: Bool) in "Java" `{ recv.setSpeakerphoneOn(b); `}
-       fun manage_audio_mode in "Java" `{
-               recv.setMode(0);
-               if (recv.isWiredHeadsetOn()) {
-                       recv.setSpeakerphoneOn(false);
-               } else {
-                       recv.setSpeakerphoneOn(true);
-               }
+
+       # Sends a request to obtain audio focus
+       fun request_audio_focus: Int in "Java" `{
+               return recv.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        `}
 
+       # Gives up audio focus
+       fun abandon_audio_focus: Int in "Java" `{ return recv.abandonAudioFocus(afChangeListener); `}
 end
 
 # Media Player from Java, used to play long sounds or musics, not simultaneously
 # This is a low-level class, use `MediaPlater` instead
-extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
+private extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
        super JavaObject
 
-       new in "Java" `{ return new MediaPlayer(); `}
+       new in "Java" `{
+               MediaPlayer mp = new MediaPlayer();
+               mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+               return mp;
+       `}
        fun start in "Java" `{ recv.start(); `}
        fun prepare in "Java" `{
                try {
@@ -104,7 +138,7 @@ end
 
 # Sound Pool from Java, used to play sounds simultaneously
 # This is a low-level class, use `SoundPool`instead
-extern class NativeSoundPool in "Java" `{ android.media.SoundPool `}
+private extern class NativeSoundPool in "Java" `{ android.media.SoundPool `}
        super JavaObject
 
        new(max_streams, stream_type, src_quality: Int) in "Java" `{
@@ -165,7 +199,7 @@ class SoundPool
                return new SoundSP(null, nsoundpool.load_asset_fd(afd, priority), self)
        end
 
-       # Load the sound from it's resource id
+       # Load the sound from its resource id
        fun load_id(context: NativeActivity, id:Int): Sound do
                return new SoundSP(null, nsoundpool.load_id(context, id, priority), self)
        end
@@ -184,7 +218,7 @@ class SoundPool
                return nsoundpool.play(id, left_volume, right_volume, priority, looping, rate)
        end
 
-       # Load a sound by it's name in the resources, the sound must be in the `res/raw` folder
+       # Load a sound by its name in the resources, the sound must be in the `res/raw` folder
        fun load_name(resource_manager: ResourcesManager, context: NativeActivity, sound: String): Sound do
                var id = resource_manager.raw_id(sound)
                return new SoundSP(id, nsoundpool.load_id(context, id, priority), self)
@@ -231,8 +265,11 @@ end
 class MediaPlayer
        private var nmedia_player: NativeMediaPlayer is noinit
 
+       # Used to control the state of the mediaplayer
+       private var is_prepared = false is writable
+
        # The sound associated with this mediaplayer
-       var sound: nullable Sound = null
+       var sound: nullable Sound = null is writable
 
        # Create a new MediaPlayer, but no sound is attached, you'll need
        # to use `load_sound` before using it
@@ -249,20 +286,21 @@ class MediaPlayer
        fun load_sound(id: Int, context: NativeActivity): Sound do
                self.nmedia_player = self.nmedia_player.create(context, id)
                self.sound = new SoundMP(id, self)
+               self.is_prepared = true
                return self.sound.as(not null)
        end
 
-       # Starts or resume playback
+       # Starts or resumes playback
        # REQUIRE `self.sound != null`
        fun start do
-               assert sound != null
+               if not is_prepared then prepare
                nmedia_player.start
        end
 
        # Stops playback after playback has been stopped or paused
        # REQUIRE `self.sound != null`
        fun stop do
-               assert sound != null
+               is_prepared = false
                nmedia_player.stop
        end
 
@@ -271,6 +309,7 @@ class MediaPlayer
        fun prepare do
                assert sound != null
                nmedia_player.prepare
+               is_prepared = true
        end
 
        # Pauses playback
@@ -292,7 +331,7 @@ class MediaPlayer
        # Reset MediaPlayer to its initial state
        fun reset do nmedia_player.reset
 
-       # Sets the datasource (file-pathor http/rtsp URL) to use
+       # Sets the datasource (file-path or http/rtsp URL) to use
        fun data_source(path: String): Sound do
                sys.jni_env.push_local_frame(1)
                nmedia_player.data_source_path(path.to_java_string)
@@ -323,14 +362,10 @@ class MediaPlayer
        fun stream_type=(stream_type: Int) do nmedia_player.stream_type = stream_type
 end
 
-# Represents an android sound that can be played by a SoundPool or a MediaPlayer
-# The only way to get a sound is by a MediaPlayer, a SoundPool, or the App
-abstract class Sound
+redef class Sound
 
-       # The resource ID of this sound
+       # Resource ID of this sound
        var id: nullable Int
-
-       fun play is abstract
 end
 
 # Sound implemented with a SoundPool
@@ -350,6 +385,8 @@ class SoundSP
        end
 
        redef fun play do soundpool.play(soundpool_id)
+       redef fun pause do soundpool.pause_stream(soundpool_id)
+       redef fun resume do soundpool.resume(soundpool_id)
 end
 
 # Sound Implemented with a MediaPlayer
@@ -364,53 +401,81 @@ class SoundMP
                self.media_player = media_player
        end
 
-       redef fun play do self.media_player.start
+       redef fun play do media_player.start
+       redef fun pause do media_player.pause
+       redef fun resume do play
 end
 
 redef class App
 
+       # Sounds handled by the application, when you load a sound, it's added to this list.
+       # This array is used in `pause` and `resume`
+       private var sounds = new Array[Sound]
+
+       # Returns the default MediaPlayer of the application.
+       # When you load a music, it goes in this MediaPlayer.
+       # Use it for advanced sound management
        fun default_mediaplayer: MediaPlayer is cached do return new MediaPlayer
+
+       # Returns the default MediaPlayer of the application.
+       # When you load a short sound (not a music), it's added to this soundpool.
+       # Use it for advanced sound management.
        fun default_soundpool: SoundPool is cached do return new SoundPool
 
        # Get the native audio manager
-       private fun audio_manager: NativeAudioManager import native_activity in "Java" `{
+       fun audio_manager: NativeAudioManager import native_activity in "Java" `{
                return (AudioManager)App_native_activity(recv).getSystemService(Context.AUDIO_SERVICE);
        `}
 
-       # Manages whether the app sound need to be in headphones mode or not
-       # TODO: this method is not ideal, need to find a better way
-       fun manage_audio_mode import native_activity in "Java" `{
-               AudioManager manager = (AudioManager)App_native_activity(recv).getSystemService(Context.AUDIO_SERVICE);
-               manager.setMode(0);
-                       if (manager.isWiredHeadsetOn()) {
-                               manager.setSpeakerphoneOn(false);
-                       } else {
-                               manager.setSpeakerphoneOn(true);
-                       }
+       # Sets the stream of the app to STREAM_MUSIC.
+       # STREAM_MUSIC is the default stream used by android apps.
+       private fun manage_audio_stream import native_activity, native_app_glue in "Java" `{
+               App_native_activity(recv).setVolumeControlStream(AudioManager.STREAM_MUSIC);
        `}
 
-       # Retrieve a sound with a soundpool in the `assets` folder using it's name
+       # Retrieves a sound with a soundpool in the `assets` folder using its name.
        # Used to play short songs, can play multiple sounds simultaneously
-       fun load_sound(path: String): Sound do
-               return default_soundpool.load_asset_fd(asset_manager.open_fd(path))
+       redef fun load_sound(path: String): Sound do
+               return add_to_sounds(default_soundpool.load_asset_fd(asset_manager.open_fd(path)))
        end
 
-       # Retrieve a music with a media player in the `assets`folder using it's name
+       # Retrieves a music with a media player in the `assets` folder using its name.
        # Used to play long sounds or musics, can't play multiple sounds simultaneously
        fun load_music(path: String): Sound do
                var fd = asset_manager.open_fd(path)
-               var sound =  default_mediaplayer.data_source_fd(fd.file_descriptor, fd.start_offset, fd.length)
-               return sound
+               return add_to_sounds(default_mediaplayer.data_source_fd(fd.file_descriptor, fd.start_offset, fd.length))
        end
 
-       # same as `load_sound` but load the sound from the `res\raw` folder
+       # Same as `load_sound` but load the sound from the `res/raw` folder
        fun load_sound_from_res(sound_name: String): Sound do
-               return default_soundpool.load_name(resource_manager,self.native_activity, sound_name)
+               return add_to_sounds(default_soundpool.load_name(resource_manager,self.native_activity, sound_name))
        end
 
-       # same as `load_music` but load the sound from the `res\raw` folder
+       # Same as `load_music` but load the sound from the `res/raw` folder
        fun load_music_from_res(music: String): Sound do
-               return default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity)
+               return add_to_sounds(default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity))
        end
 
+       # Factorizes `sounds.add` to use it in `load_music`, `load_sound`, `load_music_from_res` and `load_sound_from_res`
+       private fun add_to_sounds(sound: Sound): Sound do
+               sounds.add(sound)
+               return sound
+       end
+
+       redef fun pause do
+               for s in sounds do s.pause
+               audio_manager.abandon_audio_focus
+       end
+
+       redef fun init_window do
+               super
+               audio_manager.request_audio_focus
+               manage_audio_stream
+       end
+
+       redef fun resume do
+               super
+               audio_manager.request_audio_focus
+               for s in sounds do s.resume
+       end
 end
index fc5a19f..af3c797 100644 (file)
@@ -301,7 +301,10 @@ extern class NativeAppGlue `{ struct android_app* `}
        # We use the `userData` field of the C structure to store an handle to
        # the associated App
        private fun user_data: App `{ return recv->userData; `}
-       private fun user_data=(val: App) `{ recv->userData = val; `}
+       private fun user_data=(val: App) `{
+               App_incr_ref(val);
+               recv->userData = val;
+       `}
 
        # Fill this in with the function to process input events.  At this point
        # the event has already been pre-dispatched, and it will be finished upon
index 98ee444..ae5d572 100644 (file)
@@ -331,22 +331,15 @@ extern class NativeTextView in "Java" `{ android.widget.TextView `}
 
        fun text=(value: JavaString) in "Java" `{
 
-               android.util.Log.d("Nity", "1");
                final TextView final_recv = recv;
                final String final_value = value;
 
-               android.util.Log.d("Nity", "4");
                ((NativeActivity)recv.getContext()).runOnUiThread(new Runnable() {
                        @Override
                        public void run()  {
-                               android.util.Log.d("Nity", "-5");
-                               android.util.Log.d("Nity", final_value);
-                               android.util.Log.d("Nity", "-5.5");
                                final_recv.setText(final_value);
-                               android.util.Log.d("Nity", "-6");
                        }
                });
-               android.util.Log.d("Nity", "7");
        `}
 
        fun enabled: Bool in "Java" `{ return recv.isEnabled(); `}
diff --git a/lib/app/audio.nit b/lib/app/audio.nit
new file mode 100644 (file)
index 0000000..de68bb9
--- /dev/null
@@ -0,0 +1,43 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Romain Chanoir <romain.chanoir@viacesi.fr>
+#
+# 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.
+
+# App audio abstraction
+#
+# Once the application has started (after `App.setup`)
+# use `App.load_sound` to get a sound
+# then `Sound.play` to play it
+module audio
+
+import app_base
+
+# Abstraction of a sound
+abstract class Sound
+
+       # Plays the sound
+       fun play is abstract
+
+       # Pauses the sound
+       fun pause is abstract
+
+       # Resumes the sound
+       fun resume is abstract
+end
+
+redef class App
+
+       # Loads a sound from the assets of the app, returns `null` if the loading failed
+       fun load_sound(name: String): nullable Sound do return null
+end
index f112d7a..4d2edb4 100644 (file)
@@ -21,6 +21,7 @@ redef class Sys
        # Keeps the average length of an Array when calling to_s
        var arr_len = new Counter[Int]
 
+       # Compute the average array length.
        fun avg_arr_len: Float do
                var total = 0
                var sum = 0
@@ -31,6 +32,7 @@ redef class Sys
                return sum.to_f / total.to_f
        end
 
+       # Compute the average string length.
        fun avg_s_len: Float do
                var total = 0
                var sum = 0
@@ -41,6 +43,7 @@ redef class Sys
                return sum.to_f / total.to_f
        end
 
+       # Display statistics in standard output.
        fun print_stats do
                if arr_len.sum == 0 then
                        print "*** No Array stats ***"
index 703ff5c..ae0d792 100644 (file)
@@ -179,8 +179,6 @@ class Game
        # but cannot be used to add new Events.
        var last_turn: nullable ThinGameTurn[G] = null
 
-       init do end
-
        # Execute and return a new GameTurn.
        #
        # This method calls `do_pre_turn` before executing the GameTurn
index d94ae8e..ae1b875 100644 (file)
--- a/lib/c.nit
+++ b/lib/c.nit
@@ -70,10 +70,10 @@ extern class NativeCArray `{ void * `}
        type E: nullable Object
 
        # Get element at `index`.
-       fun [](index: E): E is abstract
+       fun [](index: Int): E is abstract
 
        # Set `val` at `index`.
-       fun []=(index: E, val: E) is abstract
+       fun []=(index: Int, val: E) is abstract
 
        # Return pointer to the address to the second element of this array
        #
index 91bc87c..5fe0af2 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # The Application Kit provides services to create GUI
-module app_kit is c_linker_option "-framework AppKit"
+module app_kit is ldflags "-framework AppKit"
 
 import foundation
 
index 99c6b12..09e1291 100644 (file)
@@ -20,7 +20,7 @@
 #
 # This wrapper of the Cocoa API regroups the Foundation Kit and the
 # Application Kit.
-module cocoa is c_linker_option "-framework Cocoa"
+module cocoa is ldflags "-framework Cocoa"
 
 import foundation
 import app_kit
index 72e35d7..224c7ab 100644 (file)
@@ -19,10 +19,6 @@ module cocoa_message_box
 
 import cocoa
 
-in "ObjC" `{
-       #import <Cocoa/Cocoa.h>
-`}
-
 fun dialog in "ObjC" `{
        NSAlert *alert = [[[NSAlert alloc] init] autorelease];
        [alert setMessageText:@"Hello world!"];
index 3a2b168..960b001 100644 (file)
@@ -19,10 +19,6 @@ module hello_cocoa
 
 import cocoa::foundation
 
-in "ObjC" `{
-       #import <Foundation/Foundation.h>
-`}
-
 # Print `"Hello world!"` to the log
 fun hello_world in "ObjC" `{
        @autoreleasepool {
index ab027fd..cbe8f08 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # The Foundation Kit provides basic Objective-C classes and structures
-module foundation is c_linker_option "-framework Foundation"
+module foundation is ldflags "-framework Foundation"
 
 in "ObjC Header" `{
        #import <Foundation/Foundation.h>
index 6fe897a..78bb96c 100644 (file)
@@ -16,7 +16,7 @@
 
 # Offers features to interface with C++ code and libraries
 module cpp is
-       new_annotation cpp_compiler_option
+       new_annotation cppflags
 end
 
 # A pointer to a C++ std::string instance
index bca105c..c087642 100644 (file)
@@ -85,8 +85,7 @@ class CurlHTTPRequest
                curl.prim_curl.easy_setopt(new CURLOption.user_agent, name)
        end
 
-       init (url: String, curl: nullable Curl)
-       do
+       init (url: String, curl: nullable Curl) is old_style_init do
                self.url = url
                self.curl = curl
        end
@@ -211,8 +210,7 @@ class CurlMailRequest
        var body: nullable String = "" is writable
        private var supported_outgoing_protocol: Array[String] = ["smtp", "smtps"]
 
-       init (curl: nullable Curl)
-       do
+       init (curl: nullable Curl) is old_style_init do
                self.curl = curl
        end
 
@@ -351,12 +349,6 @@ class CurlResponseFailed
 
        var error_code: Int
        var error_msg: String
-
-       init (err_code: Int, err_msg: String)
-       do
-               self.error_code = err_code
-               self.error_msg = err_msg
-       end
 end
 
 # Success Abstract Response Success Class
@@ -467,7 +459,7 @@ class HeaderMapIterator
        super MapIterator[String, String]
 
        private var iterator: Iterator[Couple[String, String]]
-       init(map: HeaderMap) do self.iterator = map.arr.iterator
+       init(map: HeaderMap) is old_style_init do self.iterator = map.arr.iterator
 
        redef fun is_ok do return self.iterator.is_ok
        redef fun next do self.iterator.next
index 2797b67..8fb57fd 100644 (file)
@@ -5,7 +5,7 @@
 #
 # 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 
+# 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
@@ -83,8 +83,8 @@ class StreamCat
        do
                _streams = streams.iterator
        end
-       init(streams: IStream ...)
-       do
+
+       init(streams: IStream ...) is old_style_init do
                _streams = streams.iterator
        end
 end
@@ -133,8 +133,7 @@ class StreamDemux
                _streams = streams
        end
 
-       init(streams: OStream ...)
-       do
+       init(streams: OStream ...) is old_style_init do
                _streams = streams
        end
 end
index 138b079..2655ae3 100644 (file)
@@ -40,11 +40,11 @@ import github_curl
 #
 # ~~~
 # var repo = api.load_repo("privat/nit")
-# assert repo isa Repo
+# assert repo != null
 # assert repo.name == "nit"
 #
 # var user = api.load_user("Morriar")
-# assert user isa User
+# assert user != null
 # assert user.login == "Morriar"
 # ~~~
 class GithubAPI
@@ -133,7 +133,7 @@ class GithubAPI
 
        # Load the json object from Github.
        # See `GithubEntity::load_from_github`.
-       private fun load_from_github(key: String): JsonObject do
+       protected fun load_from_github(key: String): JsonObject do
                message(1, "Get {key} (github)")
                var res = get(key)
                if was_error then return new JsonObject
@@ -149,9 +149,7 @@ class GithubAPI
        #     assert user.login == "Morriar"
        fun load_user(login: String): nullable User do
                var user = new User(self, login)
-               user.load_from_github
-               if was_error then return null
-               return user
+               return user.load_from_github
        end
 
        # Get the Github repo with `full_name`.
@@ -165,9 +163,7 @@ class GithubAPI
        #     assert repo.default_branch.name == "master"
        fun load_repo(full_name: String): nullable Repo do
                var repo = new Repo(self, full_name)
-               repo.load_from_github
-               if was_error then return null
-               return repo
+               return repo.load_from_github
        end
 
        # Get the Github branch with `name`.
@@ -176,15 +172,13 @@ class GithubAPI
        #
        #     var api = new GithubAPI(get_github_oauth)
        #     var repo = api.load_repo("privat/nit")
-       #     assert repo isa Repo
+       #     assert repo != null
        #     var branch = api.load_branch(repo, "master")
        #     assert branch.name == "master"
        #     assert branch.commit isa Commit
        fun load_branch(repo: Repo, name: String): nullable Branch do
                var branch = new Branch(self, repo, name)
-               branch.load_from_github
-               if was_error then return null
-               return branch
+               return branch.load_from_github
        end
 
        # Get the Github commit with `sha`.
@@ -193,14 +187,41 @@ class GithubAPI
        #
        #     var api = new GithubAPI(get_github_oauth)
        #     var repo = api.load_repo("privat/nit")
-       #     assert repo isa Repo
+       #     assert repo != null
        #     var commit = api.load_commit(repo, "64ce1f")
        #     assert commit isa Commit
        fun load_commit(repo: Repo, sha: String): nullable Commit do
                var commit = new Commit(self, repo, sha)
-               commit.load_from_github
-               if was_error then return null
-               return commit
+               return commit.load_from_github
+       end
+
+       # Get the Github issue #`number`.
+       #
+       # Returns `null` if the issue cannot be found.
+       #
+       #     var api = new GithubAPI(get_github_oauth)
+       #     var repo = api.load_repo("privat/nit")
+       #     assert repo != null
+       #     var issue = api.load_issue(repo, 1)
+       #     assert issue.title == "Doc"
+       fun load_issue(repo: Repo, number: Int): nullable Issue do
+               var issue = new Issue(self, repo, number)
+               return issue.load_from_github
+       end
+
+       # Get the Github pull request #`number`.
+       #
+       # Returns `null` if the pull request cannot be found.
+       #
+       #     var api = new GithubAPI(get_github_oauth)
+       #     var repo = api.load_repo("privat/nit")
+       #     assert repo != null
+       #     var pull = api.load_pull(repo, 1)
+       #     assert pull.title == "Doc"
+       #     assert pull.user.login == "Morriar"
+       fun load_pull(repo: Repo, number: Int): nullable PullRequest do
+               var pull = new PullRequest(self, repo, number)
+               return pull.load_from_github
        end
 
        # Get the Github label with `name`.
@@ -209,14 +230,12 @@ class GithubAPI
        #
        #     var api = new GithubAPI(get_github_oauth)
        #     var repo = api.load_repo("privat/nit")
-       #     assert repo isa Repo
+       #     assert repo != null
        #     var labl = api.load_label(repo, "ok_will_merge")
        #     assert labl != null
        fun load_label(repo: Repo, name: String): nullable Label do
                var labl = new Label(self, repo, name)
-               labl.load_from_github
-               if was_error then return null
-               return labl
+               return labl.load_from_github
        end
 
        # Get the Github milestone with `id`.
@@ -225,14 +244,79 @@ class GithubAPI
        #
        #     var api = new GithubAPI(get_github_oauth)
        #     var repo = api.load_repo("privat/nit")
-       #     assert repo isa Repo
+       #     assert repo != null
        #     var stone = api.load_milestone(repo, 4)
        #     assert stone.title == "v1.0prealpha"
        fun load_milestone(repo: Repo, id: Int): nullable Milestone do
                var milestone = new Milestone(self, repo, id)
-               milestone.load_from_github
+               return milestone.load_from_github
+       end
+
+       # Get the Github issue event with `id`.
+       #
+       # Returns `null` if the event cannot be found.
+       #
+       #     var api = new GithubAPI(get_github_oauth)
+       #     var repo = api.load_repo("privat/nit")
+       #     assert repo isa Repo
+       #     var event = api.load_issue_event(repo, 199674194)
+       #     assert event.actor.login == "privat"
+       #     assert event.event == "labeled"
+       #     assert event.labl.name == "need_review"
+       #     assert event.issue.number == 945
+       fun load_issue_event(repo: Repo, id: Int): nullable IssueEvent do
+               var event = new IssueEvent(self, repo, id)
+               event.load_from_github
                if was_error then return null
-               return milestone
+               return event
+       end
+
+       # Get the Github commit comment with `id`.
+       #
+       # Returns `null` if the comment cannot be found.
+       #
+       #     var api = new GithubAPI(get_github_oauth)
+       #     var repo = api.load_repo("privat/nit")
+       #     assert repo != null
+       #     var comment = api.load_commit_comment(repo, 8982707)
+       #     assert comment.user.login == "Morriar"
+       #     assert comment.body == "For testing purposes..."
+       #     assert comment.commit.sha == "7eacb86d1e24b7e72bc9ac869bf7182c0300ceca"
+       fun load_commit_comment(repo: Repo, id: Int): nullable CommitComment do
+               var comment = new CommitComment(self, repo, id)
+               return comment.load_from_github
+       end
+
+       # Get the Github issue comment with `id`.
+       #
+       # Returns `null` if the comment cannot be found.
+       #
+       #     var api = new GithubAPI(get_github_oauth)
+       #     var repo = api.load_repo("privat/nit")
+       #     assert repo != null
+       #     var comment = api.load_issue_comment(repo, 6020149)
+       #     assert comment.user.login == "privat"
+       #     assert comment.created_at.to_s == "2012-05-30T20:16:54Z"
+       #     assert comment.issue.number == 10
+       fun load_issue_comment(repo: Repo, id: Int): nullable IssueComment do
+               var comment = new IssueComment(self, repo, id)
+               return comment.load_from_github
+       end
+
+       # Get the Github diff comment with `id`.
+       #
+       # Returns `null` if the comment cannot be found.
+       #
+       #     var api = new GithubAPI(get_github_oauth)
+       #     var repo = api.load_repo("privat/nit")
+       #     assert repo != null
+       #     var comment = api.load_review_comment(repo, 21010363)
+       #     assert comment.path == "src/modelize/modelize_property.nit"
+       #     assert comment.original_position == 26
+       #     assert comment.pull.number == 945
+       fun load_review_comment(repo: Repo, id: Int): nullable ReviewComment do
+               var comment = new ReviewComment(self, repo, id)
+               return comment.load_from_github
        end
 end
 
@@ -255,8 +339,10 @@ abstract class GithubEntity
        var json: JsonObject is noinit, protected writable
 
        # Load `json` from Github API.
-       private fun load_from_github do
+       private fun load_from_github: nullable SELF do
                json = api.load_from_github(key)
+               if api.was_error then return null
+               return self
        end
 
        redef fun to_s do return json.to_json
@@ -332,6 +418,31 @@ class Repo
                return res
        end
 
+       # List of issues associated with their ids.
+       fun issues: Map[Int, Issue] do
+               api.message(1, "Get issues for {full_name}")
+               var res = new HashMap[Int, Issue]
+               var issue = last_issue
+               if issue == null then return res
+               res[issue.number] = issue
+               while issue.number > 1 do
+                       issue = api.load_issue(self, issue.number - 1)
+                       assert issue isa Issue
+                       res[issue.number] = issue
+               end
+               return res
+       end
+
+       # Get the last published issue.
+       fun last_issue: nullable Issue do
+               var array = api.get("repos/{full_name}/issues")
+               if not array isa JsonArray then return null
+               if array.is_empty then return null
+               var obj = array.first
+               if not obj isa JsonObject then return null
+               return new Issue.from_json(api, self, obj)
+       end
+
        # List of labels associated with their names.
        fun labels: Map[String, Label] do
                api.message(1, "Get labels for {full_name}")
@@ -361,6 +472,41 @@ class Repo
                return res
        end
 
+       # List of pull-requests associated with their ids.
+       #
+       # Implementation notes: because PR numbers are not consecutive,
+       # PR are loaded from pages.
+       # See: https://developer.github.com/v3/pulls/#list-pull-requests
+       fun pulls: Map[Int, PullRequest] do
+               api.message(1, "Get pulls for {full_name}")
+               var res = new HashMap[Int, PullRequest]
+               var page = 1
+               var array = api.get("{key}/pulls?page={page}").as(JsonArray)
+               while not array.is_empty do
+                       for obj in array do
+                               if not obj isa JsonObject then continue
+                               var number = obj["number"].as(Int)
+                               res[number] = new PullRequest.from_json(api, self, obj)
+                       end
+                       page += 1
+                       array = api.get("{key}/pulls?page={page}").as(JsonArray)
+               end
+               return res
+       end
+
+       # List of contributor related statistics.
+       fun contrib_stats: Array[ContributorStats] do
+               api.message(1, "Get contributor stats for {full_name}")
+               var res = new Array[ContributorStats]
+               var array = api.get("{key}/stats/contributors")
+               if array isa JsonArray then
+                       for obj in array do
+                               res.add new ContributorStats.from_json(api, obj.as(JsonObject))
+                       end
+               end
+               return res
+       end
+
        # Repo default branch.
        fun default_branch: Branch do
                var name = json["default_branch"].to_s
@@ -490,10 +636,246 @@ class Commit
                return new ISODate.from_string(author["date"].to_s)
        end
 
+       # List files staged in this commit.
+       fun files: Array[GithubFile] do
+               var res = new Array[GithubFile]
+               var files = json["files"]
+               if not files isa JsonArray then return res
+               for obj in files do
+                       res.add(new GithubFile(obj.as(JsonObject)))
+               end
+               return res
+       end
+
        # Commit message.
        fun message: String do return json["commit"].as(JsonObject)["message"].to_s
 end
 
+# A Github issue.
+#
+# Should be accessed from `GithubAPI::load_issue`.
+#
+# See <https://developer.github.com/v3/issues/>.
+class Issue
+       super RepoEntity
+
+       redef var key is lazy do return "{repo.key}/issues/{number}"
+
+       # Issue Github ID.
+       var number: Int
+
+       redef init from_json(api, repo, json) do
+               self.number = json["number"].as(Int)
+               super
+       end
+
+       # Issue title.
+       fun title: String do return json["title"].to_s
+
+       # User that created this issue.
+       fun user: User do
+               return new User.from_json(api, json["user"].as(JsonObject))
+       end
+
+       # List of labels on this issue associated to their names.
+       fun labels: Map[String, Label] do
+               var res = new HashMap[String, Label]
+               for obj in json["labels"].as(JsonArray) do
+                       if not obj isa JsonObject then continue
+                       var name = obj["name"].to_s
+                       res[name] = new Label.from_json(api, repo, obj)
+               end
+               return res
+       end
+
+       # State of the issue on Github.
+       fun state: String do return json["state"].to_s
+
+       # Is the issue locked?
+       fun locked: Bool do return json["locked"].as(Bool)
+
+       # Assigned `User` (if any).
+       fun assignee: nullable User do
+               var assignee = json["assignee"]
+               if not assignee isa JsonObject then return null
+               return new User.from_json(api, assignee)
+       end
+
+       # `Milestone` (if any).
+       fun milestone: nullable Milestone do
+               var milestone = json["milestone"]
+               if not milestone isa JsonObject then return null
+               return new Milestone.from_json(api, repo, milestone)
+       end
+
+       # List of comments made on this issue.
+       fun comments: Array[IssueComment] do
+               var res = new Array[IssueComment]
+               var count = comments_count
+               var page = 1
+               var array = api.get("{key}/comments?page={page}")
+               if not array isa JsonArray then
+                       return res
+               end
+               while not array.is_empty and res.length < count do
+                       for obj in array do
+                               if not obj isa JsonObject then continue
+                               var id = obj["id"].as(Int)
+                               res.add(api.load_issue_comment(repo, id).as(not null))
+                       end
+                       page += 1
+                       array = api.get("{key}/comments?page={page}").as(JsonArray)
+               end
+               return res
+       end
+
+       # Number of comments on this issue.
+       fun comments_count: Int do return json["comments"].to_s.to_i
+
+       # Creation time in ISODate format.
+       fun created_at: ISODate do
+               return new ISODate.from_string(json["created_at"].to_s)
+       end
+
+       # Last update time in ISODate format (if any).
+       fun updated_at: nullable ISODate do
+               var res = json["updated_at"]
+               if res == null then return null
+               return new ISODate.from_string(res.to_s)
+       end
+
+       # Close time in ISODate format (if any).
+       fun closed_at: nullable ISODate do
+               var res = json["closed_at"]
+               if res == null then return null
+               return new ISODate.from_string(res.to_s)
+       end
+
+       # TODO link to pull request
+
+       # Full description of the issue.
+       fun body: String  do return json["body"].to_s
+
+       # List of events on this issue.
+       fun events: Array[IssueEvent] do
+               var res = new Array[IssueEvent]
+               var page = 1
+               var array = api.get("{key}/events?page={page}").as(JsonArray)
+               while not array.is_empty do
+                       for obj in array do
+                               if not obj isa JsonObject then continue
+                               res.add new IssueEvent.from_json(api, repo, obj)
+                       end
+                       page += 1
+                       array = api.get("{key}/events?page={page}").as(JsonArray)
+               end
+               return res
+       end
+
+       # User that closed this issue (if any).
+       fun closed_by: nullable User do
+               var closer = json["closed_by"]
+               if not closer isa JsonObject then return null
+               return new User.from_json(api, closer)
+       end
+end
+
+# A Github pull request.
+#
+# Should be accessed from `GithubAPI::load_pull`.
+#
+# PullRequest are basically Issues with more data.
+# See <https://developer.github.com/v3/pulls/>.
+class PullRequest
+       super Issue
+
+       redef var key is lazy do return "{repo.key}/pulls/{number}"
+
+       # Merge time in ISODate format (if any).
+       fun merged_at: nullable ISODate do
+               var res = json["merged_at"]
+               if res == null then return null
+               return new ISODate.from_string(res.to_s)
+       end
+
+       # Merge commit SHA.
+       fun merge_commit_sha: String do return json["merge_commit_sha"].to_s
+
+       # Count of comments made on the pull request diff.
+       fun review_comments: Int do return json["review_comments"].to_s.to_i
+
+       # Pull request head (can be a commit SHA or a branch name).
+       fun head: PullRef do
+               var json = json["head"].as(JsonObject)
+               return new PullRef(api, json)
+       end
+
+       # Pull request base (can be a commit SHA or a branch name).
+       fun base: PullRef do
+               var json = json["base"].as(JsonObject)
+               return new PullRef(api, json)
+       end
+
+       # Is this pull request merged?
+       fun merged: Bool do return json["merged"].as(Bool)
+
+       # Is this pull request mergeable?
+       fun mergeable: Bool do return json["mergeable"].as(Bool)
+
+       # Mergeable state of this pull request.
+       #
+       # See <https://developer.github.com/v3/pulls/#list-pull-requests>.
+       fun mergeable_state: Int do return json["mergeable_state"].to_s.to_i
+
+       # User that merged this pull request (if any).
+       fun merged_by: nullable User do
+               var merger = json["merged_by"]
+               if not merger isa JsonObject then return null
+               return new User.from_json(api, merger)
+       end
+
+       # Count of commits in this pull request.
+       fun commits: Int do return json["commits"].to_s.to_i
+
+       # Added line count.
+       fun additions: Int do return json["additions"].to_s.to_i
+
+       # Deleted line count.
+       fun deletions: Int do return json["deletions"].to_s.to_i
+
+       # Changed files count.
+       fun changed_files: Int do return json["changed_files"].to_s.to_i
+end
+
+# A pull request reference (used for head and base).
+class PullRef
+
+       # Api instance that maintains self.
+       var api: GithubAPI
+
+       # JSON representation.
+       var json: JsonObject
+
+       # Label pointed by `self`.
+       fun labl: String do return json["label"].to_s
+
+       # Reference pointed by `self`.
+       fun ref: String do return json["ref"].to_s
+
+       # Commit SHA pointed by `self`.
+       fun sha: String do return json["sha"].to_s
+
+       # User pointed by `self`.
+       fun user: User do
+               return new User.from_json(api, json["user"].as(JsonObject))
+       end
+
+       # Repo pointed by `self`.
+       fun repo: Repo do
+               return new Repo.from_json(api, json["repo"].as(JsonObject))
+       end
+end
+
 # A Github label.
 #
 # Should be accessed from `GithubAPI::load_label`.
@@ -580,3 +962,264 @@ class Milestone
                return new ISODate.from_string(res.to_s)
        end
 end
+
+# A Github comment
+#
+# There is two kinds of comments:
+#
+# * `CommitComment` are made on a commit page.
+# * `IssueComment` are made on an issue or pull request page.
+# * `ReviewComment` are made on the diff associated to a pull request.
+abstract class Comment
+       super RepoEntity
+
+       # Identifier of this comment.
+       var id: Int
+
+       redef init from_json(api, repo, json) do
+               self.id = json["id"].as(Int)
+               super
+       end
+
+       # User that made this comment.
+       fun user: User do
+               return new User.from_json(api, json["user"].as(JsonObject))
+       end
+
+       # Creation time in ISODate format.
+       fun created_at: ISODate do
+               return new ISODate.from_string(json["created_at"].to_s)
+       end
+
+       # Last update time in ISODate format (if any).
+       fun updated_at: nullable ISODate do
+               if not json.has_key("updated_at") then return null
+               return new ISODate.from_string(json["updated_at"].to_s)
+       end
+
+       # Comment body text.
+       fun body: String do return json["body"].to_s
+end
+
+# A comment made on a commit.
+class CommitComment
+       super Comment
+
+       redef var key is lazy do return "{repo.key}/comments/{id}"
+
+       # Commented commit.
+       fun commit: Commit do
+               return api.load_commit(repo, json["commit_id"].to_s).as(not null)
+       end
+
+       # Position of the comment on the line.
+       fun position: nullable String do
+               if not json.has_key("position") then return null
+               var res = json["position"]
+               if res == null then return null
+               return res.to_s
+       end
+
+       # Line of the comment.
+       fun line: nullable String do
+               if not json.has_key("line") then return null
+               var res = json["line"]
+               if res == null then return null
+               return res.to_s
+       end
+
+       # Path of the commented file.
+       fun path: String do return json["path"].to_s
+end
+
+# Comments made on Github issue and pull request pages.
+#
+# Should be accessed from `GithubAPI::load_issue_comment`.
+#
+# See <https://developer.github.com/v3/issues/comments/>.
+class IssueComment
+       super Comment
+
+       redef var key is lazy do return "{repo.key}/issues/comments/{id}"
+
+       # Issue that contains `self`.
+       fun issue: Issue do
+               var number = issue_url.split("/").last.to_i
+               return api.load_issue(repo, number).as(not null)
+       end
+
+       # Link to the issue document on API.
+       fun issue_url: String do return json["issue_url"].to_s
+end
+
+# Comments made on Github pull request diffs.
+#
+# Should be accessed from `GithubAPI::load_diff_comment`.
+#
+# See <https://developer.github.com/v3/pulls/comments/>.
+class ReviewComment
+       super Comment
+
+       redef var key is lazy do return "{repo.key}/pulls/comments/{id}"
+
+       # Pull request that contains `self`.
+       fun pull: PullRequest do
+               var number = pull_request_url.split("/").last.to_i
+               return api.load_pull(repo, number).as(not null)
+       end
+
+       # Link to the pull request on API.
+       fun pull_request_url: String do return json["pull_request_url"].to_s
+
+       # Diff hunk.
+       fun diff_hunk: String do return json["diff_hunk"].to_s
+
+       # Path of commented file.
+       fun path: String do return json["path"].to_s
+
+       # Position of the comment on the file.
+       fun position: Int do return json["position"].to_s.to_i
+
+       # Original position in the diff.
+       fun original_position: Int do return json["original_position"].to_s.to_i
+
+       # Commit referenced by this comment.
+       fun commit_id: String do return json["commit_id"].to_s
+
+       # Original commit id.
+       fun original_commit_id: String do return json["original_commit_id"].to_s
+end
+
+# An event that occurs on a Github `Issue`.
+#
+# Should be accessed from `GithubAPI::load_issue_event`.
+#
+# See <https://developer.github.com/v3/issues/events/>.
+class IssueEvent
+       super RepoEntity
+
+       redef var key is lazy do return "{repo.key}/issues/events/{id}"
+
+       # Event id on Github.
+       var id: Int
+
+       redef init from_json(api, repo, json) do
+               self.id = json["id"].as(Int)
+               super
+       end
+
+       # Issue that contains `self`.
+       fun issue: Issue do
+               return new Issue.from_json(api, repo, json["issue"].as(JsonObject))
+       end
+
+       # User that initiated the event.
+       fun actor: User do
+               return new User.from_json(api, json["actor"].as(JsonObject))
+       end
+
+       # Creation time in ISODate format.
+       fun created_at: ISODate do
+               return new ISODate.from_string(json["created_at"].to_s)
+       end
+
+       # Event descriptor.
+       fun event: String do return json["event"].to_s
+
+       # Commit linked to this event (if any).
+       fun commit_id: nullable String do
+               var res = json["commit_id"]
+               if res == null then return null
+               return res.to_s
+       end
+
+       # Label linked to this event (if any).
+       fun labl: nullable Label do
+               var res = json["label"]
+               if not res isa JsonObject then return null
+               return new Label.from_json(api, repo, res)
+       end
+
+       # User linked to this event (if any).
+       fun assignee: nullable User do
+               var res = json["assignee"]
+               if not res isa JsonObject then return null
+               return new User.from_json(api, res)
+       end
+
+       # Milestone linked to this event (if any).
+       fun milestone: nullable Milestone do
+               var res = json["milestone"]
+               if not res isa JsonObject then return null
+               return new Milestone.from_json(api, repo, res)
+       end
+
+       # Rename linked to this event (if any).
+       fun rename: nullable RenameAction do
+               var res = json["rename"]
+               if res == null then return null
+               return new RenameAction(res.as(JsonObject))
+       end
+end
+
+# A rename action maintains the name before and after a renaming action.
+class RenameAction
+
+       # JSON content.
+       var json: JsonObject
+
+       # Name before renaming.
+       fun from: String do return json["from"].to_s
+
+       # Name after renaming.
+       fun to: String do return json["to"].to_s
+end
+
+# Contributors list with additions, deletions, and commit counts.
+#
+# Should be accessed from `Repo::contrib_stats`.
+#
+# See <https://developer.github.com/v3/repos/statistics/>.
+class ContributorStats
+       super Comparable
+
+       redef type OTHER: ContributorStats
+
+       # Github API client.
+       var api: GithubAPI
+
+       # Json content.
+       var json: JsonObject
+
+       # Init `self` from a `json` object.
+       init from_json(api: GithubAPI, json: JsonObject) do
+               self.api = api
+               self.json = json
+       end
+
+       # User these statistics are about.
+       fun author: User do
+               return new User.from_json(api, json["author"].as(JsonObject))
+       end
+
+       # Total number of commit.
+       fun total: Int do return json["total"].to_s.to_i
+
+       # Are of weeks of activity with detailed statistics.
+       fun weeks: JsonArray do return json["weeks"].as(JsonArray)
+
+       # ContributorStats can be compared on the total amount of commits.
+       redef fun <(o) do return total < o.total
+end
+
+# A Github file representation.
+#
+# Mostly a wrapper around a json object.
+class GithubFile
+
+       # Json content.
+       var json: JsonObject
+
+       # File name.
+       fun filename: String do return json["filename"].to_s
+end
diff --git a/lib/github/cache.nit b/lib/github/cache.nit
new file mode 100644 (file)
index 0000000..559b4a8
--- /dev/null
@@ -0,0 +1,80 @@
+# 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.
+
+# Enable caching on Github API accesses.
+#
+# If `GithubAPI::enable_cache` is set to true then Github JSON responses
+# will be cached locally using `JsonStore`.
+#
+# Cache can be used to limit the number of access on the API and lighten
+# the rate limit on your github key.
+#
+# Usage:
+#
+# ~~~
+# var api = new GithubAPI(get_github_oauth)
+# api.enable_cache = true
+#
+# var name = "privat/nit"
+# assert not api.has_cache(name)
+# var repo = api.load_repo(name) # load from GitHub
+# assert api.has_cache(name)
+# repo = api.load_repo(name) # load from cache
+#
+# api.clear_cache
+# assert not api.has_cache(name)
+# ~~~
+module cache
+
+intrude import github::api
+import json::store
+
+redef class GithubAPI
+
+       # Enable caching for this client.
+       # Default is `false`.
+       var enable_cache = false is writable
+
+       # JsonStore used to cache data.
+       #
+       # Default directory is `".github_data/"`.
+       var store = new JsonStore(".github_data/") is writable, lazy
+
+       # Delete the cache directory.
+       fun clear_cache do store.clear
+
+       # If no cache data is found for `key` then json is loaded from Github API.
+       redef fun load_from_github(key) do
+               if not enable_cache then return super
+               if store.has_key(key) then
+                       message(1, "Get {key} (cache)")
+                       was_error = false
+                       return store.load_object(key)
+               end
+               var obj = super
+               if not was_error then cache(key, obj)
+               return obj
+       end
+
+       # Save `json` data in cache under `key`.
+       private fun cache(key: String, json: JsonObject) do
+               message(2, "Cache key {key}")
+               store.store_object(key, json)
+       end
+
+       # Check if a cache file exists for `key`.
+       fun has_cache(key: String): Bool do
+               return store.has_key(key)
+       end
+end
diff --git a/lib/github/events.nit b/lib/github/events.nit
new file mode 100644 (file)
index 0000000..b6e5af3
--- /dev/null
@@ -0,0 +1,302 @@
+# 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.
+
+# Events are emitted by Github Hooks.
+#
+# See <https://developer.github.com/v3/activity/events/types/>
+module events
+
+import api
+
+# Github event stub.
+class GithubEvent
+
+       # Github API client.
+       var api: GithubAPI
+
+       # Json representation of `self`.
+       var json: JsonObject is noinit
+
+       init do
+               json = new JsonObject
+       end
+
+       # Init `self` from a `json` object.
+       init from_json(api: GithubAPI, json: JsonObject) do
+               self.api = api
+               self.json = json
+       end
+
+       # Action performed by the event.
+       fun action: String do return json["action"].to_s
+
+       # Repo where this event occured.
+       fun repo: Repo do
+               return new Repo.from_json(api, json["repository"].as(JsonObject))
+       end
+end
+
+# Triggered when a commit comment is created.
+class CommitCommentEvent
+       super GithubEvent
+
+       # The `Comment` itself.
+       fun comment: CommitComment do
+               return new CommitComment.from_json(api, repo, json["comment"].as(JsonObject))
+       end
+end
+
+# Triggered when a repository, branch, or tag is created.
+class CreateEvent
+       super GithubEvent
+
+       # Oject type that was created.
+       #
+       # Can be one of `repository`, `branch`, or `tag`.
+       fun ref_type: String do return json["ref_type"].to_s
+
+       # Git ref (or null if only a repository was created).
+       fun ref: String do return json["ref"].to_s
+
+       # Name of the repo's default branch (usually master).
+       fun master_branch: String do return json["master_branch"].to_s
+
+       # Repo's current description.
+       fun description: String do return json["description"].to_s
+end
+
+# Triggered when a branch or a tag is deleted.
+class DeleteEvent
+       super GithubEvent
+
+       # Object type that was deleted.
+       #
+       # Can be one of `repository`, `branch`, or `tag`.
+       fun ref_type: String do return json["ref_type"].to_s
+
+       # Git ref (or null if only a repository was deleted).
+       fun ref: String do return json["ref"].to_s
+end
+
+# Triggered when a new snapshot is deployed.
+#
+# Deployement are mainly used with integration testing servers.
+class DeploymentEvent
+       super GithubEvent
+
+       # Commit SHA for which this deployment was created.
+       fun sha: String do return json["sha"].to_s
+
+       # Name of repository for this deployment, formatted as :owner/:repo.
+       fun name: String do return json["name"].to_s
+
+       # Optional extra information for this deployment.
+       fun payload: nullable String do
+               if not json.has_key("payload") then return null
+               return json["payload"].to_s
+       end
+
+       # Optional environment to deploy to.
+       # Default: "production"
+       fun environment: nullable String do
+               if not json.has_key("environment") then return null
+               return json["environment"].to_s
+       end
+
+       # Optional human-readable description added to the deployment.
+       fun description: nullable String do
+               if not json.has_key("description") then return null
+               return json["description"].to_s
+       end
+end
+
+# Triggered when a deployement's status changes.
+class DeploymentStatusEvent
+       super GithubEvent
+
+       # New deployment state.
+       #
+       # Can be `pending`, `success`, `failure`, or `error`.
+       fun state: String do return json["state"].to_s
+
+       # Optional link added to the status.
+       fun target_url: nullable String do
+               if not json.has_key("target_url") then return null
+               return json["target_url"].to_s
+       end
+
+       # Deployment hash that this status is associated with.
+       fun deployment: String do return json["deployment"].to_s
+
+       # Optional human-readable description added to the status.
+       fun description: nullable String do
+               if not json.has_key("description") then return null
+               return json["description"].to_s
+       end
+end
+
+# Triggered when a user forks a repository.
+class ForkEvent
+       super GithubEvent
+
+       # Created repository.
+       fun forkee: Repo do return new Repo.from_json(api, json["forkee"].as(JsonObject))
+end
+
+# Triggered when an issue comment is created.
+class IssueCommentEvent
+       super GithubEvent
+
+       # `Issue` the comment belongs to.
+       fun issue: Issue do
+               return new Issue.from_json(api, repo, json["issue"].as(JsonObject))
+       end
+
+       # The `Comment` itself.
+       fun comment: IssueComment do
+               return new IssueComment.from_json(api, repo, json["comment"].as(JsonObject))
+       end
+end
+
+# Triggered when an event occurs on an issue.
+#
+# Triggered when an issue is assigned, unassigned, labeled, unlabeled,
+# opened, closed or reopened.
+class IssuesEvent
+       super GithubEvent
+
+       # The `Issue` itself.
+       fun issue: Issue do return new Issue.from_json(api, repo, json["issue"].as(JsonObject))
+
+       # Optional `Label` that was added or removed from the issue.
+       fun lbl: nullable Label do
+               if not json.has_key("label") then return null
+               return new Label.from_json(api, repo, json["label"].as(JsonObject))
+       end
+
+       # Optional `User` that was assigned or unassigned from the issue.
+       fun assignee: nullable User do
+               if not json.has_key("assignee") then return null
+               return new User.from_json(api, json["assignee"].as(JsonObject))
+       end
+end
+
+# Triggered when a user is added as a collaborator to a repository.
+class MemberEvent
+       super GithubEvent
+
+       # `User` that was added.
+       fun member: User do return new User.from_json(api, json["member"].as(JsonObject))
+end
+
+# Triggered when an event occurs on a pull request.
+#
+# Triggered when a pull request is assigned, unassigned,
+# labeled, unlabeled, opened, closed, reopened, or synchronized.
+class PullRequestEvent
+       super GithubEvent
+
+       # The pull request number.
+       fun number: Int do return json["number"].as(Int)
+
+       # The `PullRequest` itself.
+       fun pull: PullRequest do
+               return new PullRequest.from_json(api, repo, json["pull_request"].as(JsonObject))
+       end
+end
+
+# Triggered when a comment is created on a pull request diff.
+class PullRequestReviewCommentEvent
+       super GithubEvent
+
+       # The `Comment` itself.
+       fun comment: ReviewComment do
+               return new ReviewComment.from_json(api, repo, json["comment"].as(JsonObject))
+       end
+
+       # `PullRequest` the `comment` belongs to.
+       fun pull: PullRequest do
+               return new PullRequest.from_json(api, repo, json["pull_request"].as(JsonObject))
+       end
+end
+
+# Triggered when a repository branch is pushed to.
+class PushEvent
+       super GithubEvent
+
+       # SHA of the HEAD commit on the repository.
+       fun head: String do return json["head"].to_s
+
+       # Full Git ref that was pushed.
+       #
+       # Example: “refs/heads/master”
+       fun ref: String do return json["ref"].to_s
+
+       # Number of commits in the push.
+       fun size: Int do return json["size"].as(Int)
+
+       # Array of pushed commits.
+       fun commits: Array[Commit] do
+               var res = new Array[Commit]
+               var arr = json["commits"].as(JsonArray)
+               for obj in arr do
+                       if not obj isa JsonObject then continue
+                       res.add api.load_commit(repo, obj["sha"].to_s).as(not null)
+               end
+               return res
+       end
+end
+
+# Triggered when the status of a Git commit changes.
+class StatusEvent
+       super GithubEvent
+
+       # The `Commit` itself.
+       fun commit: Commit do
+               return api.load_commit(repo, json["sha"].to_s).as(not null)
+       end
+
+       # New state.
+       #
+       # Can be `pending`, `success`, `failure`, or `error`.
+       fun state: String do return json["state"].to_s
+
+       # Optional human-readable description added to the status.
+       fun description: nullable String do
+               if not json.has_key("description") then return null
+               return json["description"].to_s
+       end
+
+       # Optional link added to the status.
+       fun target_url: nullable String do
+               if not json.has_key("target_url") then return null
+               return json["target_url"].to_s
+       end
+
+       # Array of branches containing the status' SHA.
+       #
+       # Each branch contains the given SHA,
+       # but the SHA may or may not be the head of the branch.
+       #
+       # The array includes a maximum of 10 branches.
+       fun branches: Array[Branch] do
+               var res = new Array[Branch]
+               var arr = json["branches"].as(JsonArray)
+               for obj in arr do
+                       if not obj isa JsonObject then continue
+                       res.add api.load_branch(repo, obj["name"].to_s).as(not null)
+               end
+               return res
+       end
+end
index 778f826..9bc63a8 100644 (file)
@@ -15,4 +15,4 @@
 # Github API related features.
 module github
 
-import api
+import cache
diff --git a/lib/github/hooks.nit b/lib/github/hooks.nit
new file mode 100644 (file)
index 0000000..351042b
--- /dev/null
@@ -0,0 +1,150 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Github hook event listening with `nitcorn`.
+#
+# Usage:
+#
+# ~~~
+# import github::hooks
+#
+# # A simple hook listener that print received events in stdout.
+# class LogHookListener
+#    super HookListener
+#
+#    # Use double dispatch to implement log behavior.
+#    redef fun apply_event(event) do event.log_event(self)
+# end
+#
+# redef class GithubEvent
+#    # Log this event.
+#    #
+#    # Do nothing by default.
+#    fun log_event(l: LogHookListener) do end
+# end
+#
+# redef class CommitCommentEvent
+#
+#    redef fun log_event(l) do
+#        print "new comment on commit {comment.commit.sha}"
+#    end
+# end
+#
+# var api = new GithubAPI(get_github_oauth)
+# var listener = new LogHookListener(api, "127.0.0.1", 8080)
+# # listener.listen # uncomment to start listening
+# ~~~
+module hooks
+
+import events
+import nitcorn
+
+# A nitcorn listener for Github hooks.
+class HookListener
+
+       # Api client used to perform Github API requests.
+       var api: GithubAPI
+
+       # Host to listen.
+       var host: String
+
+       # Port to listen.
+       var port: Int
+
+       # VirtualHost listened
+       private var vh: VirtualHost is noinit
+
+       init do
+               vh = new VirtualHost("{host}:{port}")
+               vh.routes.add new Route("/", new HookAction(self))
+       end
+
+       # Verbosity level (0: quiet, 1: debug).
+       # Default: 0
+       var verbosity = 0
+
+       # Print `message` if `lvl` <= `verbosity`
+       fun message(lvl: Int, message: String) do
+               if lvl <= verbosity then print message
+       end
+
+       # Start listening and responding to event.
+       fun listen do
+               message(1, "Hook listening on {host}:{port}")
+               var factory = new HttpFactory.and_libevent
+               factory.config.virtual_hosts.add vh
+               factory.run
+       end
+
+       # How to build events from received json objects.
+       fun event_factory(kind: String, json: JsonObject): GithubEvent do
+               if kind == "commit_comment" then
+                       return new CommitCommentEvent.from_json(api, json)
+               else if kind == "create" then
+                       return new CreateEvent.from_json(api, json)
+               else if kind == "delete" then
+                       return new DeleteEvent.from_json(api, json)
+               else if kind == "deployment" then
+                       return new DeploymentEvent.from_json(api, json)
+               else if kind == "deployment_status" then
+                       return new DeploymentStatusEvent.from_json(api, json)
+               else if kind == "fork" then
+                       return new ForkEvent.from_json(api, json)
+               else if kind == "issues" then
+                       return new IssuesEvent.from_json(api, json)
+               else if kind == "issue_comment" then
+                       return new IssueCommentEvent.from_json(api, json)
+               else if kind == "member" then
+                       return new MemberEvent.from_json(api, json)
+               else if kind == "pull_request" then
+                       return new PullRequestEvent.from_json(api, json)
+               else if kind == "pull_request_review_comment" then
+                       return new PullRequestReviewCommentEvent.from_json(api, json)
+               else if kind == "push" then
+                       return new PushEvent.from_json(api, json)
+               else if kind == "status" then
+                       return new StatusEvent.from_json(api, json)
+               else
+                       return new GithubEvent.from_json(api, json)
+               end
+       end
+
+       # What to do when we receive an event from a hook?
+       fun apply_event(event: GithubEvent) is abstract
+end
+
+# A nitcorn action dedicated to GitHub hook listening.
+private class HookAction
+       super Action
+
+       # Listener that contains this action.
+       #
+       # The `listener` is used for its `event_factory` method
+       # and the `apply_event`.
+       var listener: HookListener
+
+       # Parse hook request then call `listener.apply_event`.
+       redef fun answer(request, uri) do
+               # get event type
+               var kind = request.header.get_or_null("X-GitHub-Event")
+               if kind == null then return new HttpResponse(403)
+               # get POST object
+               var obj = request.body.parse_json
+               if not obj isa JsonObject then return new HttpResponse(403)
+               # parse event
+               var event = listener.event_factory(kind, obj)
+               listener.apply_event(event)
+               return new HttpResponse(200)
+       end
+end
index cfab7ae..cbf851f 100644 (file)
@@ -22,7 +22,7 @@ module opengles2_hello_triangle
 
 import glesv2
 import egl
-import mnit_linux # for sdl
+import mnit_linux::sdl
 import x11
 
 if "NIT_TESTING".environ == "true" then exit(0)
index 01ca43c..06803ae 100644 (file)
@@ -146,15 +146,21 @@ extern class GLProgram `{GLuint`}
                return active_attrib_name_native(index, max_size).to_s
        end
        private fun active_attrib_name_native(index, max_size: Int): NativeString `{
+               // We get more values than we need, for compatibility. At least the
+               // NVidia driver tries to fill them even if NULL.
+
                char *name = malloc(max_size);
-               glGetActiveAttrib(recv, index, max_size, NULL, NULL, NULL, name);
+               int size;
+               GLenum type;
+               glGetActiveAttrib(recv, index, max_size, NULL, &size, &type, name);
                return name;
        `}
 
        # Size of the active attribute at `index`
        fun active_attrib_size(index: Int): Int `{
                int size;
-               glGetActiveAttrib(recv, index, 0, NULL, NULL, &size, NULL);
+               GLenum type;
+               glGetActiveAttrib(recv, index, 0, NULL, &size, &type, NULL);
                return size;
        `}
 
@@ -162,8 +168,9 @@ extern class GLProgram `{GLuint`}
        #
        # May only be float related data types (single float, vectors and matrix).
        fun active_attrib_type(index: Int): GLFloatDataType `{
+               int size;
                GLenum type;
-               glGetActiveAttrib(recv, index, 0, NULL, &type, NULL, NULL);
+               glGetActiveAttrib(recv, index, 0, NULL, &size, &type, NULL);
                return type;
        `}
 
@@ -175,14 +182,17 @@ extern class GLProgram `{GLuint`}
        end
        private fun active_uniform_name_native(index, max_size: Int): NativeString `{
                char *name = malloc(max_size);
-               glGetActiveUniform(recv, index, max_size, NULL, NULL, NULL, name);
+               int size;
+               GLenum type;
+               glGetActiveUniform(recv, index, max_size, NULL, &size, &type, name);
                return name;
        `}
 
        # Size of the active uniform at `index`
        fun active_uniform_size(index: Int): Int `{
                int size;
-               glGetActiveUniform(recv, index, 0, NULL, NULL, &size, NULL);
+               GLenum type;
+               glGetActiveUniform(recv, index, 0, NULL, &size, &type, NULL);
                return size;
        `}
 
@@ -190,8 +200,9 @@ extern class GLProgram `{GLuint`}
        #
        # May be any data type supported by OpenGL ES 2.0 shaders.
        fun active_uniform_type(index: Int): GLDataType `{
-               GLenum type;
-               glGetActiveUniform(recv, index, 0, NULL, &type, NULL, NULL);
+               int size;
+               GLenum type = 0;
+               glGetActiveUniform(recv, index, 0, NULL, &size, &type, NULL);
                return type;
        `}
 end
index 60d2f12..71868b6 100644 (file)
@@ -37,10 +37,7 @@ class ConfigTree
        # The ini file used to read/store data
        var ini_file: String
 
-       init(file: String) do
-               self.ini_file = file
-               if file.file_exists then load
-       end
+       init do if ini_file.file_exists then load
 
        # Get the config value for `key`
        #
@@ -103,7 +100,6 @@ class ConfigTree
        #    assert config.has_key("foo.bar")
        #    assert not config.has_key("zoo")
        fun has_key(key: String): Bool do
-               var children = roots
                var parts = key.split(".").reversed
                var node = get_root(parts.pop)
                if node == null then return false
@@ -223,7 +219,6 @@ class ConfigTree
        private var roots = new Array[ConfigNode]
 
        private fun set_node(key: String, value: nullable String) do
-               var children = roots
                var parts = key.split(".").reversed
                var k = parts.pop
                var root = get_root(k)
@@ -250,7 +245,6 @@ class ConfigTree
        end
 
        private fun get_node(key: String): nullable ConfigNode do
-               var children = roots
                var parts = key.split(".").reversed
                var node = get_root(parts.pop)
                while not parts.is_empty do
@@ -283,14 +277,11 @@ class ConfigTree
 end
 
 private class ConfigNode
-       var parent: nullable ConfigNode
+
+       var parent: nullable ConfigNode = null
        var children = new HashMap[String, ConfigNode]
        var name: String is writable
-       var value: nullable String
-
-       init(name: String) do
-               self.name = name
-       end
+       var value: nullable String = null
 
        fun key: String do
                if parent == null then
index c3db430..2020e30 100644 (file)
@@ -27,8 +27,8 @@
 # most of JNI functions. You can use it to further customize the behavior
 # of your code.
 module java is
-       c_compiler_option "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
-       c_linker_option("-L $(JNI_LIB_PATH) -ljvm")
+       cflags "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
+       ldflags "-L $(JNI_LIB_PATH) -ljvm"
        new_annotation extra_java_files
 end
 
index bb578fd..12dbc7c 100644 (file)
@@ -34,12 +34,28 @@ interface Jsonable
        # SEE: `append_json`
        fun to_json: String is abstract
 
+       # Use `append_json` to implement `to_json`.
+       #
+       # Therefore, one that redefine `append_json` may use the following
+       # redefinition to link `to_json` and `append_json`:
+       #
+       # ~~~nitish
+       # redef fun to_json do return to_json_by_append
+       # ~~~
+       #
+       # Note: This is not the default implementation of `to_json` in order to
+       # avoid cyclic references between `append_json` and `to_json` when none are
+       # implemented.
+       protected fun to_json_by_append: String do
+               var buffer = new RopeBuffer
+               append_json(buffer)
+               return buffer.write_to_string
+       end
+
        # Append the JSON representation of `self` to the specified buffer.
        #
        # SEE: `to_json`
-       fun append_json(buffer: Buffer) do
-               buffer.append(to_json)
-       end
+       fun append_json(buffer: Buffer) do buffer.append(to_json)
 end
 
 redef class Text
@@ -82,11 +98,7 @@ redef class Text
        #
        #     assert "\t\"http://example.com\"\r\n\0\\".to_json ==
        #               "\"\\t\\\"http:\\/\\/example.com\\\"\\r\\n\\u0000\\\\\""
-       redef fun to_json do
-               var buffer = new FlatBuffer
-               append_json(buffer)
-               return buffer.write_to_string
-       end
+       redef fun to_json do return to_json_by_append
 
        # Parse `self` as JSON.
        #
@@ -211,11 +223,7 @@ interface JsonMapRead[K: String, V: nullable Jsonable]
        #     obj = new JsonObject
        #     obj["baz"] = null
        #     assert obj.to_json == "\{\"baz\":null\}"
-       redef fun to_json do
-               var buffer = new FlatBuffer
-               append_json(buffer)
-               return buffer.write_to_string
-       end
+       redef fun to_json do return to_json_by_append
 
        private fun append_json_entry(iterator: MapIterator[String, nullable Jsonable],
                        buffer: Buffer) do
@@ -259,11 +267,7 @@ class JsonSequenceRead[E: nullable Jsonable]
        #     assert arr.to_json =="[\"foo\"]"
        #     arr.pop
        #     assert arr.to_json =="[]"
-       redef fun to_json do
-               var buffer = new FlatBuffer
-               append_json(buffer)
-               return buffer.write_to_string
-       end
+       redef fun to_json do return to_json_by_append
 
        private fun append_json_entry(iterator: Iterator[nullable Jsonable],
                        buffer: Buffer) do
index 6063f23..abdcab6 100644 (file)
@@ -19,8 +19,8 @@
 #
 # See: http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
 module jvm is
-       c_compiler_option "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
-       c_linker_option "-L $(JNI_LIB_PATH) -ljvm"
+       cflags "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
+       ldflags "-L $(JNI_LIB_PATH) -ljvm"
 end
 
 in "C Header" `{
index 1bcc514..6396ec2 100644 (file)
@@ -29,7 +29,7 @@ module more_collections
 #     assert m.has_key("four")
 #     assert m["four"] == ['i', 'i', 'i', 'i']
 #     assert m["zzz"] == new Array[Char]
-class MultiHashMap[K: Object, V]
+class MultiHashMap[K, V]
        super HashMap[K, Array[V]]
 
        # Add `v` to the array associated with `k`.
@@ -59,7 +59,7 @@ end
 # assert hm2[1, "one"] == 1.0
 # assert hm2[2, "not-two"] == null
 # ~~~~
-class HashMap2[K1: Object, K2: Object, V]
+class HashMap2[K1, K2, V]
        private var level1 = new HashMap[K1, HashMap[K2, V]]
 
        # Return the value associated to the keys `k1` and `k2`.
@@ -97,7 +97,7 @@ end
 # assert hm3[1, "one", 11] == 1.0
 # assert hm3[2, "not-two", 22] == null
 # ~~~~
-class HashMap3[K1: Object, K2: Object, K3: Object, V]
+class HashMap3[K1, K2, K3, V]
        private var level1 = new HashMap[K1, HashMap2[K2, K3, V]]
 
        # Return the value associated to the keys `k1`, `k2`, and `k3`.
@@ -165,7 +165,7 @@ end
 # assert dma["b"] == [65, 66]
 # assert dma.default == [65]
 # ~~~~
-class DefaultMap[K: Object, V]
+class DefaultMap[K, V]
        super HashMap[K, V]
 
        # The default value.
index 01137f7..de91686 100644 (file)
@@ -26,8 +26,8 @@
 # Since this module is a thin wrapper around OpenMPI, in case of missing
 # documentation, you can refer to https://www.open-mpi.org/doc/v1.8/.
 module mpi is
-       c_compiler_option(exec("mpicc", "-showme:compile"))
-       c_linker_option(exec("mpicc", "-showme:link"))
+       cflags exec("mpicc", "-showme:compile")
+       ldflags exec("mpicc", "-showme:link")
 end
 
 import c
diff --git a/lib/neo4j/graph/graph.nit b/lib/neo4j/graph/graph.nit
new file mode 100644 (file)
index 0000000..39063f2
--- /dev/null
@@ -0,0 +1,278 @@
+# 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.
+
+# Provides an interface for services on a Neo4j graphs.
+module neo4j::graph::graph
+
+import neo4j
+import progression
+
+# A Neo4j graph with a local identification scheme for its nodes.
+#
+# An identification scheme can be defined by subclassing `NeoNodeCollection`.
+#
+# `GraphStore` can be subclassed to add ways to save or load a graph. The
+# storing mechanisms may use `nodes.id_of` to identify the nodes in the graph
+# while encoding the relationships.
+class NeoGraph
+       # All the nodes in the graph.
+       var nodes: NeoNodeCollection
+
+       # All the relationships in the graph.
+       var edges: SimpleCollection[NeoEdge] = new Array[NeoEdge]
+
+       # Add a new node to the graph and return it.
+       #
+       # Set the local ID of the node before returning it.
+       #
+       # SEE: `NeoNodeCollection.add`
+       # SEE: `NeoNodeCollection.create_node`
+       # SEE: `NeoNodeCollection.register`
+       fun create_node: NeoNode do return nodes.create_node
+end
+
+# All the nodes in a `NeoGraph`.
+#
+# An identification scheme can be defined throught the `register` and `add`
+# methods. The `id_property` attribute defines where the local ID (that is the
+# ID managed by the collection) is stored in each node.
+abstract class NeoNodeCollection
+       super SimpleCollection[NeoNode]
+
+       # The type of the local IDs.
+       type ID_TYPE: Jsonable
+
+       # The property of the nodes that hold the local ID.
+       var id_property: String
+
+       # Retrieve the node that has the specified local id.
+       #
+       # Note: The default implementation uses `get_or_null`.
+       fun [](id: ID_TYPE): NeoNode do
+               var n = get_or_null(id)
+               assert n isa NeoNode
+               return n
+       end
+
+       # Retrieve the node that has the specified local id, or return `null`.
+       #
+       # Note: The default implementation uses `iterator`.
+       fun get_or_null(id: ID_TYPE): nullable NeoNode do
+               for n in self do
+                       if id_of(n) == id then return n
+               end
+               return null
+       end
+
+       # There is a node that has the specified local id?
+       #
+       # Note: The default implementation uses `get_or_null`.
+       fun has_id(id: ID_TYPE): Bool do return get_or_null(id) isa NeoNode
+
+       # Return the local ID of the node.
+       fun id_of(node: NeoNode): ID_TYPE do return node[id_property].as(ID_TYPE)
+
+       # Set the local ID of the specified node.
+       #
+       # Just update the property at `property_id`. Do not check anything.
+       protected fun id_of=(node: NeoNode, id: ID_TYPE) do
+               node[id_property] = id
+       end
+
+       # Enlarge the collection to have at least the specified capacity.
+       #
+       # The capacity is specified in number of nodes. Used to minimize the
+       # number of times the collection need to be resized when adding nodes
+       # in batches.
+       #
+       # Do nothing by default.
+       fun enlarge(cap: Int) do end
+
+       # Add the specified node to the graph and set its local ID.
+       #
+       # SEE: `add`
+       # SEE: `create_node`
+       fun register(node: NeoNode) is abstract
+
+       # Add the specified node to the graph assuming that its local ID is already set.
+       #
+       # SEE: `create_node`
+       # SEE: `register`
+       redef fun add(node: NeoNode) is abstract
+
+       # Add a new node to the graph and return it.
+       #
+       # Set the local ID of the node before returning it.
+       #
+       # SEE: `add`
+       # SEE: `register`
+       fun create_node: NeoNode do
+               var node = new NeoNode
+               register(node)
+               return node
+       end
+
+       # Remove the node with the specified local ID.
+       fun remove_at(id: ID_TYPE) is abstract
+
+       # Remove the specified node.
+       #
+       # The local ID is used instead of `==` to seek the node.
+       fun remove_node(node: NeoNode) do
+               remove_at(id_of(node))
+       end
+
+       redef fun clear do
+               for node in self do remove_node(node)
+       end
+
+       redef fun remove(node: NeoNode) do
+               for n in self do
+                       if node == n then
+                               remove_node(n)
+                               return
+                       end
+               end
+       end
+
+       redef fun remove_all(node: NeoNode) do
+               for n in self do
+                       if node == n then remove_node(n)
+               end
+       end
+
+       # Optimize the collection, possibly by rewritting it.
+       #
+       # The local ID of the elements may be changed by this method.
+       fun compact do end
+end
+
+# A mean to save and load a Neo4j graph.
+abstract class GraphStore
+       super Trackable
+
+       # The graph to save or load.
+       var graph: NeoGraph
+
+       # Can we save the graph without conflict?
+       fun isolated_save: Bool is abstract
+
+       # Load the graph (or a part of it).
+       #
+       # Do not reset the graph.
+       fun load is abstract
+
+       # Save the graph.
+       fun save do save_part(graph.nodes, graph.edges)
+
+       # Save the specified part of the graph.
+       #
+       # Assume that for each relationship specified, both ends are already saved
+       # or are specified in the same call to this method.
+       fun save_part(nodes: Collection[NeoNode],
+                       edges: Collection[NeoEdge]) is abstract
+end
+
+# Save or load a graph using an actual Neo4j database.
+class Neo4jGraphStore
+       super GraphStore
+
+       # The maximum number of entities saved in one request.
+       #
+       # Also defines the granulity of the reported progression.
+       #
+       # TODO Also honor this limit in `load`.
+       var batch_max_size = 512 is writable
+
+       # The Neo4j client to use.
+       var client: Neo4jClient
+
+       # The label to use to retrieve the nodes.
+       var node_label: String
+
+       private var done_part = 0
+       private var total = 0
+
+       # Is the database already contains at least one node with the specified label?
+       fun has_node_label(name: String): Bool do
+               var query = new CypherQuery.from_string(
+                               "match n where \{name\} in labels(n) return count(n)")
+               query.params["name"] = name
+               var data = client.cypher(query).as(JsonObject)["data"]
+               var result = data.as(JsonArray).first.as(JsonArray).first.as(Int)
+               return result > 0
+       end
+
+       redef fun isolated_save do return not has_node_label(node_label)
+
+       redef fun load do
+               assert batch_max_size > 0
+               fire_started
+               var db_nodes = client.nodes_with_label(node_label)
+               var nodes = graph.nodes
+               var edges = graph.edges
+               var i = 0
+
+               total = nodes.length * 2
+               done_part = nodes.length
+               fire_progressed(done_part, total)
+               for node in db_nodes do
+                       nodes.add(node)
+                       edges.add_all(node.out_edges)
+                       i += 1
+                       if i >= batch_max_size then
+                               done_part += batch_max_size
+                               fire_progressed(done_part, total)
+                       end
+               end
+               fire_done
+       end
+
+       redef fun save_part(nodes, edges) do
+               assert batch_max_size > 0
+               fire_started
+               total = nodes.length + edges.length
+               done_part = 0
+
+               save_entities(nodes)
+               save_entities(edges)
+               fire_done
+       end
+
+       # Save the specified entities.
+       private fun save_entities(neo_entities: Collection[NeoEntity]) do
+               var batch = new NeoBatch(client)
+               var batch_length = 0
+
+               for nentity in neo_entities do
+                       batch.save_entity(nentity)
+                       batch_length += 1
+                       if batch_length >= batch_max_size then
+                               do_batch(batch)
+                               done_part += batch_max_size
+                               fire_progressed(done_part, total)
+                               batch = new NeoBatch(client)
+                               batch_length = 0
+                       end
+               end
+               do_batch(batch)
+               done_part += batch_length
+       end
+
+       # Execute `batch` and check for errors.
+       #
+       # Abort if `batch.execute` returns errors.
+       private fun do_batch(batch: NeoBatch) do
+               var errors = batch.execute
+               assert errors.is_empty else
+                       for e in errors do sys.stderr.write("{e}\n")
+               end
+       end
+end
diff --git a/lib/neo4j/graph/json_graph_store.nit b/lib/neo4j/graph/json_graph_store.nit
new file mode 100644 (file)
index 0000000..9c97216
--- /dev/null
@@ -0,0 +1,321 @@
+# 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.
+
+# Provides JSON as a mean to store graphs.
+module neo4j::graph::json_graph_store
+
+import graph
+
+# Save or load a graph using a JSON document.
+#
+# The graph (or the specified part of it) is stored as a JSON object with the
+# following properties:
+#
+# * `"nodes"`: An array with all nodes. Each node is an object with the
+# following properties:
+#      * `"labels"`: An array of all applied labels.
+#      * `"properties"`: An object mapping each defined property to its value.
+# * `"edges"`: An array with all relationships. Each relationship is an object
+# with the following properties:
+#      * `"type"`: The type (`String`) of the relationship.
+#      * `"properties"`: An object mapping each defined property to its value.
+#      * `"from"`: The local ID of the source node.
+#      * `"to"`: The local ID of the destination node.
+#
+# ~~~nit
+# import neo4j::graph::sequential_id
+#
+# var graph = new NeoGraph(new SequentialNodeCollection("nid"))
+# var a = new NeoNode
+# a.labels.add "Foo"
+# a["answer"] = 42
+# a["Ultimate question of"] = new JsonArray.from(["life",
+#              "the Universe", "and Everything."])
+# graph.nodes.register a
+# var b = graph.create_node
+# b.labels.add "Foo"
+# b.labels.add "Bar"
+# graph.edges.add new NeoEdge(a, "BAZ", b)
+#
+# var ostream = new StringOStream
+# var store = new JsonGraphStore(graph)
+# store.ostream = ostream
+# store.save
+# assert ostream.to_s == """{"nodes":[""" + """
+# {"labels":["Foo"],"properties":{"answer":42,""" + """
+# "Ultimate question of":["life","the Universe","and Everything."],""" + """
+# "nid":1}},""" + """
+# {"labels":["Foo","Bar"],"properties":{"nid":2}}],""" + """
+# "edges":[{"type":"BAZ","properties":{},"from":1,"to":2}]}"""
+#
+# graph.nodes.clear
+# graph.edges.clear
+# store.istream = new StringIStream(ostream.to_s)
+# store.load
+# assert 1 == graph.edges.length
+# for edge in graph.edges do
+#      assert "BAZ" == edge.rel_type
+#      assert a.labels == edge.from.labels
+#      for k, v in a.properties do assert v == edge.from.properties[k]
+#      assert b.labels == edge.to.labels
+#      for k, v in b.properties do assert v == edge.to.properties[k]
+# end
+# assert 2 == graph.nodes.length
+# ~~~
+class JsonGraphStore
+       super GraphStore
+
+       # The stream to use for `load`.
+       var istream: nullable IStream = null is writable
+
+       # The stream to use for `save` and `save_part`.
+       var ostream: nullable OStream = null is writable
+
+       # Use the specified `IOStream`.
+       init from_io(graph: NeoGraph, iostream: IOStream) do
+               init(graph)
+               istream = iostream
+               ostream = iostream
+       end
+
+       # Use the specified string to load the graph.
+       init from_string(graph: NeoGraph, string: String) do
+               init(graph)
+               istream = new StringIStream(string)
+       end
+
+       redef fun isolated_save do return true
+
+       redef fun load do
+               var istream = self.istream
+               assert istream isa IStream
+               fire_started
+               graph.load_json(istream.read_all)
+               fire_done
+       end
+
+       redef fun save_part(nodes, edges) do
+               var ostream = self.ostream
+               assert ostream isa OStream
+               fire_started
+               ostream.write(graph.to_json)
+               fire_done
+       end
+end
+
+redef class NeoGraph
+       super Jsonable
+
+       # Retrieve the graph from the specified JSON document.
+       #
+       # For the expected format, see `JsonGraphStore`.
+       #
+       # ~~~nit
+       # import neo4j::graph::sequential_id
+       #
+       # var graph = new NeoGraph(new SequentialNodeCollection("node_id"))
+       # var a = new NeoNode
+       # a.labels.add "Foo"
+       # a["answer"] = 42
+       # a["Ultimate question of"] = new JsonArray.from(["life",
+       #               "the Universe", "and Everything."])
+       # graph.nodes.register a
+       # var b = graph.create_node
+       # b.labels.add "Foo"
+       # b.labels.add "Bar"
+       # graph.edges.add new NeoEdge(a, "BAZ", b)
+       #
+       # graph = new NeoGraph.from_json(
+       #               new SequentialNodeCollection("node_id"), graph.to_json)
+       # assert 1 == graph.edges.length
+       # for edge in graph.edges do
+       #       assert "BAZ" == edge.rel_type
+       #       assert a.labels == edge.from.labels
+       #       for k, v in a.properties do assert v == edge.from.properties[k]
+       #       assert b.labels == edge.to.labels
+       #       for k, v in b.properties do assert v == edge.to.properties[k]
+       # end
+       # assert 2 == graph.nodes.length
+       # ~~~
+       init from_json(nodes: NeoNodeCollection, t: Text) do
+               from_json_object(nodes, t.parse_json.as(JsonObject))
+       end
+
+       # Retrieve the graph from the specified JSON object.
+       #
+       # For the expected format, see `JsonGraphStore`.
+       init from_json_object(nodes: NeoNodeCollection, o: JsonObject) do
+               init(nodes)
+               load_json_object(o)
+       end
+
+       # Retrieve a part of the graph from the specified JSON document.
+       #
+       # For the expected format, see `JsonGraphStore`.
+       fun load_json(t: Text) do
+               load_json_object(t.parse_json.as(JsonObject))
+       end
+
+       # Retrieve a part of the graph from the specified JSON object.
+       #
+       # For the expected format, see `JsonGraphStore`.
+       fun load_json_object(o: JsonObject) do
+               var json_nodes = o["nodes"].as(JsonArray)
+               var nodes = self.nodes
+               nodes.enlarge(nodes.length)
+               for json_node in json_nodes do
+                       assert json_node isa JsonObject
+                       var node = new NeoNode.from_json_object(json_node)
+                       nodes.add node
+               end
+
+               var json_edges = o["edges"].as(JsonArray)
+               var edges = self.edges
+               if edges isa AbstractArray[NeoEdge] then edges.enlarge(edges.length)
+               for json_edge in json_edges do
+                       assert json_edge isa JsonObject
+                       var from = nodes[nodes.id_from_jsonable(json_edge["from"])]
+                       var to = nodes[nodes.id_from_jsonable(json_edge["to"])]
+                       var rel_type = json_edge["type"].as(String)
+                       var json_properties = json_edge["properties"].as(JsonObject)
+                       var edge = new NeoEdge(from, rel_type, to)
+                       edge.properties.recover_with(json_properties)
+                       edges.add edge
+               end
+       end
+
+       redef fun to_json do return to_json_by_append
+
+       # Append the JSON representation of `self` to the specified buffer.
+       #
+       # For a description of the format, see `JsonGraphStore`.
+       #
+       # SEE: `to_json`
+       redef fun append_json(b) do
+               b.append "\{\"nodes\":["
+               append_entities_json(nodes, b)
+               b.append "],\"edges\":["
+               append_entities_json(edges, b)
+               b.append "]\}"
+       end
+
+       # Encode `self` in JSON.
+       #
+       # For a description of the format, see `JsonGraphStore`.
+       #
+       # SEE: `append_json`
+       private fun append_entities_json(entities: Collection[NeoEntity],
+                       b: Buffer) do
+               var i = entities.iterator
+               if i.is_ok then
+                       i.item.append_json_for(self, b)
+                       i.next
+                       for entity in i do
+                               b.add ','
+                               entity.append_json_for(self, b)
+                       end
+               end
+       end
+end
+
+redef class NeoNodeCollection
+       # Convert the specified JSON value into a local ID.
+       fun id_from_jsonable(id: nullable Jsonable): ID_TYPE do return id.as(ID_TYPE)
+end
+
+redef class NeoEntity
+
+       # Append the JSON representation of the entity to the specified buffer.
+       fun append_json_for(graph: NeoGraph, buffer: Buffer) is abstract
+end
+
+# Make `NeoNode` `Jsonable`.
+redef class NeoNode
+       super Jsonable
+
+       # Retrieve the node from the specified JSON value.
+       #
+       # Note: Here, the `"id"` is optional and ignored.
+       #
+       # SEE: `JsonGraph`
+       #
+       #     var node = new NeoNode.from_json("""
+       #     {
+       #       "labels": ["foo", "Bar"],
+       #       "properties": {
+       #               "baz": 42
+       #       }
+       #     }
+       #     """)
+       #     assert ["foo", "Bar"] == node.labels
+       #     assert 42 == node["baz"]
+       init from_json(t: Text) do
+               from_json_object(t.parse_json.as(JsonObject))
+       end
+
+       # Retrieve the node from the specified JSON value.
+       #
+       # Note: Here, the `"id"` is optional and ignored.
+       #
+       # SEE: `JsonGraph`
+       init from_json_object(o: JsonObject) do
+               init
+               var labels = o["labels"].as(JsonArray)
+               for lab in labels do self.labels.add(lab.as(String))
+               var json_properties = o["properties"].as(JsonObject)
+               properties.recover_with(json_properties)
+       end
+
+       redef fun to_json do return to_json_by_append
+
+       # Append the JSON representation of the node to the specified buffer.
+       #
+       # SEE: `JsonGraph`
+       redef fun append_json(b) do
+               b.append "\{\"labels\":["
+               var i = labels.iterator
+               if i.is_ok then
+                       i.item.append_json(b)
+                       i.next
+                       for lab in i do
+                               b.add ','
+                               lab.append_json(b)
+                       end
+               end
+               b.append "],\"properties\":"
+               properties.append_json(b)
+               b.add '}'
+       end
+
+       redef fun to_s do return to_json
+
+       # Append the JSON representation of the node to the specified buffer.
+       redef fun append_json_for(graph: NeoGraph, buffer: Buffer) do
+               append_json(buffer)
+       end
+end
+
+redef class NeoEdge
+
+       # Append the JSON representation of the relationship to the specified buffer.
+       #
+       # Use the IDs specfied by `graph.nodes`.
+       redef fun append_json_for(graph: NeoGraph, buffer: Buffer) do
+               buffer.append "\{\"type\":"
+               rel_type.append_json(buffer)
+               buffer.append ",\"properties\":"
+               properties.append_json(buffer)
+               buffer.append ",\"from\":"
+               graph.nodes.id_of(from).append_json(buffer)
+               buffer.append ",\"to\":"
+               graph.nodes.id_of(to).append_json(buffer)
+               buffer.append "}"
+       end
+end
diff --git a/lib/neo4j/graph/sequential_id.nit b/lib/neo4j/graph/sequential_id.nit
new file mode 100644 (file)
index 0000000..fb6129f
--- /dev/null
@@ -0,0 +1,116 @@
+# 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.
+
+# Provides a sequential identification scheme for Neo4j nodes.
+module neo4j::graph::sequential_id
+
+import graph
+private import pipeline
+
+
+# A Neo4j node collection using a sequential identification scheme.
+#
+# The local IDs are sequential numbers (integers) starting at `1`.
+#
+# Note: When loading nodes, the local IDs should forms a mostly contiguous
+# range starting at `1`. Else, this collection will consume a lot of memory.
+# Futhermore, the local IDs **must** be positive.
+#
+# ~~~nit
+# var nodes = new SequentialNodeCollection("id")
+# var a = nodes.create_node
+# var b = new NeoNode
+# var c = new NeoNode
+#
+# nodes.register b
+# c["id"] = 4
+# nodes.add c
+# assert a["id"] == 1
+# assert b["id"] == 2
+# assert c["id"] == 4
+# assert nodes.to_a == [a, b, c]
+# assert nodes.length == 3
+#
+# nodes.compact
+# assert a["id"] == 1
+# assert b["id"] == 2
+# assert c["id"] == 3
+# assert nodes.to_a == [a, b, c]
+# assert nodes.length == 3
+# ~~~
+class SequentialNodeCollection
+       super NeoNodeCollection
+
+       redef type ID_TYPE: Int
+
+       private var nodes = new Array[nullable NeoNode]
+
+       redef var length = 0
+
+       redef fun iterator do return new NullSkipper[NeoNode](self.nodes.iterator)
+
+       redef fun [](id) do return nodes[id].as(NeoNode)
+
+       redef fun get_or_null(id) do
+               if id < 0 or id > nodes.length then return null
+               return nodes[id]
+       end
+
+       redef fun has_id(id: Int): Bool do
+               return id >= 0 and id < nodes.length and nodes[id] isa NeoNode
+       end
+
+       redef fun enlarge(cap) do nodes.enlarge(cap)
+
+       redef fun register(node) do
+               nodes.add node
+               id_of(node) = nodes.length
+               length += 1
+       end
+
+       redef fun add(node) do
+               var id = node[id_property]
+               assert id isa Int else
+                       sys.stderr.write "The local ID must be an `Int`.\n"
+               end
+               assert id >= 0 else
+                       sys.stderr.write "The local ID must be greater or equal to 0. Got {id}.\n"
+               end
+               # Pad with nulls.
+               nodes.enlarge(id)
+               var delta = id - nodes.length
+               while delta > 0 do
+                       nodes.add null
+                       delta -= 1
+               end
+               nodes[id] = node
+               length += 1
+       end
+
+       redef fun remove_at(id) do
+               nodes[id] = null
+               length -= 1
+       end
+
+       redef fun clear do
+               nodes.clear
+               length = 0
+       end
+
+       redef fun compact do
+               var i = iterator
+
+               nodes = new Array[nullable NeoNode]
+               for n in i do
+                       nodes.add n
+                       id_of(n) = nodes.length
+               end
+       end
+end
diff --git a/lib/neo4j/json_store.nit b/lib/neo4j/json_store.nit
deleted file mode 100644 (file)
index cf9f441..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-# 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.
-
-# Uses JSON as a storage medium for a Neo4j subgraph.
-module neo4j::json_store
-
-import neo4j
-private import template
-
-# A Neo4j graph that uses as a storage medium.
-#
-# The graph is stored as a JSON object with the following properties:
-#
-# * `"nodes"`: An array with all nodes. Each node is an object with the
-# following properties:
-#      * `"id"`: The ID (`Int`) that uniquely identifies the node in the current
-#      graph.
-#      * `"labels"`: An array of all applied labels.
-#      * `"properties"`: An object mapping each defined property to its value.
-# * `"links"`: An array with all relationships. Each relationship is an object
-# with the following properties:
-#      * `"type"`: The type (`String`) of the relationship.
-#      * `"properties"`: An object mapping each defined property to its value.
-#      * `"from"`: The ID (`Int`) of the source node.
-#      * `"to"`: The ID (`Int`) of the destination node.
-#
-# TODO Refine the graph API instead when it will be available.
-class JsonGraph
-       super Jsonable
-
-       # All nodes in the graph.
-       var nodes: SimpleCollection[NeoNode] = new Array[NeoNode]
-
-       # All relationships in the graph.
-       var links: SimpleCollection[NeoEdge] = new Array[NeoEdge]
-
-       # Create an empty graph.
-       init do end
-
-       # Retrieve the graph from the specified JSON value.
-       #
-       #     var graph = new JsonGraph
-       #     var a = new NeoNode
-       #     a.labels.add "Foo"
-       #     a["answer"] = 42
-       #     a["Ultimate question of"] = new JsonArray.from(["life",
-       #               "the Universe", "and Everything."])
-       #     graph.nodes.add a
-       #     var b = new NeoNode
-       #     b.labels.add "Foo"
-       #     b.labels.add "Bar"
-       #     graph.nodes.add b
-       #     graph.links.add new NeoEdge(a, "BAZ", b)
-       #     #
-       #     graph = new JsonGraph.from_json(graph.to_json)
-       #     assert 1 == graph.links.length
-       #     for link in graph.links do
-       #       assert "BAZ" == link.rel_type
-       #       assert a.labels == link.from.labels
-       #       for k, v in a.properties do assert v == link.from.properties[k]
-       #       assert b.labels == link.to.labels
-       #       for k, v in b.properties do assert v == link.to.properties[k]
-       #     end
-       #     assert 2 == graph.nodes.length
-       init from_json(t: Text) do
-               from_json_object(t.parse_json.as(JsonObject))
-       end
-
-       # Retrieve the graph from the specified JSON object.
-       init from_json_object(o: JsonObject) do
-               var node_by_id = new HashMap[Int, NeoNode]
-               var nodes = o["nodes"].as(JsonArray)
-               for json_node in nodes do
-                       assert json_node isa JsonObject
-                       var node = new NeoNode.from_json_object(json_node)
-                       node_by_id[json_node["id"].as(Int)] = node
-                       self.nodes.add node
-               end
-               var links = o["links"].as(JsonArray)
-               for json_link in links do
-                       assert json_link isa JsonObject
-                       var from = node_by_id[json_link["from"].as(Int)]
-                       var to = node_by_id[json_link["to"].as(Int)]
-                       var rel_type = json_link["type"].as(String)
-                       var json_properties = json_link["properties"].as(JsonObject)
-                       var link = new NeoEdge(from, rel_type, to)
-                       link.properties.recover_with(json_properties)
-                       self.links.add link
-               end
-       end
-
-       redef fun to_json do
-               var t = new Template
-               t.add "\{\"nodes\":["
-               var i = 0
-               for n in nodes do
-                       if i > 0 then t.add ","
-                       t.add n.to_json
-                       i += 1
-               end
-               t.add "],\"links\":["
-               i = 0
-               for link in links do
-                       if i > 0 then t.add ","
-                       t.add link.to_json
-                       i += 1
-               end
-               t.add "]\}"
-               return t.write_to_string
-       end
-end
-
-# Make `NeoNode` `Jsonable`.
-redef class NeoNode
-       super Jsonable
-
-       # Retrieve the node from the specified JSON value.
-       #
-       # Note: Here, the `"id"` is optional and ignored.
-       #
-       # SEE: `JsonGraph`
-       #
-       #     var node = new NeoNode.from_json("""
-       #     {
-       #       "labels": ["foo", "Bar"],
-       #       "properties": {
-       #               "baz": 42
-       #       }
-       #     }
-       #     """)
-       #     assert ["foo", "Bar"] == node.labels
-       #     assert 42 == node["baz"]
-       init from_json(t: Text) do
-               from_json_object(t.parse_json.as(JsonObject))
-       end
-
-       # Retrieve the node from the specified JSON value.
-       #
-       # Note: Here, the `"id"` is optional and ignored.
-       #
-       # SEE: `JsonGraph`
-       init from_json_object(o: JsonObject) do
-               init
-               var labels = o["labels"].as(JsonArray)
-               for lab in labels do self.labels.add(lab.as(String))
-               var json_properties = o["properties"].as(JsonObject)
-               properties.recover_with(json_properties)
-       end
-
-       # Get the JSON representation of `self`.
-       #
-       # SEE: `JsonGraph`
-       redef fun to_json do
-               var t = new Template
-               t.add "\{\"id\":"
-               t.add object_id.to_json
-               t.add ",\"labels\":["
-               var i = 0
-               for lab in labels do
-                       if i > 0 then t.add ","
-                       t.add lab.to_json
-                       i += 1
-               end
-               t.add "],\"properties\":"
-               t.add properties.to_json
-               t.add "}"
-               return t.write_to_string
-       end
-
-       redef fun to_s do return to_json
-end
-
-# Make `NeoEdge` `Jsonable`.
-redef class NeoEdge
-       super Jsonable
-
-       redef fun to_json do
-               var t = new Template
-               t.add "\{\"type\":"
-               t.add rel_type.to_json
-               t.add ",\"properties\":"
-               t.add properties.to_json
-               t.add ",\"from\":"
-               t.add from.object_id.to_json
-               t.add ",\"to\":"
-               t.add to.object_id.to_json
-               t.add "}"
-               return t.write_to_string
-       end
-
-       redef fun to_s do return to_json
-end
index 9c5ec01..31c0a45 100644 (file)
@@ -374,10 +374,10 @@ end
 
 private class DephIterator
        super Iterator[Node]
+
        var stack = new List[Iterator[nullable Node]]
 
-       init(i: Iterator[nullable Node])
-       do
+       init(i: Iterator[nullable Node]) is old_style_init do
                stack.add i
        end
 
index 8c8f9ea..fa5ce15 100644 (file)
@@ -44,11 +44,13 @@ abstract class Option
        var default_value: VALUE is writable
 
        # Create a new option
-       init(help: String, default: VALUE, names: nullable Array[String])
-       do
+       init(help: String, default: VALUE, names: nullable Array[String]) is old_style_init do
                init_opt(help, default, names)
        end
 
+       # Init option `helptext`, `default_value` and `names`.
+       #
+       # Also set current `value` to `default`.
        fun init_opt(help: String, default: VALUE, names: nullable Array[String])
        do
                if names == null then
@@ -80,6 +82,7 @@ abstract class Option
                return text.to_s
        end
 
+       # Pretty print the default value.
        fun pretty_default: String
        do
                var dv = default_value
@@ -97,7 +100,9 @@ end
 # Not really an option. Just add a line of text when displaying the usage
 class OptionText
        super Option
-       init(text: String) do super(text, null, null)
+
+       # Init a new OptionText with `text`.
+       init(text: String) is old_style_init do super(text, null, null)
 
        redef fun pretty(off) do return to_s
 
@@ -109,7 +114,8 @@ class OptionBool
        super Option
        redef type VALUE: Bool
 
-       init(help: String, names: String...) do super(help, false, names)
+       # Init a new OptionBool with a `help` message and `names`.
+       init(help: String, names: String...) is old_style_init do super(help, false, names)
 
        redef fun read_param(it)
        do
@@ -123,7 +129,8 @@ class OptionCount
        super Option
        redef type VALUE: Int
 
-       init(help: String, names: String...) do super(help, 0, names)
+       # Init a new OptionCount with a `help` message and `names`.
+       init(help: String, names: String...) is old_style_init do super(help, 0, names)
 
        redef fun read_param(it)
        do
@@ -135,6 +142,8 @@ end
 # Option with one parameter (mandatory by default)
 abstract class OptionParameter
        super Option
+
+       # Convert `str` to a value of type `VALUE`.
        protected fun convert(str: String): VALUE is abstract
 
        # Is the parameter mandatory?
@@ -159,7 +168,8 @@ class OptionString
        super OptionParameter
        redef type VALUE: nullable String
 
-       init(help: String, names: String...) do super(help, null, names)
+       # Init a new OptionString with a `help` message and `names`.
+       init(help: String, names: String...) is old_style_init do super(help, null, names)
 
        redef fun convert(str) do return str
 end
@@ -170,10 +180,14 @@ end
 class OptionEnum
        super OptionParameter
        redef type VALUE: Int
+
+       # Values in the enumeration.
        var values: Array[String]
 
-       init(values: Array[String], help: String, default: Int, names: String...)
-       do
+       # Init a new OptionEnum from `values` with a `help` message and `names`.
+       #
+       # `default` is the index of the default value in `values`.
+       init(values: Array[String], help: String, default: Int, names: String...) is old_style_init do
                assert values.length > 0
                self.values = values.to_a
                super("{help} <{values.join(", ")}>", default, names)
@@ -190,6 +204,7 @@ class OptionEnum
                return id
        end
 
+       # Get the value name from `values`.
        fun value_name: String do return values[value]
 
        redef fun pretty_default
@@ -203,7 +218,10 @@ class OptionInt
        super OptionParameter
        redef type VALUE: Int
 
-       init(help: String, default: Int, names: String...) do super(help, default, names)
+       # Init a new OptionInt with a `help` message, a `default` value and `names`.
+       init(help: String, default: Int, names: String...) is old_style_init do
+               super(help, default, names)
+       end
 
        redef fun convert(str) do return str.to_i
 end
@@ -213,7 +231,10 @@ class OptionFloat
        super OptionParameter
        redef type VALUE: Float
 
-       init(help: String, default: Float, names: String...) do super(help, default, names)
+       # Init a new OptionFloat with a `help` message, a `default` value and `names`.
+       init(help: String, default: Float, names: String...) is old_style_init do
+               super(help, default, names)
+       end
 
        redef fun convert(str) do return str.to_f
 end
@@ -224,8 +245,8 @@ class OptionArray
        super OptionParameter
        redef type VALUE: Array[String]
 
-       init(help: String, names: String...)
-       do
+       # Init a new OptionArray with a `help` message and `names`.
+       init(help: String, names: String...) is old_style_init do
                values = new Array[String]
                super(help, values, names)
        end
@@ -352,6 +373,7 @@ class OptionContext
                end
        end
 
+       # Options parsing errors.
        fun get_errors: Array[String]
        do
                var errors = new Array[String]
index be553ad..0ca08b2 100644 (file)
@@ -159,6 +159,45 @@ redef interface Iterator[E]
        end
 end
 
+# Wraps an iterator to skip nulls.
+#
+# ~~~nit
+# var i: Iterator[Int]
+#
+# i = new NullSkipper[Int]([null, 1, null, 2, null: nullable Int].iterator)
+# assert i.to_a == [1, 2]
+#
+# i = new NullSkipper[Int]([1, null, 2, 3: nullable Int].iterator)
+# assert i.to_a == [1, 2, 3]
+# ~~~
+class NullSkipper[E: Object]
+       super Iterator[E]
+
+       # The inner iterator.
+       var inner: Iterator[nullable E]
+
+       redef fun finish do inner.finish
+
+       redef fun is_ok do
+               skip_nulls
+               return inner.is_ok
+       end
+
+       redef fun item do
+               skip_nulls
+               return inner.item.as(E)
+       end
+
+       redef fun next do
+               inner.next
+               skip_nulls
+       end
+
+       private fun skip_nulls do
+               while inner.is_ok and inner.item == null do inner.next
+       end
+end
+
 # Interface that reify a function.
 # Concrete subclasses must implements the `apply` method.
 #
diff --git a/lib/progression.nit b/lib/progression.nit
new file mode 100644 (file)
index 0000000..b5d7f96
--- /dev/null
@@ -0,0 +1,68 @@
+# 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.
+
+# Event-based interface to track the progression of an operation.
+module progression
+
+# An operation that is trackable using a `ProgressionListener`.
+abstract class Trackable
+
+       # Listen to the progression of the operation.
+       var progression_listeners: SimpleCollection[ProgressionListener] =
+                       new Array[ProgressionListener]
+
+       # Notice the registered `ProgessionListener` that the operation started.
+       protected fun fire_started do
+               for l in progression_listeners do
+                       l.started
+                       l.progressed(0)
+               end
+       end
+
+       # Notice the registered `ProgessionListener` that the operation progressed.
+       #
+       # Parameter:
+       #
+       # * `done_part`: Indicates what is done.
+       # * `total`: Indicates what need to be done, `done_part` included.
+       protected fun fire_progressed(done_part: Int, total: Int) do
+               for l in progression_listeners do
+                       l.progressed(done_part * l.progression_max / total)
+               end
+       end
+
+       # Notice the registered `ProgessionListener` that the operation is done.
+       protected fun fire_done do
+               for l in progression_listeners do
+                       l.progressed(l.progression_max)
+                       l.done
+               end
+       end
+end
+
+# Listens to the progression of a possibly long-running operation.
+interface ProgressionListener
+       # The number that represents a completed operation.
+       fun progression_max: Int do return 100
+
+       # The operation started.
+       fun started do end
+
+       # The operation progressed.
+       #
+       # Parameter:
+       #
+       # * `progression`: Indicator of the progession, between `0` and
+       # `progression_max`.
+       fun progressed(progression: Int) do end
+
+       # The operation is done.
+       fun done do end
+end
index 416b174..e408270 100644 (file)
@@ -16,8 +16,8 @@
 
 # Offers some POSIX threads services that are not available on all platforms
 module extra is
-       c_compiler_option("-pthread")
-       c_linker_option("-pthread")
+       cflags "-pthread"
+       ldflags "-pthread"
 end
 
 intrude import pthreads
index bce93c4..5d6b80d 100644 (file)
@@ -16,8 +16,8 @@
 
 # Main POSIX threads support and intro the classes `Thread`, `Mutex` and `Barrier`
 module pthreads is
-       c_compiler_option("-pthread")
-       c_linker_option("-pthread")
+       cflags "-pthread"
+       ldflags "-pthread"
        pkgconfig "bdw-gc"
 end
 
index b66ade0..3a02d86 100644 (file)
@@ -11,7 +11,7 @@
 # another product.
 
 # Provides the Clock utility class to keep time of real time flow
-module realtime is c_linker_option("-lrt")
+module realtime is ldflags "-lrt"
 
 in "C header" `{
 #ifdef _POSIX_C_SOURCE
index b01c3e5..aa567e8 100644 (file)
@@ -48,9 +48,16 @@ class Sprite
        # height of the sprite
        var height: Int = 100 is writable
 
+       # X coordinate of left side.
        fun left: Int do return x - width/2
+
+       # X coordinate of right side.
        fun right: Int do return x + width/2
+
+       # Y coordinate of top.
        fun top: Int do return y - height/2
+
+       # Y coordinate of bottom.
        fun bottom: Int do return y + height/2
 
        # x velocity (applied by `update')
@@ -102,10 +109,6 @@ class LiveGroup[E: LiveObject]
        super LiveObject
        super List[E]
 
-       init
-       do
-       end
-
        # Recursively update each live objects that `exists'
        redef fun update
        do
index 5225f9f..41ddc5b 100644 (file)
 
 # SDL display support (used in Linux for windows and inputes only)
 module sdl is
-       c_compiler_option(exec("sdl-config", "--cflags"))
-       c_linker_option(exec("sdl-config", "--libs"), "-lSDL_image -lSDL_ttf")
+       cflags exec("sdl-config", "--cflags")
+       ldflags(exec("sdl-config", "--libs"), "-lSDL_image -lSDL_ttf")
 end
 
 import mnit_display
+import c
 
 in "C header" `{
        #include <unistd.h>
@@ -160,7 +161,26 @@ extern class SDLDisplay `{SDL_Surface *`}
        fun warp_mouse(x,y: Int) `{ SDL_WarpMouse(x, y); `}
 
        # Show or hide the cursor
-       fun show_cursor(show: Bool) `{ SDL_ShowCursor(show); `}
+       fun show_cursor=(val: Bool) `{ SDL_ShowCursor(val? SDL_ENABLE: SDL_DISABLE); `}
+
+       # Is the cursor visible?
+       fun show_cursor: Bool `{ SDL_ShowCursor(SDL_QUERY); `}
+
+       # Grab or release the input
+       fun grab_input=(val: Bool) `{ SDL_WM_GrabInput(val? SDL_GRAB_ON: SDL_GRAB_OFF); `}
+
+       # Is the input grabbed?
+       fun grab_input: Bool `{ SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON; `}
+
+       # Are instances of `SDLMouseMotionEvent` ignored?
+       fun ignore_mouse_motion_events: Bool `{
+               return SDL_EventState(SDL_MOUSEMOTION, SDL_QUERY);
+       `}
+
+       # Do not raise instances of `SDLMouseMotionEvent` if `val`
+       fun ignore_mouse_motion_events=(val: Bool) `{
+               SDL_EventState(SDL_MOUSEMOTION, val? SDL_IGNORE: SDL_ENABLE);
+       `}
 end
 
 # Basic Drawing figures
@@ -225,6 +245,12 @@ extern class SDLImage
        redef fun height: Int `{ return recv->h; `}
 
        fun is_ok: Bool do return not address_is_null
+
+       # Returns a reference to the pixels of the texture
+       fun pixels: NativeCByteArray `{ return recv->pixels; `}
+
+       # Does this texture has an alpha mask?
+       fun amask: Bool `{ return recv->format->Amask; `}
 end
 
 # A simple rectangle
@@ -284,10 +310,10 @@ class SDLMouseButtonEvent
        fun is_left_button: Bool do return button == 1
 
        # Is this event raised by the right button?
-       fun is_right_button: Bool do return button == 2
+       fun is_right_button: Bool do return button == 3
 
        # Is this event raised by the middle button?
-       fun is_middle_button: Bool do return button == 3
+       fun is_middle_button: Bool do return button == 2
 
        # Is this event raised by the wheel going down?
        fun is_down_wheel: Bool do return button == 4
index 6dc53b3..ce642b8 100644 (file)
@@ -20,7 +20,7 @@
 # alone: JPG, PNG, TIF, GIT, ICO and much more.
 module image is
        pkgconfig "sdl2"
-       c_linker_option "-lSDL2_image"
+       ldflags "-lSDL2_image"
 end
 
 import sdl2
index 26d9937..d9bc5a3 100644 (file)
@@ -66,7 +66,11 @@ class TCPStream
                        closed = true
                        return
                end
-               socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1)
+               if not socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then
+                       end_reached = true
+                       closed = true
+                       return
+               end
                var hostname = socket.gethostbyname(host)
                addrin = new NativeSocketAddrIn.with_hostent(hostname, port)
 
@@ -107,7 +111,7 @@ class TCPStream
        fun ready_to_read(timeout: Int): Bool
        do
                if _buffer_pos < _buffer.length then return true
-               if eof then return false
+               if end_reached then return false
                var events = [new NativeSocketPollValues.pollin]
                return pollin(events, timeout).length != 0
        end
@@ -168,14 +172,17 @@ class TCPStream
                if closed then return
                if socket.close >= 0 then
                        closed = true
+                       end_reached = true
                end
        end
 
        # Send the data present in the socket buffer
        fun flush
        do
-               socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 1)
-               socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 0)
+               if not socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 1) or
+                  not socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 0) then
+                       closed = true
+               end
        end
 end
 
@@ -193,7 +200,10 @@ class TCPServer
                socket = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet,
                        new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_null)
                assert not socket.address_is_null
-               socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1)
+               if not socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then
+                       closed = true
+                       return
+               end
                addrin = new NativeSocketAddrIn.with(port, new NativeSocketAddressFamilies.af_inet)
                address = addrin.address
 
index 71867b9..44aff9e 100644 (file)
@@ -37,6 +37,7 @@ in "C" `{
 
 # Wrapper for the data structure PollFD used for polling on a socket
 class PollFD
+       super FinalizableOnce
 
        # The PollFD object
        private var poll_struct: NativeSocketPollFD
@@ -76,27 +77,30 @@ class PollFD
                return response & mask;
        `}
 
+       redef fun finalize_once
+       do
+               poll_struct.free
+       end
 end
 
 # Data structure used by the poll function
-private extern class NativeSocketPollFD `{ struct pollfd `}
+private extern class NativeSocketPollFD `{ struct pollfd * `}
 
-       # File descriptor id
-       private fun fd: Int `{ return recv.fd; `}
+       # File descriptor
+       fun fd: Int `{ return recv->fd; `}
 
        # List of events to be watched
-       private fun events: Int `{ return recv.events; `}
+       fun events: Int `{ return recv->events; `}
 
        # List of events received by the last poll function
-       private fun revents: Int `{  return recv.revents; `}
+       fun revents: Int `{  return recv->revents; `}
 
        new (pid: Int, events: NativeSocketPollValues) `{
-               struct pollfd poll;
-               poll.fd = pid;
-               poll.events = events;
+               struct pollfd *poll = malloc(sizeof(struct pollfd));
+               poll->fd = pid;
+               poll->events = events;
                return poll;
        `}
-
 end
 
 extern class NativeSocket `{ int* `}
@@ -141,12 +145,14 @@ extern class NativeSocket `{ int* `}
        `}
 
        # Sets an option for the socket
-       fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int) `{
+       #
+       # Returns `true` on success.
+       fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int): Bool `{
                int err = setsockopt(*recv, level, option_name, &option_value, sizeof(int));
                if(err != 0){
-                       perror("Error on setsockopts: ");
-                       exit(1);
+                       return 0;
                }
+               return 1;
        `}
 
        fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn)); `}
@@ -177,7 +183,7 @@ extern class NativeSocket `{ int* `}
        # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by
        # OR'ing a combination of the pollfd flags.
        private fun native_poll(filedesc: NativeSocketPollFD, timeout: Int): Int `{
-               int poll_return = poll(&filedesc, 1, timeout);
+               int poll_return = poll(filedesc, 1, timeout);
                return poll_return;
        `}
 
@@ -368,9 +374,6 @@ extern class NativeSocketAddressFamilies `{ int `}
        # Novell Internet Protocol
        new af_ipx `{ return AF_IPX; `}
 
-       # Integrated Services Digital Network
-       new af_isdn `{ return AF_ISDN; `}
-
        # IPv6
        new af_inet6 `{ return AF_INET6; `}
 
@@ -387,7 +390,6 @@ extern class NativeSocketProtocolFamilies `{ int `}
        new pf_decnet `{ return PF_DECnet; `}
        new pf_route `{ return PF_ROUTE; `}
        new pf_ipx `{ return PF_IPX; `}
-       new pf_isdn `{ return PF_ISDN; `}
        new pf_key `{ return PF_KEY; `}
        new pf_inet6 `{ return PF_INET6; `}
        new pf_max `{ return PF_MAX; `}
index d5621a9..bb17064 100644 (file)
@@ -377,7 +377,7 @@ interface Set[E: Object]
 end
 
 # MapRead are abstract associative collections: `key` -> `item`.
-interface MapRead[K: Object, V]
+interface MapRead[K, V]
        # Get the item at `key`
        #
        #     var x = new HashMap[String, Int]
@@ -492,7 +492,7 @@ end
 #     assert map.values.has(1)      ==  true
 #     assert map.values.has(3)      ==  false
 #
-interface Map[K: Object, V]
+interface Map[K, V]
        super MapRead[K, V]
 
        # Set the `value` at `key`.
@@ -552,7 +552,7 @@ interface Map[K: Object, V]
 end
 
 # Iterators for Map.
-interface MapIterator[K: Object, V]
+interface MapIterator[K, V]
        # The current item.
        # Require `is_ok`.
        fun item: V is abstract
@@ -583,7 +583,7 @@ interface MapIterator[K: Object, V]
 end
 
 # Iterator on a 'keys' point of view of a map
-class MapKeysIterator[K: Object, V]
+class MapKeysIterator[K, V]
        super Iterator[K]
        # The original iterator
        var original_iterator: MapIterator[K, V]
@@ -594,7 +594,7 @@ class MapKeysIterator[K: Object, V]
 end
 
 # Iterator on a 'values' point of view of a map
-class MapValuesIterator[K: Object, V]
+class MapValuesIterator[K, V]
        super Iterator[V]
        # The original iterator
        var original_iterator: MapIterator[K, V]
@@ -941,7 +941,7 @@ end
 
 # Associative arrays that internally uses couples to represent each (key, value) pairs.
 # This is an helper class that some specific implementation of Map may implements.
-interface CoupleMap[K: Object, V]
+interface CoupleMap[K, V]
        super Map[K, V]
 
        # Return the couple of the corresponding key
@@ -968,7 +968,7 @@ end
 # Iterator on CoupleMap
 #
 # Actually it is a wrapper around an iterator of the internal array of the map.
-private class CoupleMapIterator[K: Object, V]
+private class CoupleMapIterator[K, V]
        super MapIterator[K, V]
        redef fun item do return _iter.item.second
        
index 68f84e7..44ea5d3 100644 (file)
@@ -536,7 +536,7 @@ end
 
 
 # Associative arrays implemented with an array of (key, value) pairs.
-class ArrayMap[K: Object, E]
+class ArrayMap[K, E]
        super CoupleMap[K, E]
 
        # O(n)
@@ -618,7 +618,7 @@ class ArrayMap[K: Object, E]
        end
 end
 
-private class ArrayMapKeys[K: Object, E]
+private class ArrayMapKeys[K, E]
        super RemovableCollection[K]
        # The original map
        var map: ArrayMap[K, E]
@@ -638,7 +638,7 @@ private class ArrayMapKeys[K: Object, E]
        redef fun remove_all(key) do self.remove(key)
 end
 
-private class ArrayMapValues[K: Object, E]
+private class ArrayMapValues[K, E]
        super RemovableCollection[E]
        # The original map
        var map: ArrayMap[K, E]
index d389c5a..f385729 100644 (file)
 # You  are  allowed  to  redistribute it and sell it, alone or is a part of
 # another product.
 
-# Introduce Hashmap and Hashset.
+# Introduce `HashMap` and `HashSet`.
 module hash_collection
 
 import array
 
+redef class Map[K, V]
+       # Get a `HashMap[K, V]` as default implementation
+       new do return new HashMap[K, V]
+end
+
 # A HashCollection is an array of HashNode[K] indexed by the K hash value
-private abstract class HashCollection[K: Object]
+private abstract class HashCollection[K]
        type N: HashNode[K]
 
        var array: nullable NativeArray[nullable N] = null # Used to store items
@@ -35,12 +40,14 @@ private abstract class HashCollection[K: Object]
        # Return the index of the key k
        fun index_at(k: K): Int
        do
+               if k == null then return 0
+
                var i = k.hash % _capacity
                if i < 0 then i = - i
                return i
        end
 
-       # Return the node assosiated with the key
+       # Return the node associated with the key
        fun node_at(k: K): nullable N
        do
                # cache: `is` is used instead of `==` because it is a faster filter (even if not exact)
@@ -52,7 +59,7 @@ private abstract class HashCollection[K: Object]
                return res
        end
 
-       # Return the node assosiated with the key (but with the index already known)
+       # Return the node associated with the key (but with the index already known)
        fun node_at_idx(i: Int, k: K): nullable N
        do
                var c = _array[i]
@@ -190,7 +197,7 @@ private abstract class HashCollection[K: Object]
        end
 end
 
-private abstract class HashNode[K: Object]
+private abstract class HashNode[K]
        var key: K
        type N: HashNode[K]
        var next_item: nullable N = null
@@ -199,9 +206,20 @@ private abstract class HashNode[K: Object]
        var next_in_bucklet: nullable N = null
 end
 
-# A map implemented with a hash table.
-# Keys of such a map cannot be null and require a working `hash` method
-class HashMap[K: Object, V]
+# A `Map` implemented with a hash table.
+#
+# ~~~
+# var map = new HashMap[nullable String, Int]
+# map[null] = 0
+# map["one"] = 1
+# map["two"] = 2
+#
+# assert map[null] == 0
+# assert map["one"] == 1
+# assert map.keys.has("two")
+# assert map.values.length == 3
+# ~~~
+class HashMap[K, V]
        super Map[K, V]
        super HashCollection[K]
 
@@ -249,7 +267,7 @@ class HashMap[K: Object, V]
 end
 
 # View of the keys of a HashMap
-private class HashMapKeys[K: Object, V]
+private class HashMapKeys[K, V]
        super RemovableCollection[K]
        # The original map
        var map: HashMap[K, V]
@@ -270,7 +288,7 @@ private class HashMapKeys[K: Object, V]
 end
 
 # View of the values of a Map
-private class HashMapValues[K: Object, V]
+private class HashMapValues[K, V]
        super RemovableCollection[V]
        # The original map
        var map: HashMap[K, V]
@@ -340,14 +358,14 @@ private class HashMapValues[K: Object, V]
        end
 end
 
-private class HashMapNode[K: Object, V]
+private class HashMapNode[K, V]
        super HashNode[K]
        redef type N: HashMapNode[K, V]
        var value: V
 end
 
 # A `MapIterator` over a `HashMap`.
-class HashMapIterator[K: Object, V]
+class HashMapIterator[K, V]
        super MapIterator[K, V]
        redef fun is_ok do return _node != null
 
index acee1f6..23fa47f 100644 (file)
@@ -36,3 +36,29 @@ class Finalizable
        # to use attributes of this instances.
        fun finalize do end
 end
+
+# An object to be finalized only once
+#
+# This is an utility sub-class to `Finalizable` which ensures that `finalized_once`
+# is called only once per instance. User classes implementing `FinalizableOnce`
+# shoud specialize `finalize_once` and _not_ `finalize`. When manipulating the user
+# class, only `finalize` should be called as it protects `finalize_once`.
+class FinalizableOnce
+       super Finalizable
+
+       # Has `self` been finalized? (either by the GC or an explicit call to `finalize`)
+       var finalized = false
+
+       redef fun finalize
+       do
+               if finalized then return
+
+               finalize_once
+               finalized = true
+       end
+
+       # Real finalization method of `FinalizableOnce`, will be called only once
+       #
+       # See: `Finalizable::finalize` for restrictions on finalizer methods.
+       protected fun finalize_once do end
+end
index 46ad73c..ddc01fe 100644 (file)
@@ -493,5 +493,12 @@ class StringIStream
                source = ""
        end
 
+       redef fun read_all do
+               var c = cursor
+               cursor = source.length
+               if c == 0 then return source
+               return source.substring_from(c)
+       end
+
        redef fun eof do return cursor >= source.length
 end
index c4ae7b0..2c46179 100644 (file)
@@ -1092,15 +1092,19 @@ class FlatString
                        from = 0
                end
 
-               var realFrom = index_from + from
+               var new_from = index_from + from
 
-               if (realFrom + count) > index_to then return new FlatString.with_infos(items, index_to - realFrom + 1, realFrom, index_to)
+               if (new_from + count) > index_to then
+                       var new_len = index_to - new_from + 1
+                       if new_len <= 0 then return empty
+                       return new FlatString.with_infos(items, new_len, new_from, index_to)
+               end
 
-               if count == 0 then return empty
+               if count <= 0 then return empty
 
-               var to = realFrom + count - 1
+               var to = new_from + count - 1
 
-               return new FlatString.with_infos(items, to - realFrom + 1, realFrom, to)
+               return new FlatString.with_infos(items, to - new_from + 1, new_from, to)
        end
 
        redef fun empty do return "".as(FlatString)
@@ -2174,7 +2178,7 @@ redef class Map[K,V]
                var i = iterator
                var k = i.key
                var e = i.item
-               s.append("{k}{couple_sep}{e or else "<null>"}")
+               s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
 
                # Concat other items
                i.next
@@ -2182,7 +2186,7 @@ redef class Map[K,V]
                        s.append(sep)
                        k = i.key
                        e = i.item
-                       s.append("{k}{couple_sep}{e or else "<null>"}")
+                       s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
                        i.next
                end
                return s.to_s
index 1e9fb67..b865017 100644 (file)
@@ -32,8 +32,5 @@ end
 # A symbol is a unique immutable string
 class Symbol
        private var string: String
-       redef fun to_s do return _string.to_s
-
-       # Only used by String::to_symbol
-       private init(s: String) do _string = s
+       redef fun to_s do return string.to_s
 end
index 089df1b..8b4584c 100644 (file)
@@ -19,7 +19,7 @@ module websocket_server
 
 import websocket
 
-var sock = new WebSocket(8088, 1)
+var sock = new WebSocketListener(8088, 1)
 
 var msg: String
 
@@ -27,19 +27,21 @@ if sock.listener.closed then
        print sys.errno.strerror
 end
 
-sock.accept
+var cli: TCPStream
 
-while not sock.listener.closed do
-       if not sock.connected then sock.accept
-       if sys.stdin.poll_in then
-               msg = gets
-               printn "Received message : {msg}"
-               if msg == "exit" then sock.close
-               if msg == "disconnect" then sock.disconnect_client
-               sock.write(msg)
-       end
-       if sock.can_read(10) then
-               msg = sock.read_line
-               if msg != "" then print msg
+while not sock.closed do
+       cli = sock.accept
+       while cli.connected do
+               if sys.stdin.poll_in then
+                       msg = gets
+                       printn "Received message : {msg}"
+                       if msg == "disconnect" then cli.close
+                       cli.write(msg)
+               end
+               if cli.can_read(10) then
+                       msg = ""
+                       while cli.can_read(0) do msg += cli.read(100)
+                       if msg != "" then print msg
+               end
        end
 end
index ebf3f49..7e4d329 100644 (file)
@@ -24,14 +24,11 @@ import base64
 
 intrude import standard::stream
 
-# Websocket compatible server, works as an extra layer to the original Sockets
-class WebSocket
-       super BufferedIStream
-       super OStream
-       super PollableIStream
-
-       # Client connection to the server
-       var client: TCPStream
+# Websocket compatible listener
+#
+# Produces Websocket client-server connections
+class WebSocketListener
+       super Socket
 
        # Socket listening to connections on a defined port
        var listener: TCPServer
@@ -39,39 +36,50 @@ class WebSocket
        # Creates a new Websocket server listening on given port with `max_clients` slots available
        init(port: Int, max_clients: Int)
        do
-               _buffer = new FlatBuffer
-               _buffer_pos = 0
                listener = new TCPServer(port)
                listener.listen max_clients
        end
 
-       # Accept an incoming connection and initializes the handshake
-       fun accept
+       # Accepts an incoming connection
+       fun accept: WebsocketConnection
        do
                assert not listener.closed
 
                var client = listener.accept
                assert client != null
-               self.client = client
 
+               return new WebsocketConnection(listener.port, "", client)
+       end
+
+       # Stop listening for incoming connections
+       fun close
+       do
+               listener.close
+       end
+end
+
+# Connection to a websocket client
+#
+# Can be used to communicate with a client
+class WebsocketConnection
+       super TCPStream
+
+       init do
+               _buffer = new FlatBuffer
+               _buffer_pos = 0
                var headers = parse_handshake
                var resp = handshake_response(headers)
 
                client.write(resp)
        end
 
-       # Disconnect from a client
-       fun disconnect_client
-       do
-               client.close
-       end
+       # Client connection to the server
+       var client: TCPStream
 
-       # Disconnects the client if one is connected
-       # And stops the server
+       # Disconnect from a client
        redef fun close
        do
                client.close
-               listener.close
        end
 
        # Parses the input handshake sent by the client
@@ -132,9 +140,9 @@ class WebSocket
        # Reads an HTTP frame
        protected fun read_http_frame(buf: Buffer): String
        do
-               client.append_line_to(buf)
-               buf.chars.add('\n')
-               if buf.has_substring("\r\n\r\n", buf.length - 4) then return buf.to_s
+               buf.append client.read_line
+               buf.append("\r\n")
+               if buf.has_suffix("\r\n\r\n") then return buf.to_s
                return read_http_frame(buf)
        end
 
@@ -222,9 +230,9 @@ class WebSocket
        end
 
        # Checks if a connection to a client is available
-       fun connected: Bool do return client.connected
+       redef fun connected do return client.connected
 
-       redef fun write(msg: Text)
+       redef fun write(msg)
        do
                client.write(frame_message(msg.to_s))
        end
@@ -238,7 +246,7 @@ class WebSocket
                unpad_message
        end
 
-       redef fun end_reached do return _buffer_pos >= _buffer.length and client.eof
+       redef fun end_reached do return client._buffer_pos >= client._buffer.length and client.end_reached
 
        # Is there some data available to be read ?
        fun can_read(timeout: Int): Bool do return client.ready_to_read(timeout)
diff --git a/share/libgc/.gitignore b/share/libgc/.gitignore
new file mode 100644 (file)
index 0000000..98279d0
--- /dev/null
@@ -0,0 +1,4 @@
+include
+lib
+share
+src
diff --git a/share/libgc/android-setup-libgc.sh b/share/libgc/android-setup-libgc.sh
new file mode 100755 (executable)
index 0000000..8e20ca0
--- /dev/null
@@ -0,0 +1,98 @@
+#!/bin/bash
+# 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.
+
+# Fetch, configure and build libgc (the Boehm GC) for Android
+#
+# Will produce libgc.a which can be linked to Android NDK applications.
+#
+# The `ndk-build` tool from the Android NDK must be in PATH before
+# invoking this tool. It will be used to guess the path to the NDK.
+#
+# Alternatively, you may define a custom path to the NDK by setting
+# `ANDROID_NDK`.
+
+# If ANDROID_NDK is not set, get it from the path to `ndk-build`
+if test -z "$ANDROID_NDK"; then
+       ndk_build_path=`which ndk-build`
+       if test $? -ne 0; then
+               echo "Error: ndk-build from the Android NDK must be in your PATH"
+               exit 1
+       fi
+
+       ANDROID_NDK=`dirname $ndk_build_path`
+fi
+
+# Get the first platform available (it shouldn't change much, but it may
+# have to be adjusted)
+for platform in `echo $ANDROID_NDK/platforms/android-*/arch-arm`; do
+       SYS_ROOT=$platform
+       break
+done
+
+if test -z "$SYS_ROOT"; then
+       echo "Error: could not an Android platform in the NDK, define ANDROID_NDK to the correct path."
+       exit 1
+fi
+
+# Information on the currently targeted libgc and libatomic_ops source URL
+# These may have to be updated according to server-side changes and newer
+# versions of the Boehm GC.
+libgc_url=http://www.hboehm.info/gc/gc_source/gc-7.4.0.tar.gz
+libgc_dir=gc-7.4.0
+libatomic_ops_url=http://www.hboehm.info/gc/gc_source/libatomic_ops-7.4.0.tar.gz
+libatomic_ops_dir=libatomic_ops-7.4.0
+
+# Absolute installation path
+if expr match "$0" "^/.*"; then
+       install="`dirname "$0"`"
+else
+       install="`pwd`/`dirname "$0"`"
+fi
+
+# Local source directory
+mkdir -p "$install/src"
+cd "$install/src"
+
+# Download libs
+for url in $libgc_url $libatomic_ops_url; do
+       echo "Downloading $url..."
+       curl --progress-bar -o `basename $url` $url || exit 1
+done
+
+if test -d $libgc_dir; then
+       rm -r $libgc_dir
+fi
+
+# Extract
+tar -xzf `basename $libgc_url` || exit 1
+tar -xzf `basename $libatomic_ops_url` || exit 1
+mv $libatomic_ops_dir $libgc_dir/libatomic_ops || exit 1
+
+cd $libgc_dir || exit 1
+
+# Configure for Android
+path="$ANDROID_NDK/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/"
+export CC="$path/arm-linux-androideabi-gcc --sysroot=$SYS_ROOT"
+export CXX="$path/arm-linux-androideabi-g++ --sysroot=$SYS_ROOT"
+export LD="$path/arm-linux-androideabi-ld"
+export AR="$path/arm-linux-androideabi-ar"
+export RANLIB="$path/arm-linux-androideabi-ranlib"
+export STRIP="$path/arm-linux-androideabi-strip"
+export CFLAGS="-DIGNORE_DYNAMIC_LOADING -DPLATFORM_ANDROID -I libatomic_ops/src/"
+export LIBS="-lc -lgcc"
+./configure --host=arm-linux-androideabi --enable-static --disable-shared --prefix="$install" || exit 1
+
+# Compile and install locally
+make install -j 4 || exit 1
index 9c1d562..f98d50d 100644 (file)
@@ -329,6 +329,18 @@ Usually you do not need them since they make the generated code slower.
 `--colo-dead-methods`
 :   Force colorization of dead methods.
 
+`--colors-are-symbols`
+:   Store colors as symbols instead of static data.
+
+    By default, the various identifiers used to implement OO-mechanisms are stored as genuine constant static variables.
+
+    This option uses linker symbols to encode these identifiers.
+    This makes the compiled program faster since less indirections are required to get the values.
+    It also produces executables that are a little bit smaller since static memory does not have to store the colors.
+
+    Warning: the usage of linker symbols is not portable on all toolchains (eg. Mac OS X).
+    Also, this option does nothing on some platforms (like android).
+
 `--no-gcc-directive`
 :   Disable advanced gcc directives for optimization.
 
index 754f545..e85393d 100755 (executable)
@@ -26,8 +26,10 @@ echo "shortstat"
 git diff --shortstat "$orig".."$cur"
 echo "PR"
 git log --first-parent "$orig".."$cur" | grep 'Pull-Request: #' | wc -l
+echo "non-merge commits"
+git log --no-merges --oneline "$orig".."$cur"  | wc -l
 echo "shortlog"
-git shortlog -ens "$orig".."$cur"
+git shortlog -ens --no-merges "$orig".."$cur"
 echo log
 echo
 git log --format="* %s [[!commit %h]]" --first-parent "$orig".."$cur" | tac
index 2312544..dfe463c 100644 (file)
@@ -120,20 +120,16 @@ redef class ModelBuilder
        # Simple indirection to `Toolchain::write_and_make`
        protected fun write_and_make(compiler: AbstractCompiler)
        do
-               var platform = compiler.mainmodule.target_platform
-               var toolchain
-               if platform == null then
-                       toolchain = new MakefileToolchain(toolcontext)
-               else
-                       toolchain = platform.toolchain(toolcontext)
-               end
+               var platform = compiler.target_platform
+               var toolchain = platform.toolchain(toolcontext)
                compile_dir = toolchain.compile_dir
                toolchain.write_and_make compiler
        end
 end
 
 redef class Platform
-       fun toolchain(toolcontext: ToolContext): Toolchain is abstract
+       # The specific tool-chain associated to the platform
+       fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext)
 end
 
 class Toolchain
@@ -189,10 +185,10 @@ class MakefileToolchain
 
        fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
        do
-               var platform = compiler.mainmodule.target_platform
-               if self.toolcontext.opt_stacktrace.value == "nitstack" and (platform == null or platform.supports_libunwind) then compiler.build_c_to_nit_bindings
+               var platform = compiler.target_platform
+               if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
                var cc_opt_with_libgc = "-DWITH_LIBGC"
-               if platform != null and not platform.supports_libgc then cc_opt_with_libgc = ""
+               if not platform.supports_libgc then cc_opt_with_libgc = ""
 
                # Add gc_choser.h to aditionnal bodies
                var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc)
@@ -311,7 +307,7 @@ class MakefileToolchain
        fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
-               var platform = compiler.mainmodule.target_platform
+               var platform = compiler.target_platform
 
                var outname = outfile(mainmodule)
 
@@ -336,7 +332,7 @@ class MakefileToolchain
                makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS  ?= -lm {linker_options.join(" ")}\n\n")
 
                var ost = toolcontext.opt_stacktrace.value
-               if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n")
+               if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n")
 
                # Dynamic adaptations
                # While `platform` enable complex toolchains, they are statically applied
@@ -371,6 +367,18 @@ class MakefileToolchain
                        dep_rules.add(o)
                end
 
+               # Generate linker script, if any
+               if not compiler.linker_script.is_empty then
+                       var linker_script_path = "{compile_dir}/linker_script"
+                       ofiles.add "linker_script"
+                       var f = new OFStream.open(linker_script_path)
+                       for l in compiler.linker_script do
+                               f.write l
+                               f.write "\n"
+                       end
+                       f.close
+               end
+
                var java_files = new Array[ExternFile]
 
                var pkgconfigs = new Array[String]
@@ -476,9 +484,13 @@ abstract class AbstractCompiler
        # Is hardening asked? (see --hardening)
        fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
 
+       # The targeted specific platform
+       var target_platform: Platform is noinit
+
        init
        do
                self.realmainmodule = mainmodule
+               target_platform = mainmodule.target_platform or else new Platform
        end
 
        # Do the full code generation of the program `mainmodule`
@@ -511,6 +523,10 @@ abstract class AbstractCompiler
        # Where global declaration are stored (the main .h)
        var header: CodeWriter is writable, noinit
 
+       # Additionnal linker script for `ld`.
+       # Mainly used to do specific link-time symbol resolution
+       var linker_script = new Array[String]
+
        # Provide a declaration that can be requested (before or latter) by a visitor
        fun provide_declaration(key: String, s: String)
        do
@@ -681,11 +697,11 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                var v = self.new_visitor
                v.add_decl("#include <signal.h>")
                var ost = modelbuilder.toolcontext.opt_stacktrace.value
-               var platform = mainmodule.target_platform
+               var platform = target_platform
 
-               if platform != null and not platform.supports_libunwind then ost = "none"
+               if not platform.supports_libunwind then ost = "none"
 
-               var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value
+               var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
 
                if ost == "nitstack" or ost == "libunwind" then
                        v.add_decl("#define UNW_LOCAL_ONLY")
index 902a656..ea60c0a 100644 (file)
@@ -19,6 +19,6 @@ import separate_erasure_compiler
 import global_compiler
 import compiler_ffi
 
-import android_platform
-import pnacl_platform
-import emscripten_platform
+import platform::android
+import platform::pnacl
+import platform::emscripten
index ca22005..ac7bad3 100644 (file)
@@ -43,7 +43,7 @@ redef class MModule
 
                ensure_compile_nitni_base(v)
 
-               nitni_ccu.header_c_types.add("#include \"{name}._ffi.h\"\n")
+               nitni_ccu.header_c_types.add("#include \"{c_name}._ffi.h\"\n")
                nitni_ccu.header_c_types.add """
 extern void nitni_global_ref_incr(void*);
 extern void nitni_global_ref_decr(void*);
@@ -52,7 +52,7 @@ extern void nitni_global_ref_decr(void*);
                nitni_ccu.write_as_nitni(self, v.compiler.modelbuilder.compile_dir)
 
                for file in nitni_ccu.files do
-                       var f = new ExternCFile(file, c_compiler_options)
+                       var f = new ExternCFile(file, cflags)
                        f.pkgconfigs.add_all pkgconfigs
                        v.compiler.extern_bodies.add(f)
                end
@@ -76,7 +76,7 @@ extern void nitni_global_ref_decr(void*);
 
        redef fun collect_linker_libs
        do
-               var s = c_linker_options
+               var s = ldflags
                if s.is_empty then return null
                var res = new ArraySet[String]
                res.add s
@@ -255,11 +255,11 @@ end
 redef class CCompilationUnit
        fun write_as_nitni(mmodule: MModule, compdir: String)
        do
-               var base_name = "{mmodule.name}._nitni"
+               var base_name = "{mmodule.c_name}._nitni"
 
                var h_file = "{base_name}.h"
                write_header_to_file( mmodule, "{compdir}/{h_file}", new Array[String],
-                       "{mmodule.cname.to_s.to_upper}_NITG_NITNI_H")
+                       "{mmodule.c_name.to_s.to_upper}_NITG_NITNI_H")
 
                var c_file = "{base_name}.c"
                write_body_to_file( mmodule, "{compdir}/{c_file}", ["\"{h_file}\""] )
index 1f1cf67..9c03910 100644 (file)
@@ -29,6 +29,9 @@ redef class ToolContext
        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 = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
+       # --colors-are-symbols
+       var opt_colors_are_symbols = new OptionBool("Store colors as symbols (faster)", "--colors-are-symbols")
+
        # --inline-coloring-numbers
        var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers")
        # --inline-some-methods
@@ -50,7 +53,7 @@ redef class ToolContext
                self.option_context.add_option(self.opt_separate)
                self.option_context.add_option(self.opt_no_inline_intern)
                self.option_context.add_option(self.opt_no_union_attribute)
-               self.option_context.add_option(self.opt_no_shortcut_equate)
+               self.option_context.add_option(self.opt_no_shortcut_equate, opt_colors_are_symbols)
                self.option_context.add_option(self.opt_inline_coloring_numbers, opt_inline_some_methods, opt_direct_call_monomorph, opt_skip_dead_methods, opt_semi_global)
                self.option_context.add_option(self.opt_colo_dead_methods)
                self.option_context.add_option(self.opt_tables_metrics)
@@ -252,27 +255,21 @@ class SeparateCompiler
 
        fun compile_color_const(v: SeparateCompilerVisitor, m: Object, color: Int) do
                if color_consts_done.has(m) then return
-               if m isa MProperty then
-                       if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
-                               self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
-                       else
-                               self.provide_declaration(m.const_color, "extern const int {m.const_color};")
-                               v.add("const int {m.const_color} = {color};")
-                       end
-               else if m isa MPropDef then
+               if m isa MEntity then
                        if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
                                self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
-                       else
+                       else if not modelbuilder.toolcontext.opt_colors_are_symbols.value or not v.compiler.target_platform.supports_linker_script then
                                self.provide_declaration(m.const_color, "extern const int {m.const_color};")
                                v.add("const int {m.const_color} = {color};")
-                       end
-               else if m isa MType then
-                       if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then
-                               self.provide_declaration(m.const_color, "#define {m.const_color} {color}")
                        else
-                               self.provide_declaration(m.const_color, "extern const int {m.const_color};")
-                               v.add("const int {m.const_color} = {color};")
+                               # The color 'C' is the ``address'' of a false static variable 'XC'
+                               self.provide_declaration(m.const_color, "#define {m.const_color} ((long)&X{m.const_color})\nextern const void X{m.const_color};")
+                               if color == -1 then color = 0 # Symbols cannot be negative, so just use 0 for dead things
+                               # Teach the linker that the address of 'XC' is `color`.
+                               linker_script.add("X{m.const_color} = {color};")
                        end
+               else
+                       abort
                end
                color_consts_done.add(m)
        end
@@ -563,7 +560,7 @@ class SeparateCompiler
                                var r = pd.separate_runtime_function
                                r.compile_to_c(self)
                                var r2 = pd.virtual_runtime_function
-                               r2.compile_to_c(self)
+                               if r2 != r then r2.compile_to_c(self)
                        end
                end
                self.mainmodule = old_module
@@ -1183,8 +1180,10 @@ class SeparateCompilerVisitor
 
                var res0 = before_send(mmethod, arguments)
 
+               var runtime_function = mmethod.intro.virtual_runtime_function
+               var msignature = runtime_function.called_signature
+
                var res: nullable RuntimeVariable
-               var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true)
                var ret = msignature.return_mtype
                if ret == null then
                        res = null
@@ -1192,10 +1191,8 @@ class SeparateCompilerVisitor
                        res = self.new_var(ret)
                end
 
-               var s = new FlatBuffer
                var ss = new FlatBuffer
 
-               s.append("val*")
                ss.append("{recv}")
                for i in [0..msignature.arity[ do
                        var a = arguments[i+1]
@@ -1203,16 +1200,12 @@ class SeparateCompilerVisitor
                        if i == msignature.vararg_rank then
                                t = arguments[i+1].mcasttype
                        end
-                       s.append(", {t.ctype}")
                        a = self.autobox(a, t)
                        ss.append(", {a}")
                end
 
-
-               var r
-               if ret == null then r = "void" else r = ret.ctype
                self.require_declaration(const_color)
-               var call = "(({r} (*)({s}))({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
+               var call = "(({runtime_function.c_ret} (*){runtime_function.c_sig})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/"
 
                if res != null then
                        self.add("{res} = {call};")
@@ -1781,108 +1774,112 @@ class SeparateCompilerVisitor
 end
 
 redef class MMethodDef
-       fun separate_runtime_function: AbstractRuntimeFunction
+       # The C function associated to a mmethoddef
+       fun separate_runtime_function: SeparateRuntimeFunction
        do
                var res = self.separate_runtime_function_cache
                if res == null then
-                       res = new SeparateRuntimeFunction(self)
+                       var recv = mclassdef.bound_mtype
+                       var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true)
+                       res = new SeparateRuntimeFunction(self, recv, msignature, c_name)
                        self.separate_runtime_function_cache = res
                end
                return res
        end
        private var separate_runtime_function_cache: nullable SeparateRuntimeFunction
 
-       fun virtual_runtime_function: AbstractRuntimeFunction
+       # The C function associated to a mmethoddef, that can be stored into a VFT of a class
+       # The first parameter (the reciever) is always typed by val* in order to accept an object value
+       # The C-signature is always compatible with the intro
+       fun virtual_runtime_function: SeparateRuntimeFunction
        do
                var res = self.virtual_runtime_function_cache
                if res == null then
-                       res = new VirtualRuntimeFunction(self)
+                       # Because the function is virtual, the signature must match the one of the original class
+                       var intromclassdef = mproperty.intro.mclassdef
+                       var recv = intromclassdef.bound_mtype
+
+                       res = separate_runtime_function
+                       if res.called_recv == recv then
+                               self.virtual_runtime_function_cache = res
+                               return res
+                       end
+
+                       var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true)
+
+                       if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then
+                               self.virtual_runtime_function_cache = res
+                               return res
+                       end
+
+                       res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}")
                        self.virtual_runtime_function_cache = res
+                       res.is_thunk = true
                end
                return res
        end
-       private var virtual_runtime_function_cache: nullable VirtualRuntimeFunction
+       private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction
+end
+
+redef class MSignature
+       # Does the C-version of `self` the same than the C-version of `other`?
+       fun c_equiv(other: MSignature): Bool
+       do
+               if self == other then return true
+               if arity != other.arity then return false
+               for i in [0..arity[ do
+                       if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false
+               end
+               if return_mtype != other.return_mtype then
+                       if return_mtype == null or other.return_mtype == null then return false
+                       if return_mtype.ctype != other.return_mtype.ctype then return false
+               end
+               return true
+       end
 end
 
 # The C function associated to a methoddef separately compiled
 class SeparateRuntimeFunction
        super AbstractRuntimeFunction
 
-       redef fun build_c_name: String do return "{mmethoddef.c_name}"
+       # The call-side static receiver
+       var called_recv: MType
 
-       redef fun to_s do return self.mmethoddef.to_s
+       # The call-side static signature
+       var called_signature: MSignature
 
-       redef fun compile_to_c(compiler)
-       do
-               var mmethoddef = self.mmethoddef
+       # The name on the compiled method
+       redef var build_c_name: String
 
-               var recv = self.mmethoddef.mclassdef.bound_mtype
-               var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", recv, recv)
-               var arguments = new Array[RuntimeVariable]
-               var frame = new StaticFrame(v, mmethoddef, recv, arguments)
-               v.frame = frame
+       # Statically call the original body instead
+       var is_thunk = false
 
-               var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
+       redef fun to_s do return self.mmethoddef.to_s
 
-               var sig = new FlatBuffer
-               var comment = new FlatBuffer
-               var ret = msignature.return_mtype
+       # The C return type (something or `void`)
+       var c_ret: String is lazy do
+               var ret = called_signature.return_mtype
                if ret != null then
-                       sig.append("{ret.ctype} ")
+                       return ret.ctype
                else
-                       sig.append("void ")
+                       return "void"
                end
-               sig.append(self.c_name)
-               sig.append("({selfvar.mtype.ctype} {selfvar}")
-               comment.append("({selfvar}: {selfvar.mtype}")
-               arguments.add(selfvar)
-               for i in [0..msignature.arity[ do
-                       var mtype = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               mtype = v.get_class("Array").get_mtype([mtype])
+       end
+
+       # The C signature (only the parmeter part)
+       var c_sig: String is lazy do
+               var sig = new FlatBuffer
+               sig.append("({called_recv.ctype} self")
+               for i in [0..called_signature.arity[ do
+                       var mtype = called_signature.mparameters[i].mtype
+                       if i == called_signature.vararg_rank then
+                               mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype])
                        end
-                       comment.append(", {mtype}")
                        sig.append(", {mtype.ctype} p{i}")
-                       var argvar = new RuntimeVariable("p{i}", mtype, mtype)
-                       arguments.add(argvar)
                end
                sig.append(")")
-               comment.append(")")
-               if ret != null then
-                       comment.append(": {ret}")
-               end
-               compiler.provide_declaration(self.c_name, "{sig};")
-
-               v.add_decl("/* method {self} for {comment} */")
-               v.add_decl("{sig} \{")
-               if ret != null then
-                       frame.returnvar = v.new_var(ret)
-               end
-               frame.returnlabel = v.get_name("RET_LABEL")
-
-               if recv != arguments.first.mtype then
-                       #print "{self} {recv} {arguments.first}"
-               end
-               mmethoddef.compile_inside_to_c(v, arguments)
-
-               v.add("{frame.returnlabel.as(not null)}:;")
-               if ret != null then
-                       v.add("return {frame.returnvar.as(not null)};")
-               end
-               v.add("\}")
-               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
+               return sig.to_s
        end
-end
-
-# The C function associated to a methoddef on a primitive type, stored into a VFT of a class
-# The first parameter (the reciever) is always typed by val* in order to accept an object value
-class VirtualRuntimeFunction
-       super AbstractRuntimeFunction
-
-       redef fun build_c_name: String do return "VIRTUAL_{mmethoddef.c_name}"
-
-       redef fun to_s do return self.mmethoddef.to_s
 
        redef fun compile_to_c(compiler)
        do
@@ -1890,25 +1887,20 @@ class VirtualRuntimeFunction
 
                var recv = self.mmethoddef.mclassdef.bound_mtype
                var v = compiler.new_visitor
-               var selfvar = new RuntimeVariable("self", v.object_type, recv)
+               var selfvar = new RuntimeVariable("self", called_recv, recv)
                var arguments = new Array[RuntimeVariable]
                var frame = new StaticFrame(v, mmethoddef, recv, arguments)
                v.frame = frame
 
+               var msignature = called_signature
+               var ret = called_signature.return_mtype
+
                var sig = new FlatBuffer
                var comment = new FlatBuffer
-
-               # Because the function is virtual, the signature must match the one of the original class
-               var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef
-               var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
-               var ret = msignature.return_mtype
-               if ret != null then
-                       sig.append("{ret.ctype} ")
-               else
-                       sig.append("void ")
-               end
+               sig.append(c_ret)
+               sig.append(" ")
                sig.append(self.c_name)
-               sig.append("({selfvar.mtype.ctype} {selfvar}")
+               sig.append(c_sig)
                comment.append("({selfvar}: {selfvar.mtype}")
                arguments.add(selfvar)
                for i in [0..msignature.arity[ do
@@ -1917,11 +1909,9 @@ class VirtualRuntimeFunction
                                mtype = v.get_class("Array").get_mtype([mtype])
                        end
                        comment.append(", {mtype}")
-                       sig.append(", {mtype.ctype} p{i}")
                        var argvar = new RuntimeVariable("p{i}", mtype, mtype)
                        arguments.add(argvar)
                end
-               sig.append(")")
                comment.append(")")
                if ret != null then
                        comment.append(": {ret}")
@@ -1935,10 +1925,14 @@ class VirtualRuntimeFunction
                end
                frame.returnlabel = v.get_name("RET_LABEL")
 
-               var subret = v.call(mmethoddef, recv, arguments)
-               if ret != null then
-                       assert subret != null
-                       v.assign(frame.returnvar.as(not null), subret)
+               if is_thunk then
+                       var subret = v.call(mmethoddef, recv, arguments)
+                       if ret != null then
+                               assert subret != null
+                               v.assign(frame.returnvar.as(not null), subret)
+                       end
+               else
+                       mmethoddef.compile_inside_to_c(v, arguments)
                end
 
                v.add("{frame.returnlabel.as(not null)}:;")
@@ -1946,27 +1940,22 @@ class VirtualRuntimeFunction
                        v.add("return {frame.returnvar.as(not null)};")
                end
                v.add("\}")
-               if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}--{mmethoddef.location.line_start})"
+               compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})"
        end
-
-       # TODO ?
-       redef fun call(v, arguments) do abort
 end
 
-redef class MType
-       fun const_color: String do return "COLOR_{c_name}"
+redef class MEntity
+       var const_color: String is lazy do return "COLOR_{c_name}"
 end
 
 interface PropertyLayoutElement end
 
 redef class MProperty
        super PropertyLayoutElement
-       fun const_color: String do return "COLOR_{c_name}"
 end
 
 redef class MPropDef
        super PropertyLayoutElement
-       fun const_color: String do return "COLOR_{c_name}"
 end
 
 redef class AMethPropdef
index 8a33265..2c90e65 100644 (file)
@@ -235,13 +235,9 @@ class SeparateErasureCompiler
                                                v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
                                                continue
                                        end
-                                       if true or mpropdef.mclassdef.bound_mtype.ctype != "val*" then
-                                               v.require_declaration("VIRTUAL_{mpropdef.c_name}")
-                                               v.add_decl("(nitmethod_t)VIRTUAL_{mpropdef.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
-                                       else
-                                               v.require_declaration("{mpropdef.c_name}")
-                                               v.add_decl("(nitmethod_t){mpropdef.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */")
-                                       end
+                                       var rf = mpropdef.virtual_runtime_function
+                                       v.require_declaration(rf.c_name)
+                                       v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mpropdef.full_name} */")
                                end
                        end
                        v.add_decl("\}")
index 1c77939..1cdc51a 100644 (file)
@@ -143,6 +143,10 @@ private class NitdocDecorator
        end
 end
 
+# Decorator for span elements.
+#
+# Because inline comments can appear as span elements,
+# InlineDecorator do not decorate things like paragraphs or headers.
 private class InlineDecorator
        super NitdocDecorator
 
@@ -150,6 +154,10 @@ private class InlineDecorator
                v.emit_in block
        end
 
+       redef fun add_headline(v, block) do
+               v.emit_in block
+       end
+
        redef fun add_code(v, block) do
                # Try to parse code
                var ast = toolcontext.parse_something(block.block.text.to_s)
index f8a1250..38e6cdf 100644 (file)
@@ -166,7 +166,7 @@ class Nitdoc
 
        private fun modules do
                for mmodule in model.mmodules do
-                       if mmodule.is_fictive then continue
+                       if mmodule.is_fictive or mmodule.is_test_suite then continue
                        var page = new NitdocModule(ctx, model, mainmodule, mmodule)
                        page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}")
                end
@@ -211,7 +211,7 @@ class QuickSearch
 
        init do
                for mmodule in model.mmodules do
-                       if mmodule.is_fictive then continue
+                       if mmodule.is_fictive or mmodule.is_test_suite then continue
                        add_result_for(mmodule.name, mmodule.full_name, mmodule.nitdoc_url)
                end
                for mclass in model.mclasses do
@@ -667,7 +667,7 @@ class NitdocSearch
        private fun modules_list: Array[MModule] do
                var sorted = new Array[MModule]
                for mmodule in model.mmodule_importation_hierarchy do
-                       if mmodule.is_fictive then continue
+                       if mmodule.is_fictive or mmodule.is_test_suite then continue
                        sorted.add mmodule
                end
                name_sorter.sort(sorted)
@@ -933,7 +933,7 @@ class NitdocModule
                # Imports
                var lst = new Array[MModule]
                for dep in imports do
-                       if dep.is_fictive then continue
+                       if dep.is_fictive or dep.is_test_suite then continue
                        if dep == mmodule then continue
                        lst.add(dep)
                end
@@ -945,7 +945,7 @@ class NitdocModule
                # Clients
                lst = new Array[MModule]
                for dep in clients do
-                       if dep.is_fictive then continue
+                       if dep.is_fictive or dep.is_test_suite then continue
                        if dep == mmodule then continue
                        lst.add(dep)
                end
@@ -1024,10 +1024,10 @@ class NitdocModule
        fun tpl_dot(mmodules: Collection[MModule]): nullable TplArticle do
                var poset = new POSet[MModule]
                for mmodule in mmodules do
-                       if mmodule.is_fictive then continue
+                       if mmodule.is_fictive or mmodule.is_test_suite then continue
                        poset.add_node mmodule
                        for omodule in mmodules do
-                               if mmodule.is_fictive then continue
+                               if omodule.is_fictive or omodule.is_test_suite then continue
                                poset.add_node mmodule
                                if mmodule.in_importation < omodule then
                                        poset.add_edge(mmodule, omodule)
index 8ae08b9..ebd2277 100644 (file)
@@ -72,8 +72,8 @@ redef class Location
 end
 
 redef class MModule
-       var c_compiler_options = "" is writable
-       var c_linker_options = "" is writable
+       var cflags = "" is writable
+       var ldflags = "" is writable
 
        # Additional libraries needed for the compilation
        # Will be used with pkg-config
index af4e938..e4756a8 100644 (file)
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Offers the annotations `c_compiler_option` and `c_linker_option` to specify
+# Offers the annotations `cflags` and `ldflags` to specify
 # options for the C compiler directly or indirectly. Differs from the `pkgconfig`
 # annotation by the separation of the options between the compiler and linker.
 module c_compiler_options
@@ -24,15 +24,15 @@ import cpp
 private import annotation
 
 redef class ToolContext
-       var c_compiler_options_phase: Phase = new CCompilerOptionsPhase(self, null)
+       var cflags_phase: Phase = new CCompilerOptionsPhase(self, null)
 end
 
 private class CCompilerOptionsPhase
        super Phase
 
-       fun compiler_annotation_name: String do return "c_compiler_option"
-       fun linker_annotation_name: String do return "c_linker_option"
-       fun cpp_compiler_annotation_name: String do return "cpp_compiler_option"
+       fun compiler_annotation_name: String do return "cflags"
+       fun linker_annotation_name: String do return "ldflags"
+       fun cpp_compiler_annotation_name: String do return "cppflags"
 
        redef fun process_annotated_node(nmoduledecl, nat)
        do
@@ -145,17 +145,17 @@ private class CCompilerOptionsPhase
 
        fun process_c_compiler_annotation(mmodule: MModule, opt: String)
        do
-               mmodule.c_compiler_options = "{mmodule.c_compiler_options} {opt}"
+               mmodule.cflags = "{mmodule.cflags} {opt}"
        end
 
        fun process_c_linker_annotation(mmodule: MModule, opt: String)
        do
-               mmodule.c_linker_options = "{mmodule.c_linker_options} {opt}"
+               mmodule.ldflags = "{mmodule.ldflags} {opt}"
        end
 
        fun process_cpp_compiler_annotation(mmodule: MModule, opt: String)
        do
-               mmodule.cpp_compiler_options = "{mmodule.cpp_compiler_options} {opt}"
+               mmodule.cppflags = "{mmodule.cppflags} {opt}"
        end
 end
 
index 8ca389a..e114cd2 100644 (file)
@@ -27,7 +27,7 @@ end
 redef class MModule
        private var cpp_file: nullable CPPCompilationUnit = null
 
-       var cpp_compiler_options = "" is writable
+       var cppflags = "" is writable
 end
 
 class CPPLanguage
@@ -124,7 +124,7 @@ class CPPLanguage
 
                # write .cpp and .hpp file
                cpp_file.header_custom.add("extern \"C\" \{\n")
-               cpp_file.header_custom.add("#include \"{mmodule.name}._ffi.h\"\n")
+               cpp_file.header_custom.add("#include \"{mmodule.c_name}._ffi.h\"\n")
                cpp_file.header_custom.add("\}\n")
 
                var file = cpp_file.write_to_files(mmodule, compdir)
@@ -133,7 +133,7 @@ class CPPLanguage
                mmodule.ffi_files.add(file)
 
                # add linked option to support C++
-               mmodule.c_linker_options = "{mmodule.c_linker_options} -lstdc++"
+               mmodule.ldflags = "{mmodule.ldflags} -lstdc++"
        end
 
        redef fun compile_callback(callback, mmodule, mainmodule, ecc)
@@ -158,10 +158,10 @@ class CPPCompilationUnit
 
        fun write_to_files(mmodule: MModule, compdir: String): ExternCppFile
        do
-               var base_name = "{mmodule.name}._ffi"
+               var base_name = "{mmodule.c_name}._ffi"
 
                var h_file = "{base_name}.hpp"
-               var guard = "{mmodule.cname.to_s.to_upper}_NIT_HPP"
+               var guard = "{mmodule.c_name.to_s.to_upper}_NIT_HPP"
 
                write_header_to_file(mmodule, "{compdir}/{h_file}", new Array[String], guard)
 
@@ -180,7 +180,7 @@ class ExternCppFile
        var mmodule: MModule
 
        redef fun makefile_rule_name do return "{filename.basename("")}.o"
-       redef fun makefile_rule_content do return "$(CXX) $(CFLAGS) {mmodule.cpp_compiler_options} -c {filename.basename("")} -o {filename.basename("")}.o"
+       redef fun makefile_rule_content do return "$(CXX) $(CFLAGS) {mmodule.cppflags} -c {filename.basename("")} -o {filename.basename("")}.o"
        redef fun compiles_to_o_file do return true
 end
 
index 019d201..cccb7a0 100644 (file)
@@ -57,12 +57,12 @@ redef class MModule
 
                # include dependancies FFI
                for mod in header_dependencies do
-                       if mod.uses_ffi then ffi_ccu.header_custom.add("#include \"{mod.name}._ffi.h\"\n")
+                       if mod.uses_ffi then ffi_ccu.header_custom.add("#include \"{mod.c_name}._ffi.h\"\n")
                end
 
                ffi_ccu.write_as_impl(self, compdir)
                for filename in ffi_ccu.files do
-                       var f = new ExternCFile(filename, c_compiler_options)
+                       var f = new ExternCFile(filename, cflags)
                        f.pkgconfigs.add_all pkgconfigs
                        ffi_files.add(f)
                end
@@ -95,7 +95,7 @@ redef class AModule
                        language.compile_module_block(block, ffi_ccu, mmodule)
                end
 
-               ffi_ccu.header_c_base.add( "#include \"{mmodule.name}._nitni.h\"\n" )
+               ffi_ccu.header_c_base.add( "#include \"{mmodule.c_name}._nitni.h\"\n" )
 
                ffi_ccu.body_decl.add("#ifdef ANDROID\n")
                ffi_ccu.body_decl.add(" #include <android/log.h>\n")
index e470276..babab08 100644 (file)
@@ -152,10 +152,10 @@ end
 redef class CCompilationUnit
        fun write_as_impl(mmodule: MModule, compdir: String)
        do
-               var base_name = "{mmodule.name}._ffi"
+               var base_name = "{mmodule.c_name}._ffi"
 
                var h_file = "{base_name}.h"
-               var guard = "{mmodule.cname.to_s.to_upper}_NIT_H"
+               var guard = "{mmodule.c_name.to_upper}_NIT_H"
                write_header_to_file(mmodule, "{compdir}/{h_file}", new Array[String], guard)
 
                var c_file = "{base_name}.c"
index 10c1db3..84bbb29 100644 (file)
@@ -33,36 +33,32 @@ redef class AModule
 end
 
 redef class MModule
-       private var header_dependencies_cache: nullable Array[MModule] = null
-       fun header_dependencies: Array[MModule]
-       do
-               var cache = header_dependencies_cache
-               assert cache != null
-               return cache
-       end
+       # Modules with public foreign code blocks (C header)
+       var header_dependencies: nullable HashSet[MModule] = null
 
        private fun compute_header_dependencies(v: HeaderDependancyPhase)
        do
-               if header_dependencies_cache != null then return
+               if header_dependencies != null then return
 
-               var header_dependencies = new Array[MModule]
+               var header_dependencies = new HashSet[MModule]
 
                # gather from importation
                for m in in_importation.direct_greaters do
-                       m.compute_header_dependencies(v)
+                       m.compute_header_dependencies v
 
-                       # does the super module has inherited dependancies?
+                       # does the super module has inherited dependencies?
                        var hd = m.header_dependencies
+                       assert hd != null
                        if not hd.is_empty then
-                               header_dependencies.add_all(hd)
+                               header_dependencies.add_all hd
                        end
 
-                       # does the super module itself has extern dependancies?
+                       # does the super module itself has extern dependencies?
                        var amodule = v.toolcontext.modelbuilder.mmodule2node(m)
                        if amodule != null and amodule.has_public_c_header then header_dependencies.add(m)
                end
 
-               header_dependencies_cache = header_dependencies
+               self.header_dependencies = header_dependencies
        end
 end
 
index 1fa56e9..77c3c56 100644 (file)
@@ -242,8 +242,8 @@ redef class MModule
        # Tell the C compiler where to find jni.h and how to link with libjvm
        private fun insert_compiler_options
        do
-               c_compiler_options = "{c_compiler_options} -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
-               c_linker_options = "{c_linker_options} -L $(JNI_LIB_PATH) -ljvm"
+               cflags = "{cflags} -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
+               ldflags = "{ldflags} -L $(JNI_LIB_PATH) -ljvm"
        end
 
        # Name of the generated Java class where to store all implementation methods of this module
index 9ab6f7f..7e309bc 100644 (file)
@@ -32,6 +32,27 @@ end
 
 redef class MModule
        private var objc_file: nullable ObjCCompilationUnit = null
+
+       private var has_public_objc_header = false
+
+       # Imported modules with public Objective-C code blocks
+       var objc_imported_headers: HashSet[MModule] is lazy do
+               var dep = new HashSet[MModule]
+
+               # gather from importation
+               for m in in_importation.direct_greaters do
+                       # does the super module has inherited dependencies?
+                       var import_dep = m.objc_imported_headers
+                       if not import_dep.is_empty then
+                               dep.add_all import_dep
+                       end
+
+                       # does the super module itself has a public header?
+                       if m.has_public_objc_header then dep.add(m)
+               end
+
+               return dep
+       end
 end
 
 # The Objective-C langugage visitor
@@ -47,6 +68,8 @@ class ObjCLanguage
                if block.is_objc_header then
                        mmodule.objc_file.header_custom.add block.location.as_line_pragma
                        mmodule.objc_file.header_custom.add block.code
+
+                       mmodule.has_public_objc_header = true
                else if block.is_objc_body then
                        mmodule.objc_file.body_custom.add block.location.as_line_pragma
                        mmodule.objc_file.body_custom.add block.code
@@ -77,9 +100,15 @@ class ObjCLanguage
                var objc_file = mmodule.objc_file
                assert objc_file != null
 
+               # Import public Objective-C header of imported modules
+               var dep = mmodule.objc_imported_headers
+               for mod in dep do
+                       objc_file.header_custom.add "#include \"{mod.c_name}._ffi_m.h\"\n"
+               end
+
                # write .m and _m.h file
                mmodule.objc_file.header_c_types.add """
-       #include "{{{mmodule.cname}}}._ffi.h"
+       #include "{{{mmodule.c_name}}}._ffi.h"
 """
 
                var file = objc_file.write_to_files(mmodule, compdir)
@@ -114,10 +143,10 @@ private class ObjCCompilationUnit
        # Write this compilation unit to Objective-C source files
        fun write_to_files(mmodule: MModule, compdir: String): ExternObjCFile
        do
-               var base_name = "{mmodule.cname}._ffi"
+               var base_name = "{mmodule.c_name}._ffi"
 
                var h_file = "{base_name}_m.h"
-               var guard = "{mmodule.cname.to_s.to_upper}_NIT_OBJC_H"
+               var guard = "{mmodule.c_name.to_upper}_NIT_OBJC_H"
                write_header_to_file(mmodule, compdir/h_file, new Array[String], guard)
 
                var c_file = "{base_name}.m"
@@ -125,7 +154,7 @@ private class ObjCCompilationUnit
 
                files.add compdir/c_file
 
-               mmodule.c_linker_options = "{mmodule.c_linker_options} -lobjc"
+               mmodule.ldflags = "{mmodule.ldflags} -lobjc"
 
                return new ExternObjCFile(compdir/c_file, mmodule)
        end
@@ -211,6 +240,13 @@ end
 private class FromObjCCallContext
        super ObjCCallContext
 
+       redef fun cast_to(mtype, name)
+       do
+               if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
+                       return "(__bridge void*)({name})"
+               else return name
+       end
+
        redef fun cast_from(mtype, name)
        do
                if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then
index fa909bc..a51bece 100644 (file)
@@ -92,8 +92,8 @@ extern
 no_warning
 
 pkgconfig
-c_compiler_option
-c_linker_option
+cflags
+ldflags
 
 platform
 """
index 5675a7d..50f98c7 100644 (file)
@@ -97,15 +97,16 @@ redef class ModelBuilder
                        sock.close
                        sys.set_io(ns,ns,ns)
                else if self.toolcontext.opt_websocket_mode.value then
-                       var websock = new WebSocket(toolcontext.opt_debug_port.value, 1)
-                       websock.accept
-                       sys.set_io(websock,websock,websock)
+                       var websock = new WebSocketListener(toolcontext.opt_debug_port.value, 1)
+                       var cli = websock.accept
+                       websock.close
+                       sys.set_io(cli,cli,cli)
                end
        end
 
        fun close_stdstreams
        do
-               if sys.stdin isa WebSocket or sys.stdin isa TCPStream then
+               if sys.stdin isa TCPStream then
                        sys.stdin.close
                        sys.stdout.close
                        sys.stderr.close
@@ -118,6 +119,6 @@ redef class Sys
        do
                self.stdin = istream
                self.stdout = ostream
-               self.stderr = ostream
+               self.stderr = errstream
        end
 end
index 127815b..0de93d3 100644 (file)
 module primitive_types
 
 intrude import standard::file
+intrude import standard::string
 
 # Wrapper for `NativeFile`
 class PrimitiveNativeFile
 
-       var file: FStream
+       var file: IOS
 
        init native_stdin do
-               file = new IFStream.from_fd(0)
+               file = sys.stdin
        end
 
        init native_stdout do
-               file = new OFStream.from_fd(1)
+               file = sys.stdout
        end
 
        init native_stderr do
-               file = new OFStream.from_fd(2)
+               file = sys.stderr
        end
 
        init io_open_read(path: String) do
@@ -41,19 +42,42 @@ class PrimitiveNativeFile
                file = new OFStream.open(path.to_s)
        end
 
-       fun address_is_null: Bool do return file._file.address_is_null
+       fun address_is_null: Bool do
+               if file isa FStream then return file.as(FStream)._file.address_is_null
+               return false
+       end
 
-       fun io_read(buf: NativeString, len: Int): Int do return file._file.io_read(buf, len)
+       fun io_read(buf: NativeString, len: Int): Int do
+               if file isa FStream then return file.as(FStream)._file.io_read(buf, len)
+               var str = file.as(IStream).read(len)
+               str.to_cstring.copy_to(buf, str.length, 0, 0)
+               return str.length
+       end
 
-       fun io_write(buf: NativeString, len: Int): Int do return file._file.io_write(buf, len)
+       fun io_write(buf: NativeString, len: Int): Int do
+               if file isa FStream then return file.as(FStream)._file.io_write(buf, len)
+               file.as(OStream).write(buf.to_s_with_length(len))
+               return len
+       end
 
-       fun io_close: Int do return file._file.io_close
+       fun io_close: Int do
+               if file isa FStream then return file.as(FStream)._file.io_close
+               file.close
+               return 0
+       end
 
-       fun fileno: Int do return file._file.fileno
+       fun fileno: Int do
+               if file isa FStream then return file.as(FStream)._file.fileno
+               return 0
+       end
 
-       fun flush: Int do return file._file.flush
+       fun flush: Int do
+               if file isa FStream then return file.as(FStream)._file.flush
+               return 0
+       end
 
        fun set_buffering_type(size, mode: Int): Int do
-               return file._file.set_buffering_type(size, mode)
+               if file isa FStream then return file.as(FStream)._file.set_buffering_type(size, mode)
+               return 0
        end
 end
index 58d3506..a046815 100644 (file)
@@ -528,6 +528,7 @@ redef class ModelBuilder
                self.mmodule2nmodule[mmodule] = nmodule
 
                if decl != null then
+                       # Extract documentation
                        var ndoc = decl.n_doc
                        if ndoc != null then
                                var mdoc = ndoc.to_mdoc
@@ -536,6 +537,8 @@ redef class ModelBuilder
                        else
                                advice(decl, "missing-doc", "Documentation warning: Undocumented module `{mmodule}`")
                        end
+                       # Is the module a test suite?
+                       mmodule.is_test_suite = not decl.get_annotations("test_suite").is_empty
                end
 
                return mmodule
index e66f164..d88a0ba 100644 (file)
@@ -76,6 +76,14 @@ class MModule
        # The group of module in the project if any
        var mgroup: nullable MGroup
 
+       # The project of the module if any
+       # Safe alias for `mgroup.mproject`
+       fun mproject: nullable MProject
+       do
+               var g = mgroup
+               if g == null then return null else return g.mproject
+       end
+
        # The short name of the module
        redef var name: String
 
@@ -104,6 +112,22 @@ class MModule
                end
        end
 
+       # The namespace used for entities according to their visibility `v`.
+       #
+       # Public entities use only the project as a namespace.
+       # Private entities use the `full_name` (i.e. "project::module")
+       #
+       # This method is used by entities to implement their `full_name`.
+       fun namespace_for(v: MVisibility): String do
+               if v <= private_visibility then return full_name
+               var mgroup = self.mgroup
+               if mgroup == null then
+                       return full_name
+               else
+                       return mgroup.mproject.full_name
+               end
+       end
+
        # Return the name of the global C identifier associated to `self`.
        # This name is used to prefix files and other C identifiers associated with `self`.
        redef var c_name: String is lazy do
@@ -117,6 +141,19 @@ class MModule
                return res
        end
 
+       # C identifier version of `namespace_for`.
+       # See `c_name`
+       #
+       # This method is used by entities to implement their `c_name`.
+       fun c_namespace_for(v: MVisibility): String do
+               if v <= private_visibility then return c_name
+               var mgroup = self.mgroup
+               if mgroup == null then
+                       return c_name
+               else
+                       return mgroup.mproject.c_name
+               end
+       end
 
        # Create a new empty module and register it to a model
        init
@@ -203,5 +240,8 @@ class MModule
        # exposed to the final user.
        var is_fictive: Bool = false is writable
 
+       # Is `self` a unit test module used by `nitunit`?
+       var is_test_suite: Bool = false is writable
+
        redef fun parent_concern do return mgroup
 end
index 4d8ddc4..ff12710 100644 (file)
@@ -361,9 +361,13 @@ class MClass
        #
        # It is the name of the class prefixed by the full_name of the `intro_mmodule`
        # Example: `"owner::module::MyClass"`
-       redef var full_name is lazy do return "{self.intro_mmodule.full_name}::{name}"
+       redef var full_name is lazy do
+               return "{self.intro_mmodule.namespace_for(visibility)}::{name}"
+       end
 
-       redef var c_name is lazy do return "{intro_mmodule.c_name}__{name.to_cmangle}"
+       redef var c_name is lazy do
+               return "{intro_mmodule.c_namespace_for(visibility)}__{name.to_cmangle}"
+       end
 
        # The number of generic formal parameters
        # 0 if the class is not generic
@@ -539,17 +543,29 @@ class MClassDef
        # Example: "my_module#intro_module::MyClass"
        redef var full_name is lazy do
                if is_intro then
+                       # public gives 'p#A'
+                       # private gives 'p::m#A'
+                       return "{mmodule.namespace_for(mclass.visibility)}#{mclass.name}"
+               else if mclass.intro_mmodule.mproject != mmodule.mproject then
+                       # public gives 'q::n#p::A'
+                       # private gives 'q::n#p::m::A'
+                       return "{mmodule.full_name}#{mclass.full_name}"
+               else if mclass.visibility > private_visibility then
+                       # public gives 'p::n#A'
                        return "{mmodule.full_name}#{mclass.name}"
                else
-                       return "{mmodule.full_name}#{mclass.full_name}"
+                       # private gives 'p::n#::m::A' (redundant p is omitted)
+                       return "{mmodule.full_name}#::{mclass.intro_mmodule.name}::{mclass.name}"
                end
        end
 
        redef var c_name is lazy do
                if is_intro then
-                       return mclass.c_name
+                       return "{mmodule.c_namespace_for(mclass.visibility)}___{mclass.c_name}"
+               else if mclass.intro_mmodule.mproject == mmodule.mproject and mclass.visibility > private_visibility then
+                       return "{mmodule.c_name}___{mclass.name.to_cmangle}"
                else
-                       return "{mmodule.c_name}__{mclass.c_name.to_cmangle}"
+                       return "{mmodule.c_name}___{mclass.c_name}"
                end
        end
 
@@ -1738,11 +1754,12 @@ abstract class MProperty
        # It is the short-`name` prefixed by the short-name of the class and the full-name of the module.
        # Example: "my_project::my_module::MyClass::my_method"
        redef var full_name is lazy do
-               return "{intro_mclassdef.mmodule.full_name}::{intro_mclassdef.mclass.name}::{name}"
+               return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
        end
 
        redef var c_name is lazy do
-               return "{intro_mclassdef.mmodule.c_name}__{intro_mclassdef.mclass.c_name}__{name.to_cmangle}"
+               # FIXME use `namespace_for`
+               return "{intro_mclassdef.mmodule.c_name}__{intro_mclassdef.mclass.name.to_cmangle}__{name.to_cmangle}"
        end
 
        # The visibility of the property
@@ -2018,42 +2035,66 @@ abstract class MPropDef
        # Therefore the combination of identifiers is awful,
        # the worst case being
        #
-       # ~~~nitish
-       # "{mclassdef.mmodule.full_name}#{mclassdef.mclass.intro_mmodule.full_name}::{mclassdef.name}#{mproperty.intro_mclassdef.mmodule.full_name}::{mproperty.intro_mclassdef.name}::{name}"
-       # ~~~
+       #  * a property "p::m::A::x"
+       #  * redefined in a refinement of a class "q::n::B"
+       #  * in a module "r::o"
+       #  * so "r::o#q::n::B#p::m::A::x"
        #
        # Fortunately, the full-name is simplified when entities are repeated.
-       # The simplest form is "my_module#MyClass#my_property".
+       # For the previous case, the simplest form is "p#A#x".
        redef var full_name is lazy do
                var res = new FlatBuffer
-               res.append mclassdef.mmodule.full_name
-               res.append "#"
-               if not mclassdef.is_intro then
-                       res.append mclassdef.mclass.intro_mmodule.full_name
-                       res.append "::"
-               end
-               res.append mclassdef.name
+
+               # The first part is the mclassdef. Worst case is "r::o#q::n::B"
+               res.append mclassdef.full_name
+
                res.append "#"
-               if mproperty.intro_mclassdef.mmodule != mclassdef.mmodule then
-                       res.append mproperty.intro_mclassdef.mmodule.full_name
-                       res.append "::"
-               end
-               if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
-                       res.append mproperty.intro_mclassdef.mclass.name
-                       res.append "::"
+
+               if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
+                       # intro are unambiguous in a class
+                       res.append name
+               else
+                       # Just try to simplify each part
+                       if mclassdef.mmodule.mproject != mproperty.intro_mclassdef.mmodule.mproject then
+                               # precise "p::m" only if "p" != "r"
+                               res.append mproperty.intro_mclassdef.mmodule.full_name
+                               res.append "::"
+                       else if mproperty.visibility <= private_visibility then
+                               # Same project ("p"=="q"), but private visibility,
+                               # does the module part ("::m") need to be displayed
+                               if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mproject then
+                                       res.append "::"
+                                       res.append mproperty.intro_mclassdef.mmodule.name
+                                       res.append "::"
+                               end
+                       end
+                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
+                               # precise "B" only if not the same class than "A"
+                               res.append mproperty.intro_mclassdef.name
+                               res.append "::"
+                       end
+                       # Always use the property name "x"
+                       res.append mproperty.name
                end
-               res.append name
                return res.to_s
        end
 
        redef var c_name is lazy do
                var res = new FlatBuffer
                res.append mclassdef.c_name
-               res.append "__"
-               if is_intro then
+               res.append "___"
+               if mclassdef.mclass == mproperty.intro_mclassdef.mclass then
                        res.append name.to_cmangle
                else
-                       res.append mproperty.c_name.to_cmangle
+                       if mclassdef.mmodule != mproperty.intro_mclassdef.mmodule then
+                               res.append mproperty.intro_mclassdef.mmodule.c_name
+                               res.append "__"
+                       end
+                       if mclassdef.mclass != mproperty.intro_mclassdef.mclass then
+                               res.append mproperty.intro_mclassdef.name.to_cmangle
+                               res.append "__"
+                       end
+                       res.append mproperty.name.to_cmangle
                end
                return res.to_s
        end
index 9002009..283d54c 100644 (file)
@@ -27,6 +27,10 @@ class MProject
        # The name of the project
        redef var name: String
 
+       redef fun full_name do return name
+
+       redef var c_name = name.to_cmangle is lazy
+
        # The model of the project
        redef var model: Model
 
index 4f31aaf..6685029 100644 (file)
@@ -22,7 +22,6 @@ module nitni_base
 
 import parser
 import modelbuilder # builder only for externcalls
-private import compiler::abstract_compiler
 
 redef class MMethod
        # Short name of this method in C (without the class name)
@@ -52,12 +51,6 @@ redef class MMethod
        end
 end
 
-redef class MModule
-       # Mangled name of this module in C
-       fun cname: String do return c_name # FIXME this is a hack to keep the internal FFI
-       # API independent of the compilers while still using the `MModule::c_name` service.
-end
-
 redef class MMethodDef
        # Name of the function to callback this method from C,
        # also used in other functions names used for this method.
index af3fae0..9dd723e 100644 (file)
@@ -32,6 +32,20 @@ redef class ToolContext
        var opt_meld = new OptionBool("Show diff between source and output using meld",
           "--meld")
 
+       # Break too long string literals.
+       var opt_break_str = new OptionBool("Break too long string literals", "--break-strings")
+
+       # Force `do` on the same line as the method signature.
+       var opt_inline_do = new OptionBool("Force do keyword on the same line as the method signature",
+               "--inline-do")
+
+       # Force formatting on empty lines.
+       #
+       # By default empty lines are kept as they were typed in the file.
+       # When enabling this option, `nitpretty` will decide where to break lines
+       # and will put empty lines to separate properties and code blocks.
+       var opt_skip_empty = new OptionBool("Force formatting of empty lines", "--skip-empty")
+
        # Check formatting instead of pretty printing.
        #
        # This option create a tempory pretty printed file then check if
@@ -52,9 +66,11 @@ end
 # process options
 var toolcontext = new ToolContext
 
-toolcontext.option_context.
-   add_option(toolcontext.opt_dir, toolcontext.opt_output, toolcontext.opt_diff,
-   toolcontext.opt_meld, toolcontext.opt_check)
+var opts = toolcontext.option_context
+opts.add_option(toolcontext.opt_dir, toolcontext.opt_output)
+opts.add_option(toolcontext.opt_diff, toolcontext.opt_meld, toolcontext.opt_check)
+opts.add_option(toolcontext.opt_break_str, toolcontext.opt_inline_do)
+opts.add_option(toolcontext.opt_skip_empty)
 
 toolcontext.tooldescription = "Usage: nitpretty [OPTION]... <file.nit>\n" +
        "Pretty print Nit code from Nit source files."
@@ -81,6 +97,16 @@ var dir = toolcontext.opt_dir.value or else ".nitpretty"
 if not dir.file_exists then dir.mkdir
 var v = new PrettyPrinterVisitor
 
+if toolcontext.opt_break_str.value then
+       v.break_strings = true
+end
+if toolcontext.opt_inline_do.value then
+       v.inline_do = true
+end
+if toolcontext.opt_skip_empty.value then
+       v.skip_empty = true
+end
+
 for mmodule in mmodules do
        var nmodule = mbuilder.mmodule2node(mmodule)
        if nmodule == null then
similarity index 82%
rename from src/compiler/android_platform.nit
rename to src/platform/android.nit
index 5d6d312..78ba820 100644 (file)
 # limitations under the License.
 
 # Compile program for the Android platform
-module android_platform
+module android
 
 import platform
-import abstract_compiler
+import compiler::abstract_compiler
 import ffi
 intrude import ffi::extra_java_files
 import android_annotations
@@ -34,8 +34,12 @@ end
 class AndroidPlatform
        super Platform
 
+       redef fun supports_libgc do return true
+
        redef fun supports_libunwind do return false
 
+       redef fun supports_linker_script do return false
+
        redef fun toolchain(toolcontext) do return new AndroidToolchain(toolcontext)
 end
 
@@ -144,11 +148,11 @@ include $(call all-subdir-makefiles)
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_CFLAGS   := -D ANDROID
+LOCAL_CFLAGS   := -D ANDROID -D WITH_LIBGC
 LOCAL_MODULE    := main
 LOCAL_SRC_FILES := \\
 {{{cfiles.join(" \\\n")}}}
-LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM -lz
+LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM -lz libgc.a
 LOCAL_STATIC_LIBRARIES := android_native_app_glue png
 
 include $(BUILD_SHARED_LIBRARY)
@@ -174,7 +178,8 @@ $(call import-module,android/native_app_glue)
     <application
                android:label="@string/app_name"
                android:hasCode="true"
-               android:debuggable="{{{not release}}}">
+               android:debuggable="{{{not release}}}"
+               {{{icon_declaration}}}>
 
         <!-- Our activity is the built-in NativeActivity framework class.
              This will take care of integrating with our NDK code. -->
@@ -216,6 +221,15 @@ $(call import-module,android/native_app_glue)
                        toolcontext.exec_and_check(["ln", "-s", "{share_dir}/png/", target_png_dir], "Android project error")
                end
 
+               # Ensure that android-setup-libgc.sh has been executed
+               if not "{share_dir}/libgc/lib".file_exists then
+                       toolcontext.exec_and_check(["{share_dir}/libgc/android-setup-libgc.sh"], "Android project error")
+               end
+
+               # Copy GC files
+               toolcontext.exec_and_check(["cp", "{share_dir}/libgc/lib/libgc.a", "{android_project_root}/libgc.a"], "Android project error")
+               toolcontext.exec_and_check(["ln", "-s", "{share_dir}/libgc/include/gc/", "{android_project_root}/jni/nit_compile/gc"], "Android project error")
+
                ### Link to assets (for mnit and others)
                # This will be accessed from `android_project_root`
                var assets_dir
@@ -290,12 +304,40 @@ $(call import-module,android/native_app_glue)
                # Move the apk to the target
                var outname = outfile(compiler.mainmodule)
 
-               var src_apk_suffix
                if release then
-                       src_apk_suffix = "release-unsigned"
-               else src_apk_suffix = "debug"
+                       var apk_path = "{android_project_root}/bin/{compiler.mainmodule.name}-release-unsigned.apk"
+
+                       # Sign APK
+                       var keystore_path= "KEYSTORE".environ
+                       var key_alias= "KEY_ALIAS".environ
+                       var tsa_server= "TSA_SERVER".environ
+
+                       if key_alias.is_empty then
+                               toolcontext.fatal_error(null,
+                                       "Fatal Error: the environment variable `KEY_ALIAS` must be set to use the `--release` option on Android projects.")
+                       end
 
-               toolcontext.exec_and_check(["mv", "{android_project_root}/bin/{compiler.mainmodule.name}-{src_apk_suffix}.apk", outname], "Android project error")
+                       args = ["jarsigner", "-sigalg", "MD5withRSA", "-digestalg", "SHA1", apk_path, key_alias]
+
+                       ## Use a custom keystore
+                       if not keystore_path.is_empty then args.add_all(["-keystore", keystore_path])
+
+                       ## Use a TSA server
+                       if not tsa_server.is_empty then args.add_all(["-tsa", tsa_server])
+
+                       toolcontext.exec_and_check(args, "Android project error")
+
+                       # Clean output file
+                       if outname.to_path.exists then outname.to_path.delete
+
+                       # Align APK
+                       args = ["zipalign", "4", apk_path, outname]
+                       toolcontext.exec_and_check(args, "Android project error")
+               else
+                       # Move to the expected output path
+                       args = ["mv", "{android_project_root}/bin/{compiler.mainmodule.name}-debug.apk", outname]
+                       toolcontext.exec_and_check(args, "Android project error")
+               end
        end
 end
 
similarity index 94%
rename from src/compiler/emscripten_platform.nit
rename to src/platform/emscripten.nit
index 0a262fc..dc2662f 100644 (file)
 # limitations under the License.
 
 # Compile to JavaScript using the Emscripten SDK
-module emscripten_platform
+module emscripten
 
 import platform
-import abstract_compiler
+import compiler::abstract_compiler
 
 redef class ToolContext
        redef fun platform_from_name(name)
@@ -33,6 +33,7 @@ class EmscriptenPlatform
 
        redef fun supports_libunwind do return false
        redef fun supports_libgc do return false
+       redef fun supports_linker_script do return false
        redef fun toolchain(toolcontext) do return new EnscriptenToolchain(toolcontext)
 end
 
similarity index 97%
rename from src/platform.nit
rename to src/platform/platform.nit
index 8f2edbb..5b7f7a5 100644 (file)
@@ -103,7 +103,7 @@ end
 # Sub-classes of `Platform` represent the target platform of a compilation
 #
 # Services will be added to this class in other modules.
-abstract class Platform
+class Platform
        # Does the platform provide and support the library `unwind`?
        fun supports_libunwind: Bool do return true
 
@@ -112,4 +112,7 @@ abstract class Platform
 
        # Does this platform declare its own main function? If so, we won't generate one in Nit.
        fun no_main: Bool do return false
+
+       # Does the platform accepts linker scripts?
+       fun supports_linker_script: Bool do return true
 end
similarity index 99%
rename from src/compiler/pnacl_platform.nit
rename to src/platform/pnacl.nit
index 3f96318..9fceaad 100644 (file)
 # limitations under the License.
 
 # Compile program for the PNaCl platform
-module pnacl_platform
+module pnacl
 
 import platform
-import abstract_compiler
+import compiler::abstract_compiler
 
 redef class ToolContext
        redef fun platform_from_name(name)
index 3b9f7ef..2d08571 100644 (file)
@@ -79,7 +79,7 @@ class PrettyPrinterVisitor
                current_token = nmodule.location.file.first_token
                visit nmodule
                catch_up nmodule.location.file.last_token
-               tpl.add "\n"
+               if skip_empty then tpl.add "\n"
                return tpl.as(not null)
        end
 
@@ -172,7 +172,7 @@ class PrettyPrinterVisitor
                else
                        abort
                end
-               assert current_token.location <= token.location
+               if current_token.location > token.location then return
                while current_token != token do visit current_token
        end
 
@@ -183,7 +183,7 @@ class PrettyPrinterVisitor
                        visit current_token
                end
 
-               while current_token isa TEol do skip
+               while current_token isa TEol do visit(current_token)
        end
 
        # The template under construction.
@@ -223,6 +223,14 @@ class PrettyPrinterVisitor
                if current_length == 0 and last_line_is_blank then return
                previous_length = current_length
                current_length = 0
+               if skip_empty then wait_addn += 1
+       end
+
+       # Perform `addn` even if not `skip_empty`.
+       fun forcen do
+               if current_length == 0 and last_line_is_blank then return
+               previous_length = current_length
+               current_length = 0
                wait_addn += 1
        end
 
@@ -243,6 +251,15 @@ class PrettyPrinterVisitor
                        consume "."
                end
        end
+
+       # Do we break string literals that are too long?
+       var break_strings = false is public writable
+
+       # Do we force `do` to be on the same line as the method signature?
+       var inline_do = false is public writable
+
+       # Do we force the deletion of empty lines?
+       var skip_empty = false is public writable
 end
 
 # Base framework redefs
@@ -255,9 +272,10 @@ redef class ANodes[E]
                        if e != first then
                                if not e_can_inline then
                                        v.add ","
-                                       v.addn
-                                       v.addt
+                                       v.forcen
+                                       v.indent += 1
                                        v.addt
+                                       v.indent -= 1
                                else
                                        v.add ", "
                                end
@@ -308,6 +326,17 @@ redef class Token
        redef fun was_inline do return true
 end
 
+redef class TEol
+       redef fun accept_pretty_printer(v) do
+               if v.skip_empty then
+                       super
+               else
+                       v.add text
+                       v.current_token = next_token
+               end
+       end
+end
+
 redef class Prod
        redef fun accept_pretty_printer(v) do v.visit first_token
 
@@ -344,7 +373,7 @@ redef class Prod
        end
 
        redef fun was_inline do
-               return first_token.location.line_start == last_token.location.line_end
+               return start_token.location.line_start == last_token.location.line_end
        end
 end
 
@@ -355,13 +384,13 @@ redef class TComment
                if is_adoc then
                        v.addt
                        super
-                       v.addn
+                       v.forcen
                        return
                end
 
                if is_licence then
                        super
-                       v.addn
+                       v.forcen
                        if is_last_in_group then v.addn
                        return
                end
@@ -370,7 +399,7 @@ redef class TComment
                        v.addn
                        v.addt
                        super
-                       v.addn
+                       v.forcen
                        v.addn
                        return
                end
@@ -379,13 +408,14 @@ redef class TComment
                        if next_token isa TComment and is_first_in_group then v.addn
                        v.addt
                        super
-                       v.addn
+                       v.forcen
                        var prev_token = self.prev_token
                        if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn
                        return
                end
 
                super
+               if not v.skip_empty then v.forcen
        end
 
        # Is `self` part of an `ADoc`?
@@ -434,7 +464,6 @@ redef class AAnnotations
        redef fun accept_pretty_printer(v) do
                v.adds
                v.consume "is"
-
                if v.can_inline(self) then
                        v.adds
                        for n_item in n_items do
@@ -443,21 +472,27 @@ redef class AAnnotations
                                        v.add ", "
                                end
                        end
-                       v.finish_line
-               else if n_items.length > 1 then
-                       v.addn
+                       if not was_inline then
+                               v.finish_line
+                               if v.current_token isa TKwend then v.skip
+                       end
+               else
+                       v.forcen
                        v.indent += 1
-
                        for n_item in n_items do
                                v.addt
                                v.visit n_item
                                v.finish_line
-                               v.addn
+                               if n_item != n_items.last then
+                                       if was_inline then
+                                               v.forcen
+                                       else
+                                               v.addn
+                                       end
+                               end
                        end
-
                        v.indent -= 1
                end
-               if not was_inline and v.current_token isa TKwend then v.skip
        end
 
        redef fun is_inlinable do
@@ -469,6 +504,10 @@ end
 
 redef class AAnnotation
        redef fun accept_pretty_printer(v) do
+               if n_visibility != null and not n_visibility isa APublicVisibility then
+                       v.visit n_visibility
+                       v.adds
+               end
                v.visit n_atid
                if not n_args.is_empty then
                        if n_opar == null then
@@ -494,7 +533,7 @@ redef class AModule
                v.visit n_moduledecl
 
                if not n_imports.is_empty then
-                       v.addn
+                       if v.skip_empty then v.addn
 
                        for n_import in n_imports do
                                v.catch_up n_import
@@ -516,7 +555,7 @@ redef class AModule
                end
 
                if not n_classdefs.is_empty then
-                       v.addn
+                       if v.skip_empty then v.addn
 
                        for n_classdef in n_classdefs do
                                v.catch_up n_classdef
@@ -561,7 +600,7 @@ redef class AModuledecl
                end
 
                v.finish_line
-               v.addn
+               if v.skip_empty then v.addn
        end
 end
 
@@ -582,7 +621,7 @@ redef class ANoImport
                v.adds
                v.visit n_kwend
                v.finish_line
-               v.addn
+               if v.skip_empty then v.addn
        end
 end
 
@@ -597,7 +636,7 @@ redef class AStdImport
                v.adds
                v.visit n_name
                v.finish_line
-               v.addn
+               if v.skip_empty then v.addn
        end
 end
 
@@ -609,9 +648,9 @@ redef class AClassdef
                        v.catch_up n_propdef
 
                        if n_propdef.n_doc != null or not v.can_inline(n_propdef) then
-                               if n_propdef != n_propdefs.first then v.addn
+                               if v.skip_empty and n_propdef != n_propdefs.first then v.addn
                                v.visit n_propdef
-                               if n_propdef != n_propdefs.last then v.addn
+                               if v.skip_empty and n_propdef != n_propdefs.last then v.addn
                        else
                                v.visit n_propdef
                        end
@@ -660,7 +699,7 @@ redef class AStdClassdef
                        end
                else
                        v.finish_line
-                       v.addn
+                       if v.skip_empty then v.addn
                        v.indent += 1
 
                        for n_superclass in n_superclasses do
@@ -672,7 +711,7 @@ redef class AStdClassdef
                        end
 
                        if not n_superclasses.is_empty and not n_propdefs.is_empty then
-                               v.addn
+                               if v.skip_empty then v.addn
                        end
 
                        super
@@ -682,7 +721,7 @@ redef class AStdClassdef
 
                v.visit n_kwend
                v.finish_line
-               v.addn
+               if v.skip_empty then v.addn
                assert v.indent == 0
        end
 
@@ -772,6 +811,90 @@ redef class APropdef
                end
        end
 
+       # Factorize annotations visit for all APropdef.
+       #
+       # Return true if annotations were inlined.
+       fun visit_annotations(v: PrettyPrinterVisitor, n_annotations: nullable AAnnotations): Bool do
+               var res = v.can_inline(n_annotations)
+               if n_annotations != null then v.visit n_annotations
+               return res
+       end
+
+       # Factorize block visit for APropdefs.
+       #
+       # Were annotations printed inline? If so, we need to print the block differently.
+       fun visit_block(v: PrettyPrinterVisitor, n_block: nullable AExpr, annot_inline: Bool) do
+               # var can_inline = v.can_inline(n_block)
+               if n_block == null then return
+               if n_annotations != null and not annot_inline then
+                       v.forcen
+                       v.addt
+               end
+               if v.inline_do then
+                       while not v.current_token isa TKwdo do v.skip
+               end
+               var token = v.current_token
+               var do_inline = true
+               loop
+                       if token isa TEol then
+                               v.skip
+                               if not v.can_inline(n_block) then
+                                       v.forcen
+                                       v.addt
+                                       do_inline = false
+                               end
+                       end
+                       token = v.current_token
+                       if token isa TKwdo then break
+               end
+               if annot_inline and do_inline then v.adds
+               v.consume "do"
+
+               if v.can_inline(n_block) and do_inline then
+                       v.adds
+
+                       if n_block isa ABlockExpr then
+                               if n_block.n_expr.is_empty then
+                                       v.visit n_block.n_kwend
+                               else
+                                       v.visit n_block.n_expr.first
+                                       v.current_token = n_block.n_kwend
+                                       v.skip
+                               end
+                       else
+                               v.visit n_block
+                               if v.current_token isa TKwend then v.skip
+                       end
+               else
+                       v.finish_line
+                       if was_inline then
+                               v.forcen
+                       else
+                               v.addn
+                       end
+                       v.indent += 1
+
+                       if n_block isa ABlockExpr then
+                               n_block.force_block = true
+                               v.visit n_block
+                               v.catch_up n_block.n_kwend
+                       else
+                               v.addt
+                               v.visit n_block
+                               v.forcen
+                       end
+
+                       v.indent -= 1
+                       v.addt
+                       if n_block isa ABlockExpr then
+                               v.visit n_block.n_kwend
+                       else
+                               v.add "end"
+                       end
+               end
+               v.finish_line
+       end
+
        redef fun start_token do
                if n_doc == null then return super
                return n_doc.last_token.next_token
@@ -798,7 +921,8 @@ redef class AAttrPropdef
                        v.visit n_expr
                end
 
-               if n_annotations != null then v.visit n_annotations
+               var annot_inline = visit_annotations(v, n_annotations)
+               visit_block(v, n_block, annot_inline)
                v.finish_line
                v.addn
        end
@@ -822,6 +946,7 @@ redef class ATypePropdef
                v.consume ":"
                v.adds
                v.visit n_type
+               visit_annotations(v, n_annotations)
                v.finish_line
                v.addn
        end
@@ -834,7 +959,6 @@ redef class AMethPropdef
                #  TODO: Handle extern annotations
 
                var before = v.indent
-               var can_inline = v.can_inline(self)
                super
                if n_kwinit != null then v.visit n_kwinit
                if n_kwmeth != null then v.visit n_kwmeth
@@ -847,72 +971,15 @@ redef class AMethPropdef
 
                v.visit n_signature
 
-               if n_annotations != null then
-                       v.visit n_annotations
-               else
-                       v.adds
-               end
+               var annot_inline = visit_annotations(v, n_annotations)
 
                if n_extern_calls != null or n_extern_code_block != null then
-                       if n_annotations != null then v.adds
+                       v.adds
                        if n_extern_calls != null then v.visit n_extern_calls
                        if n_extern_code_block != null then v.visit n_extern_code_block
                end
 
-               var n_block = self.n_block
-
-               if n_block != null then
-                       while not v.current_token isa TKwdo do v.skip
-                       if n_annotations != null then
-                               if v.can_inline(n_annotations) then
-                                       v.adds
-                               else
-                                       v.addt
-                               end
-                       end
-                       v.consume "do"
-
-                       if can_inline then
-                               v.adds
-
-                               if n_block isa ABlockExpr then
-                                       if n_block.n_expr.is_empty then
-                                               v.visit n_block.n_kwend
-                                       else
-                                               v.visit n_block.n_expr.first
-                                               v.current_token = n_block.n_kwend
-                                               v.skip
-                                       end
-                               else
-                                       v.visit n_block
-                                       if v.current_token isa TKwend then v.skip
-                               end
-                       else
-                               v.finish_line
-                               v.addn
-                               v.indent += 1
-
-                               if n_block isa ABlockExpr then
-                                       n_block.force_block = true
-                                       v.visit n_block
-                                       v.catch_up n_block.n_kwend
-                               else
-                                       v.addt
-                                       v.visit n_block
-                                       v.addn
-                               end
-
-                               v.indent -= 1
-                               v.addt
-                               if n_block isa ABlockExpr then
-                                       v.visit n_block.n_kwend
-                               else
-                                       v.add "end"
-                               end
-                       end
-               end
-
-               v.finish_line
+               visit_block(v, n_block, annot_inline)
                v.addn
                assert v.indent == before
        end
@@ -934,7 +1001,7 @@ end
 redef class AMainMethPropdef
        redef fun accept_pretty_printer(v) do
                v.visit n_block
-               v.addn
+               if v.skip_empty then v.addn
        end
 end
 
@@ -980,8 +1047,9 @@ redef class AExternCalls
                        v.visit_list n_extern_calls
                else
                        v.addn
+                       v.indent += 1
                        v.addt
-                       v.addt
+                       v.indent -= 1
                        v.visit_list n_extern_calls
                end
 
@@ -1087,7 +1155,7 @@ redef class TExternCodeSegment
 
                                for line in lines do
                                        v.add line.r_trim
-                                       v.addn
+                                       v.forcen
                                end
 
                                v.addt
@@ -1177,11 +1245,18 @@ redef class AIfExpr
                        else if n_then == null then
                                v.add "end"
                        end
-
                        v.skip_to last_token.last_real_token_in_line
                else
                        v.finish_line
-                       v.addn
+                       if was_inline then
+                               v.forcen
+                       else if not v.skip_empty and n_then != null and
+                               n_then.was_inline and
+                               n_then.location.line_end == location.line_start then
+                               v.forcen # Xymus fucking syntax
+                       else
+                               v.addn
+                       end
                        v.indent += 1
 
                        if n_then != null then
@@ -1191,7 +1266,11 @@ redef class AIfExpr
                                else
                                        v.addt
                                        v.visit n_then
-                                       v.addn
+                                       if n_then.was_inline then
+                                               v.forcen
+                                       else
+                                               v.addn
+                                       end
                                end
                        end
 
@@ -1210,7 +1289,11 @@ redef class AIfExpr
                                        v.visit n_else
                                else
                                        v.finish_line
-                                       v.addn
+                                       if was_inline then
+                                               v.forcen
+                                       else
+                                               v.addn
+                                       end
                                        v.indent += 1
 
                                        if n_else isa ABlockExpr then
@@ -1219,7 +1302,11 @@ redef class AIfExpr
                                        else
                                                v.addt
                                                v.visit n_else
-                                               v.addn
+                                               if n_else.was_inline then
+                                                       v.forcen
+                                               else
+                                                       v.addn
+                                               end
                                        end
 
                                        if last_token isa TKwend then
@@ -1479,7 +1566,6 @@ redef class ACallExpr
                if not n_expr isa AImplicitSelfExpr and not can_inline then
                        v.addn
                        v.addt
-                       v.addt
                end
 
                v.visit n_id
@@ -1640,8 +1726,9 @@ redef class ANewExpr
 
                        if not can_inline then
                                v.addn
+                               v.indent += 1
                                v.addt
-                               v.addt
+                               v.indent -= 1
                        end
 
                        v.visit n_id
@@ -1749,16 +1836,15 @@ redef class AAssertExpr
                                v.visit n_else
                        else
                                v.addn
+                               v.indent += 1
 
                                if n_else isa ABlockExpr then
-                                       v.indent += 1
                                        n_else.force_block = true
                                        v.visit n_else
                                        v.indent -= 1
                                        v.addt
                                        v.visit n_else.n_kwend
                                else
-                                       v.indent += 1
                                        v.addt
                                        v.visit n_else
                                        v.addn
@@ -1897,8 +1983,9 @@ private class ABinOpHelper
                        v.visit bin_expr2
                else
                        v.addn
+                       v.indent += 1
                        v.addt
-                       v.addt
+                       v.indent -= 1
                        v.visit bin_expr2
                end
        end
@@ -2073,9 +2160,13 @@ end
 
 redef class AStringFormExpr
        redef fun accept_pretty_printer(v) do
-               var can_inline = v.can_inline(self)
-
-               if can_inline then
+               if not v.break_strings then
+                       # n_string.force_inline = true
+                       v.visit n_string
+                       return
+               end
+               if v.can_inline(self) then
+                       n_string.force_inline = true
                        v.visit n_string
                else
                        var text = n_string.text
@@ -2086,7 +2177,11 @@ redef class AStringFormExpr
 
                                if v.current_length >= v.max_size and i <= text.length - 3 then
                                        v.add "\" +"
-                                       v.addn
+                                       if was_inline then
+                                               v.forcen
+                                       else
+                                               v.addn
+                                       end
                                        v.indent += 1
                                        v.addt
                                        v.indent -= 1
@@ -2103,7 +2198,12 @@ end
 
 redef class ASuperstringExpr
        redef fun accept_pretty_printer(v) do
-               for n_expr in n_exprs do v.visit n_expr
+               for n_expr in n_exprs do
+                       if not v.break_strings then
+                               n_expr.force_inline = true
+                       end
+                       v.visit n_expr
+               end
        end
 
        redef fun must_be_inline do
index ae697d4..343edbd 100644 (file)
@@ -609,6 +609,10 @@ redef class AAttrPropdef
                var nblock = self.n_block
                if nblock != null then
                        v.visit_stmt(nblock)
+                       if not nblock.after_flow_context.is_unreachable then
+                               # We reach the end of the init without having a return, it is bad
+                               v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).")
+                       end
                end
        end
 end
@@ -975,7 +979,7 @@ redef class AForExpr
                        is_col = true
                end
 
-               if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type, objcla.mclass_type.as_nullable])) then
+               if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type.as_nullable, objcla.mclass_type.as_nullable])) then
                        # Map Iterator
                        var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla)
                        var variables = self.variables
index 3e99de2..021c2c7 100644 (file)
@@ -242,28 +242,30 @@ class ToolContext
        #
        # Stops execution and prints errors if the program isn't available or didn't end correctly
        fun exec_and_check(args: Array[String], error: String)
-        do
-                var prog = args.first
-                args.remove_at 0
-
-                # Is the wanted program available?
-                var proc_which = new IProcess.from_a("which", [prog])
-                proc_which.wait
-                var res = proc_which.status
-                if res != 0 then
-                        print "{error}: executable \"{prog}\" not found"
-                        exit 1
-                end
-
-                # Execute the wanted program
-                var proc = new Process.from_a(prog, args)
-                proc.wait
-                res = proc.status
-                if res != 0 then
-                        print "{error}: execution of \"{prog} {args.join(" ")}\" failed"
-                        exit 1
-                end
-        end
+       do
+               info("+ {args.join(" ")}", 2)
+
+               var prog = args.first
+               args.remove_at 0
+
+               # Is the wanted program available?
+               var proc_which = new IProcess.from_a("which", [prog])
+               proc_which.wait
+               var res = proc_which.status
+               if res != 0 then
+                       print "{error}: executable \"{prog}\" not found"
+                       exit 1
+               end
+
+               # Execute the wanted program
+               var proc = new Process.from_a(prog, args)
+               proc.wait
+               res = proc.status
+               if res != 0 then
+                       print "{error}: execution of \"{prog} {args.join(" ")}\" failed"
+                       exit 1
+               end
+       end
 
        # Global OptionContext
        var option_context = new OptionContext
index 6d2badc..fdafa3d 100644 (file)
@@ -24,7 +24,7 @@ class A
                2.output
                var res = a
                if res == 10 then res = 20
-               return res
+               return res #alt1# return #alt2#
        end
 end
 
index 69e3f0d..6eddab1 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#alt1# module error_annot_c_compiler_alt1 is c_compiler_option
-#alt2# module error_annot_c_compiler_alt2 is c_compiler_option(foo("llvm-config"))
-#alt3# module error_annot_c_compiler_alt3 is c_compiler_option(foo("llvm-config", "2nd arg"))
-#alt4# module error_annot_c_compiler_alt4 is c_linker_option
-#alt5# module error_annot_c_compiler_alt5 is c_compiler_option(exec("invalid-program"))
-#alt6# module error_annot_c_compiler_alt6 is c_compiler_option(exec)
+#alt1# module error_annot_c_compiler_alt1 is cflags
+#alt2# module error_annot_c_compiler_alt2 is cflags(foo("llvm-config"))
+#alt3# module error_annot_c_compiler_alt3 is cflags(foo("llvm-config", "2nd arg"))
+#alt4# module error_annot_c_compiler_alt4 is ldflags
+#alt5# module error_annot_c_compiler_alt5 is cflags(exec("invalid-program"))
+#alt6# module error_annot_c_compiler_alt6 is cflags(exec)
 
 fun foo `{ printf("nothing...\n"); `}
 
index 61b0157..77c26ea 100644 (file)
@@ -1,3 +1,34 @@
+--skip-empty test_pretty/test_mod1.nit
+--skip-empty test_pretty/test_mod2.nit
+--skip-empty test_pretty/test_mod3.nit
+--skip-empty test_pretty/test_class1.nit
+--skip-empty test_pretty/test_class2.nit
+--skip-empty test_pretty/test_class3.nit
+--skip-empty test_pretty/test_prop1.nit
+--skip-empty test_pretty/test_prop2.nit
+--skip-empty test_pretty/test_prop3.nit
+--skip-empty test_pretty/test_loop1.nit
+--skip-empty test_pretty/test_loop2.nit
+--skip-empty test_pretty/test_loop3.nit
+--skip-empty test_pretty/test_call1.nit
+--skip-empty test_pretty/test_call2.nit
+--skip-empty test_pretty/test_if1.nit
+--skip-empty test_pretty/test_if2.nit
+--skip-empty test_pretty/test_if3.nit
+--skip-empty test_pretty/test_op1.nit
+--skip-empty test_pretty/test_op2.nit
+--skip-empty test_pretty/test_op3.nit
+--skip-empty test_pretty/test_extern1.nit
+--skip-empty test_pretty/test_attr1.nit
+--skip-empty test_pretty/test_attr2.nit
+--skip-empty test_pretty/test_comments1.nit
+--skip-empty test_pretty/test_indent1.nit
+--skip-empty test_pretty/test_prims1.nit
+--skip-empty test_pretty/test_annot1.nit
+--skip-empty --break-strings test_pretty/test_prop1.nit
+--skip-empty --break-strings test_pretty/test_indent1.nit
+--skip-empty --inline-do test_pretty/test_prop1.nit
+--skip-empty --inline-do test_pretty/test_indent1.nit
 test_pretty/test_mod1.nit
 test_pretty/test_mod2.nit
 test_pretty/test_mod3.nit
@@ -25,3 +56,7 @@ test_pretty/test_comments1.nit
 test_pretty/test_indent1.nit
 test_pretty/test_prims1.nit
 test_pretty/test_annot1.nit
+--break-strings test_pretty/test_prop1.nit
+--break-strings test_pretty/test_indent1.nit
+--inline-do test_pretty/test_prop1.nit
+--inline-do test_pretty/test_indent1.nit
diff --git a/tests/sav/base_attr_init_val_block_alt1.res b/tests/sav/base_attr_init_val_block_alt1.res
new file mode 100644 (file)
index 0000000..e50539b
--- /dev/null
@@ -0,0 +1 @@
+alt/base_attr_init_val_block_alt1.nit:27,3--8: Error: Return without value in a function.
diff --git a/tests/sav/base_attr_init_val_block_alt2.res b/tests/sav/base_attr_init_val_block_alt2.res
new file mode 100644 (file)
index 0000000..8545480
--- /dev/null
@@ -0,0 +1 @@
+alt/base_attr_init_val_block_alt2.nit:23,6: Control error: Reached end of block (a 'return' with a value was expected).
diff --git a/tests/sav/doors_with_classes.res b/tests/sav/doors_with_classes.res
new file mode 100644 (file)
index 0000000..23c9daf
--- /dev/null
@@ -0,0 +1,100 @@
+Door 1: Open
+Door 2: Closed
+Door 3: Closed
+Door 4: Open
+Door 5: Closed
+Door 6: Closed
+Door 7: Closed
+Door 8: Closed
+Door 9: Open
+Door 10: Closed
+Door 11: Closed
+Door 12: Closed
+Door 13: Closed
+Door 14: Closed
+Door 15: Closed
+Door 16: Open
+Door 17: Closed
+Door 18: Closed
+Door 19: Closed
+Door 20: Closed
+Door 21: Closed
+Door 22: Closed
+Door 23: Closed
+Door 24: Closed
+Door 25: Open
+Door 26: Closed
+Door 27: Closed
+Door 28: Closed
+Door 29: Closed
+Door 30: Closed
+Door 31: Closed
+Door 32: Closed
+Door 33: Closed
+Door 34: Closed
+Door 35: Closed
+Door 36: Open
+Door 37: Closed
+Door 38: Closed
+Door 39: Closed
+Door 40: Closed
+Door 41: Closed
+Door 42: Closed
+Door 43: Closed
+Door 44: Closed
+Door 45: Closed
+Door 46: Closed
+Door 47: Closed
+Door 48: Closed
+Door 49: Open
+Door 50: Closed
+Door 51: Closed
+Door 52: Closed
+Door 53: Closed
+Door 54: Closed
+Door 55: Closed
+Door 56: Closed
+Door 57: Closed
+Door 58: Closed
+Door 59: Closed
+Door 60: Closed
+Door 61: Closed
+Door 62: Closed
+Door 63: Closed
+Door 64: Open
+Door 65: Closed
+Door 66: Closed
+Door 67: Closed
+Door 68: Closed
+Door 69: Closed
+Door 70: Closed
+Door 71: Closed
+Door 72: Closed
+Door 73: Closed
+Door 74: Closed
+Door 75: Closed
+Door 76: Closed
+Door 77: Closed
+Door 78: Closed
+Door 79: Closed
+Door 80: Closed
+Door 81: Open
+Door 82: Closed
+Door 83: Closed
+Door 84: Closed
+Door 85: Closed
+Door 86: Closed
+Door 87: Closed
+Door 88: Closed
+Door 89: Closed
+Door 90: Closed
+Door 91: Closed
+Door 92: Closed
+Door 93: Closed
+Door 94: Closed
+Door 95: Closed
+Door 96: Closed
+Door 97: Closed
+Door 98: Closed
+Door 99: Closed
+Door 100: Open
index 981a90b..23b9437 100644 (file)
@@ -1 +1 @@
-alt/error_annot_c_compiler_alt1.nit:17,39--55: Syntax error: "c_compiler_option" expects at least one argument.
+alt/error_annot_c_compiler_alt1.nit:17,39--44: Syntax error: "cflags" expects at least one argument.
index b460682..2459cd4 100644 (file)
@@ -1 +1 @@
-alt/error_annot_c_compiler_alt2.nit:18,39--75: Syntax error: "c_compiler_option" accepts only calls to `exec` with the command as arguments.
+alt/error_annot_c_compiler_alt2.nit:18,39--64: Syntax error: "cflags" accepts only calls to `exec` with the command as arguments.
index 8142bf8..a584f16 100644 (file)
@@ -1 +1 @@
-alt/error_annot_c_compiler_alt3.nit:19,39--86: Syntax error: "c_compiler_option" accepts only calls to `exec` with the command as arguments.
+alt/error_annot_c_compiler_alt3.nit:19,39--75: Syntax error: "cflags" accepts only calls to `exec` with the command as arguments.
index d753ea9..f34ea44 100644 (file)
@@ -1 +1 @@
-alt/error_annot_c_compiler_alt4.nit:20,39--53: Syntax error: "c_linker_option" expects at least one argument.
+alt/error_annot_c_compiler_alt4.nit:20,39--45: Syntax error: "ldflags" expects at least one argument.
index 2251e01..133d200 100644 (file)
@@ -1 +1 @@
-alt/error_annot_c_compiler_alt5.nit:21,57--79: Annotation error: Something went wrong executing the argument of annotation "c_compiler_option", make sure the command is valid.
+alt/error_annot_c_compiler_alt5.nit:21,46--68: Annotation error: Something went wrong executing the argument of annotation "cflags", make sure the command is valid.
index 694b887..cc29f8a 100644 (file)
@@ -1 +1 @@
-alt/error_annot_c_compiler_alt6.nit:22,39--61: Syntax error: "c_compiler_option" accepts only calls to `exec` with the command as arguments.
+alt/error_annot_c_compiler_alt6.nit:22,39--50: Syntax error: "cflags" accepts only calls to `exec` with the command as arguments.
diff --git a/tests/sav/fixme/nitpretty_args22.res b/tests/sav/fixme/nitpretty_args22.res
deleted file mode 100644 (file)
index 4ad3dc3..0000000
+++ /dev/null
@@ -1 +0,0 @@
-UNDEFINED
diff --git a/tests/sav/fixme/nitpretty_args48.res b/tests/sav/fixme/nitpretty_args48.res
new file mode 100644 (file)
index 0000000..c3a432c
--- /dev/null
@@ -0,0 +1,61 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 1
+var b = 2
+
+# 0
+if a == b then # 1
+       # 2
+else # 3
+       # 4
+end # 5
+
+if a == b then print a # printing a
+
+if a == b then
+       print a # printing a
+end
+
+if a == b then print a
+ # end
+
+if a == b then a = b
+
+if a == b then end
+
+if a == b then end
+
+if a != b then a = b
+
+
+if a > b then
+       a = b
+else
+       a = b
+end
+
+if a < b then
+       a = b
+else if a == b then
+       a = b
+end
+
+if a < b then
+       a = b
+else if a == b then
+       a = b
+else
+       a = b
+end
diff --git a/tests/sav/fixme/nitpretty_args58.res b/tests/sav/fixme/nitpretty_args58.res
new file mode 100644 (file)
index 0000000..2e6ac9a
--- /dev/null
@@ -0,0 +1,51 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_annot1 is platform("android")
+
+class A
+       fun goo is intern
+
+       # test
+       fun foo is a, b
+       fun bar is a, b do print "1"
+       fun baz is
+               a
+               bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+       do print "2"
+end
+
+class B
+       fun foo is a, b
+
+
+       fun bar is a, b
+ do print "1"
+
+       fun baz is a, b
+ do
+               bar
+               print "2"
+       end
+
+       fun gaz is
+               a
+               bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+
+       do
+               bar
+               print "2"
+       end
+
+end
index 9b58418..7a8fe04 100644 (file)
@@ -78,8 +78,8 @@ extern class TimeT `{time_t`}
        # Difference in secondes from start (self if the end time)
        fun difftime(start: TimeT): Float `{ return difftime(recv, start); `}
 
-       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is extern import
-               Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`}
+       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is
+               extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`}
 end
 
 fun address_is_null: Bool is extern "address_is_null"
index 6638706..e8ba342 100644 (file)
@@ -15,7 +15,8 @@
 class Foo
        fun bar: Bool do return true
 
-       fun foo(other: Foo): Foo do
+       fun foo(other: Foo): Foo
+       do
                if other.bar then
                        return other
                else
@@ -34,7 +35,8 @@ class Foo
                return nb
        end
 
-       fun gaz: Int do
+       fun gaz: Int
+       do
                if bar then # 3
                        return 1
                else
@@ -67,7 +69,9 @@ class Test[E]
                end
        end
 
-       fun save_those_nodes(nodes: Collection[Object]) do for node in nodes do count(node)
+       fun save_those_nodes(nodes: Collection[Object]) do
+               for node in nodes do count(node)
+       end
 end
 
 fun foo do
@@ -78,28 +82,12 @@ fun foo do
        end
 end
 
-print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidun" +
-       "t sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit a" +
-       "met lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus" +
-       " eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pell" +
-       "entesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae" +
-       " consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae" +
-       " lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas" +
-       " turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed phar" +
-       "etra lacus."
+print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus."
 
 var lorem = "lorem"
 var ipsum = "ipsum" # for fun
 
-print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, con" +
-       "sectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius a" +
-       "t non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisi" +
-       "s neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}" +
-       ". Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} pla" +
-       "cerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus pl" +
-       "acerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum " +
-       "augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pell" +
-       "entesque vitae arcu justo. Aliquam sed pharetra lacus." # ending
+print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending
 
 var title = "title"
 var links = new Array[String] # why not?
index 0406b29..422cc2a 100644 (file)
 module test_annot1 is platform("android")
 
 class A
+       fun goo is intern
+
+       # test
        fun foo is a, b
+
        fun bar is a, b do print "1"
 
        fun baz is
                a
                bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-       do
-               print "2"
-       end
+       do print "2"
 end
 
 class B
diff --git a/tests/sav/nitpretty_args28.res b/tests/sav/nitpretty_args28.res
new file mode 100644 (file)
index 0000000..1944090
--- /dev/null
@@ -0,0 +1,51 @@
+# 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.
+
+# comment 1
+class A
+       type FOO: Discrete
+       private var foo: FOO # comment
+
+       # comment 2
+       var bar: Int = 10
+end
+
+class B
+       super A
+
+       redef type FOO: Int
+
+       # comment 3
+       redef fun foo do return bar # comment
+
+       redef fun bar
+       do
+               return 10 # comment 4
+       end
+
+       fun baz do return # comment 5
+       protected fun baz2 do end
+
+       fun other: String do
+               return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaa"
+       end
+
+       fun foo1(arr: Array[String], len: Int, ind: Int): String
+       do
+               return "Hello World!"
+       end
+end
+
+# end
diff --git a/tests/sav/nitpretty_args29.res b/tests/sav/nitpretty_args29.res
new file mode 100644 (file)
index 0000000..4d75c17
--- /dev/null
@@ -0,0 +1,127 @@
+# 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.
+
+class Foo
+       fun bar: Bool do return true
+
+       fun foo(other: Foo): Foo
+       do
+               if other.bar then
+                       return other
+               else
+                       return self
+               end
+       end
+
+       fun baz: Int do
+               var nb = 0
+
+               while nb < 10 do
+                       print nb
+                       nb += 1
+               end # 1
+
+               return nb
+       end
+
+       fun gaz: Int
+       do
+               if bar then # 3
+                       return 1
+               else
+                       return -1 # 4
+               end
+       end
+end
+
+class Test[E]
+       var heap: ArrayHeap[E]
+       init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator)
+
+       init from(comparator: Comparator[E], items: Collection[E]) do
+               heap = new ArrayHeap[E].from(comparator, items.to_a)
+       end
+
+       fun count(k: E): Int do
+               if heap.has(k) then
+                       return 1
+               else
+                       return 0
+               end
+       end
+
+       fun node_at_idx(i: Int, k: E) do
+               while heap != null do
+                       if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet
+                               break
+                       end
+               end
+       end
+
+       fun save_those_nodes(nodes: Collection[Object]) do
+               for node in nodes do count(node)
+       end
+end
+
+fun foo do
+       if last_slash > 0 then
+               return substring(last_slash + 1, length)
+       else
+               return null
+       end
+end
+
+print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidun" +
+       "t sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit a" +
+       "met lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus" +
+       " eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pell" +
+       "entesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae" +
+       " consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae" +
+       " lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas" +
+       " turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed phar" +
+       "etra lacus."
+
+var lorem = "lorem"
+var ipsum = "ipsum" # for fun
+
+print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, con" +
+       "sectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius a" +
+       "t non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisi" +
+       "s neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}" +
+       ". Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} pla" +
+       "cerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus pl" +
+       "acerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum " +
+       "augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pell" +
+       "entesque vitae arcu justo. Aliquam sed pharetra lacus." # ending
+
+var title = "title"
+var links = new Array[String] # why not?
+
+var body = """
+<!DOCTYPE html>
+<head>
+       <meta charset="utf-8">
+       <meta http-equiv="X-UA-Compatible" content="IE=edge">
+       <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <title>{{{title}}}</title>
+</head>
+<body>
+       <div class="container">
+               <h1>{{{title}}}</h1>
+               <ul>
+                       <li>{{{links.join("</li>\n\t\t\t<li>")}}}</li>
+               </ul>
+       </div>
+</body>
+</html>"""
diff --git a/tests/sav/nitpretty_args30.res b/tests/sav/nitpretty_args30.res
new file mode 100644 (file)
index 0000000..57f7966
--- /dev/null
@@ -0,0 +1,48 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# comment 1
+class A
+       type FOO: Discrete
+       private var foo: FOO # comment
+
+       # comment 2
+       var bar: Int = 10
+end
+
+class B
+       super A
+
+       redef type FOO: Int
+
+       # comment 3
+       redef fun foo do return bar # comment
+
+       redef fun bar do
+               return 10 # comment 4
+       end
+
+       fun baz do return # comment 5
+       protected fun baz2 do end
+
+       fun other: String do
+               return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       end
+
+       fun foo1(arr: Array[String], len: Int, ind: Int): String do
+               return "Hello World!"
+       end
+end
+
+# end
diff --git a/tests/sav/nitpretty_args31.res b/tests/sav/nitpretty_args31.res
new file mode 100644 (file)
index 0000000..99d53dd
--- /dev/null
@@ -0,0 +1,109 @@
+# 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.
+
+class Foo
+       fun bar: Bool do return true
+
+       fun foo(other: Foo): Foo do
+               if other.bar then
+                       return other
+               else
+                       return self
+               end
+       end
+
+       fun baz: Int do
+               var nb = 0
+
+               while nb < 10 do
+                       print nb
+                       nb += 1
+               end # 1
+
+               return nb
+       end
+
+       fun gaz: Int do
+               if bar then # 3
+                       return 1
+               else
+                       return -1 # 4
+               end
+       end
+end
+
+class Test[E]
+       var heap: ArrayHeap[E]
+       init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator)
+
+       init from(comparator: Comparator[E], items: Collection[E]) do
+               heap = new ArrayHeap[E].from(comparator, items.to_a)
+       end
+
+       fun count(k: E): Int do
+               if heap.has(k) then
+                       return 1
+               else
+                       return 0
+               end
+       end
+
+       fun node_at_idx(i: Int, k: E) do
+               while heap != null do
+                       if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet
+                               break
+                       end
+               end
+       end
+
+       fun save_those_nodes(nodes: Collection[Object]) do
+               for node in nodes do count(node)
+       end
+end
+
+fun foo do
+       if last_slash > 0 then
+               return substring(last_slash + 1, length)
+       else
+               return null
+       end
+end
+
+print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus."
+
+var lorem = "lorem"
+var ipsum = "ipsum" # for fun
+
+print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending
+
+var title = "title"
+var links = new Array[String] # why not?
+
+var body = """
+<!DOCTYPE html>
+<head>
+       <meta charset="utf-8">
+       <meta http-equiv="X-UA-Compatible" content="IE=edge">
+       <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <title>{{{title}}}</title>
+</head>
+<body>
+       <div class="container">
+               <h1>{{{title}}}</h1>
+               <ul>
+                       <li>{{{links.join("</li>\n\t\t\t<li>")}}}</li>
+               </ul>
+       </div>
+</body>
+</html>"""
diff --git a/tests/sav/nitpretty_args32.res b/tests/sav/nitpretty_args32.res
new file mode 100644 (file)
index 0000000..f55be8f
--- /dev/null
@@ -0,0 +1,18 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+
+# An empty module
+
diff --git a/tests/sav/nitpretty_args33.res b/tests/sav/nitpretty_args33.res
new file mode 100644 (file)
index 0000000..76149a7
--- /dev/null
@@ -0,0 +1,24 @@
+# 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.
+
+# Testing only imports
+
+# Module comment
+module test_mod2 # second comment
+
+import standard::kernel
+#import standard::string
+
+import template # no need for string
+# import standard
\ No newline at end of file
diff --git a/tests/sav/nitpretty_args34.res b/tests/sav/nitpretty_args34.res
new file mode 100644 (file)
index 0000000..6ee57a1
--- /dev/null
@@ -0,0 +1,25 @@
+# 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.
+
+# A simple module
+module test_mod3
+
+# before
+print "Hello World" # comment
+# after
+
+# end
+
+
+
diff --git a/tests/sav/nitpretty_args35.res b/tests/sav/nitpretty_args35.res
new file mode 100644 (file)
index 0000000..ed30bc2
--- /dev/null
@@ -0,0 +1,24 @@
+# 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.
+
+# comment 1
+interface A end
+
+abstract class B # comment 2
+end
+
+class C end # comment 3
+
+enum D end # comment 4
+
diff --git a/tests/sav/nitpretty_args36.res b/tests/sav/nitpretty_args36.res
new file mode 100644 (file)
index 0000000..2a61cf1
--- /dev/null
@@ -0,0 +1,25 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_class2
+
+
+# comment
+class A end
+
+class B[T] # comment
+end
+
+private class C[U, V: B[A]] end # comment
+
diff --git a/tests/sav/nitpretty_args37.res b/tests/sav/nitpretty_args37.res
new file mode 100644 (file)
index 0000000..feedd55
--- /dev/null
@@ -0,0 +1,39 @@
+# 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.
+
+# comment
+class A end
+
+class B[T] # comment
+       # comment
+       super A # comment
+
+
+       super C[A, B[A]]
+       # comment
+end
+
+class C[U, V: B[A]] end # comment
+
+class D super A end # comment
+
+class E
+
+
+
+       super A # comment
+end
+
+# end
+
diff --git a/tests/sav/nitpretty_args38.res b/tests/sav/nitpretty_args38.res
new file mode 100644 (file)
index 0000000..31e90fc
--- /dev/null
@@ -0,0 +1,46 @@
+# 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.
+
+# comment 1
+class A
+       type FOO: Discrete
+       private var foo: FOO # comment
+       # comment 2
+       var bar: Int = 10
+end
+
+class B
+       super A
+
+       redef type FOO: Int
+       # comment 3
+       redef fun foo do return bar # comment
+       redef fun bar
+       do
+               return 10 # comment 4
+       end
+       fun baz do return # comment 5
+       protected fun baz2 do end
+       fun other: String do
+               return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       end
+
+       fun foo1(arr: Array[String], len: Int, ind: Int): String
+       do
+               return "Hello World!"
+       end
+end
+
+# end
+
diff --git a/tests/sav/nitpretty_args39.res b/tests/sav/nitpretty_args39.res
new file mode 100644 (file)
index 0000000..e48e1da
--- /dev/null
@@ -0,0 +1,32 @@
+# 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.
+
+class A
+       fun foo(a, b: Int): Bool do return true # 1
+
+       fun foo2(a, b: Int): Bool do return true # 2
+
+       fun foo3(a, b: Int): Bool do return true
+
+       fun foo4(a, b: Int): Bool do
+               var res = true # 3
+               return res # 4
+       end
+
+       fun foo5 do end # 5
+       # fun foo6 do end
+end
+
+# end
+
diff --git a/tests/sav/nitpretty_args40.res b/tests/sav/nitpretty_args40.res
new file mode 100644 (file)
index 0000000..59e2e25
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class A
+       fun foo(aaaaaaaaaaaaaa,
+               bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: Int): Bool do return true # comment
+
+       fun foo2(a, b: Int): Bool do return true # comment
+
+       fun foo3(a, b: Int): Bool do # comment
+               return true # comment
+       end # comment
+
+       fun foo4(a, b: Int): Bool do # comment
+               var res = true # comment
+               return res # comment
+       end # comment
+
+       fun foo5 do end # comment
+
+       fun foo6(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
+               aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Int) do print 1
+
+end # comment
\ No newline at end of file
diff --git a/tests/sav/nitpretty_args41.res b/tests/sav/nitpretty_args41.res
new file mode 100644 (file)
index 0000000..1be308b
--- /dev/null
@@ -0,0 +1,34 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 1
+var b = 2
+
+while a != b do # comment 1
+       # comment 2
+       var tmp = a
+       a = b
+       b = tmp
+       # comment 3
+end
+
+# comment 4
+while a != b do a = b # comment 5
+
+while a != b do
+       # comment 6
+end # comment 7
+
+# end
+
diff --git a/tests/sav/nitpretty_args42.res b/tests/sav/nitpretty_args42.res
new file mode 100644 (file)
index 0000000..6b49ffa
--- /dev/null
@@ -0,0 +1,47 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 0
+var b = 2
+
+do # comment 1
+       # comment 2
+       var tmp = a
+       a = b
+       b = tmp
+       # comment 3
+end
+
+# comment 4
+do a = b # comment 5
+
+do
+       # comment 6
+end
+
+if a > b then loop print a # test
+
+if a > b then loop print a
+
+
+if a > b then loop print a
+
+
+if a > b then
+       loop
+               # comment 7
+               print a
+       end
+end
+
diff --git a/tests/sav/nitpretty_args43.res b/tests/sav/nitpretty_args43.res
new file mode 100644 (file)
index 0000000..0135aa1
--- /dev/null
@@ -0,0 +1,27 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 0
+
+for i in [1, 2, 3] do # comment 1
+       # comment 2
+       a += i
+end
+
+# comment 4
+for i in [1..3] do a += i # comment 5
+
+for i in [1..3[ do
+       # comment 6
+end
diff --git a/tests/sav/nitpretty_args44.res b/tests/sav/nitpretty_args44.res
new file mode 100644 (file)
index 0000000..2cd014e
--- /dev/null
@@ -0,0 +1,36 @@
+# 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.
+
+class A
+       fun foo do end
+       fun bar(a: Int): Int do return 1
+       fun baz(a, b: Int) do end
+       fun gaz(a: Int, b: Float...) do end
+end
+
+fun top1 do end
+fun top2(a: Int) do end
+
+# comment 1
+var a = new A # comment 2
+a.foo # comment 3
+a.bar 1 # comment 4
+a.baz(1, 2) # comment 5
+top1 # comment 6
+top2 10 # comment 7
+
+print 10 # comment 8
+
+var b = a.bar(1)
+
diff --git a/tests/sav/nitpretty_args45.res b/tests/sav/nitpretty_args45.res
new file mode 100644 (file)
index 0000000..789abdd
--- /dev/null
@@ -0,0 +1,39 @@
+# 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.
+
+class A
+       var attr: Int
+       fun foo1=(i: Int) do end
+       fun foo2=(i, j: Int) do end
+       fun [](a: Int): Int is abstract
+       fun []=(a, b: Int) do end
+end
+
+class B
+       fun [](a, b: Int): Int is abstract
+       fun []=(a, b, c: Int) do end
+end
+
+# comment 1
+var a = new A(10) # comment 2
+
+a.foo1 = 10 # comment 3
+a.foo2(1) = 10 # comment 4
+print a[1] # comment 5
+a[1] = 2 # comment 6
+a[2] += 3 # comment 7
+
+var b = new B
+print b[1, 2]
+b[1, 2] = 10
diff --git a/tests/sav/nitpretty_args46.res b/tests/sav/nitpretty_args46.res
new file mode 100644 (file)
index 0000000..60bf5ef
--- /dev/null
@@ -0,0 +1,50 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 1
+var b = 2
+
+if a == b then a = b
+
+if a != b then
+       a = b
+       a = b
+end
+
+if a > b then
+       b = a
+       a = b
+else
+       a = b
+       a = b
+end
+
+if a < b then
+       a = b
+       a = b
+else if a == b then
+       b = a
+       a = b
+end
+
+if a < b then
+       a = b
+       a = b
+else if a == b then
+       b = b
+       a = b
+else
+       a = b
+       a = b
+end
diff --git a/tests/sav/nitpretty_args47.res b/tests/sav/nitpretty_args47.res
new file mode 100644 (file)
index 0000000..12d8db5
--- /dev/null
@@ -0,0 +1,81 @@
+# 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.
+
+# comment
+var a = 1 # comment
+# comment
+var b = 2 # comment
+
+# comment
+if a == b then a = b # comment
+
+# comment
+if a != b then # comment
+       # comment
+       a = b # comment
+       # comment
+       a = b # comment
+       # comment
+end # comment
+
+# comment
+if a > b then # comment
+       # comment
+       b = a # comment
+       # comment
+       a = b # comment
+       # comment
+else # comment
+       # comment
+       a = b # comment
+       # comment
+       a = b # comment
+       # comment
+end # comment
+
+# comment
+if a < b then # comment
+       # comment
+       a = b # comment
+       # comment
+       a = b # comment
+       # comment
+else if a == b then # comment
+       # comment
+       b = a # comment
+       # comment
+       a = b # comment
+       # comment
+end # comment
+
+# comment
+if a < b then # comment
+       # comment
+       a = b # comment
+       # comment
+       a = b # comment
+       # comment
+else if a == b then # comment
+       # comment
+       b = b # comment
+       # comment
+       a = b # comment
+       # comment
+else # comment
+       # comment
+       a = b # comment
+       # comment
+       a = b # comment
+       # comment
+end # comment
\ No newline at end of file
diff --git a/tests/sav/nitpretty_args48.res b/tests/sav/nitpretty_args48.res
new file mode 100644 (file)
index 0000000..bfde8c2
--- /dev/null
@@ -0,0 +1,59 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 1
+var b = 2
+
+# 0
+if a == b then # 1
+       # 2
+else # 3
+       # 4
+end # 5
+
+if a == b then print a # printing a
+
+if a == b then
+       print a # printing a
+end
+
+if a == b then print a # end
+
+if a == b then a = b
+
+if a == b then end
+
+if a == b then end
+
+if a != b then a = b
+
+if a > b then
+       a = b
+else
+       a = b
+end
+
+if a < b then
+       a = b
+else if a == b then
+       a = b
+end
+
+if a < b then
+       a = b
+else if a == b then
+       a = b
+else
+       a = b
+end
diff --git a/tests/sav/nitpretty_args49.res b/tests/sav/nitpretty_args49.res
new file mode 100644 (file)
index 0000000..4fec81e
--- /dev/null
@@ -0,0 +1,21 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a: nullable Int = 1
+var b: nullable Int = a.as(Int)
+var c: nullable Int = a.as(not null)
+
+assert c isa Int
+assert test1: c isa Int
+assert test2: c isa Int else abort
diff --git a/tests/sav/nitpretty_args50.res b/tests/sav/nitpretty_args50.res
new file mode 100644 (file)
index 0000000..c428dcb
--- /dev/null
@@ -0,0 +1,31 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 1
+var b = 2
+
+assert a == 2
+assert not a < 2 # comment 1
+assert a > 2 and b >= 2
+assert b != 2 or a <= 2
+assert b != null # comment 2
+
+# comment 3
+print a + b
+print a - b # comment 4
+print a * b
+print a / b
+print a % b
+
+print -a # comment 5
\ No newline at end of file
diff --git a/tests/sav/nitpretty_args51.res b/tests/sav/nitpretty_args51.res
new file mode 100644 (file)
index 0000000..90425ff
--- /dev/null
@@ -0,0 +1,27 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var a = 1
+var b = 2
+
+assert not a < 2 and (a == b or a > b) # comment 1
+assert not a < 2 and (a == b or ((a > b) or a <= b))
+assert (a > 2 and b >= 2)
+assert (b >= 2)
+
+# comment 3
+var c = a + (b - a)
+var d = (a - b) + c # comment 4
+var e = (-a) # comment 5
+var f = -(a - c)
similarity index 93%
rename from tests/sav/fixme/nitpretty_args21.res
rename to tests/sav/nitpretty_args52.res
index f4a3324..415c33f 100644 (file)
@@ -34,9 +34,8 @@ fun errno: Int is extern `{
        return errno;
 `}
 
-fun errnoooooooooooooooooooooooooooooooooooooooooooooooooooooooooo: Int is extern `{
-        return errno;
-`}
+fun errnoooooooooooooooooooooooooooooooooooooooooooooooooooooooooo: Int is
+       extern `{ return errno; `}
 
 private class A
        var my_attr = 1234
@@ -69,6 +68,7 @@ end
 extern class TimeT `{time_t`}
        new `{ return time(NULL); `}
        new from_i(i: Int) `{ return i; `}
+
        fun update `{ time(&recv); `}
 
        fun ctime: String import NativeString.to_s_with_copy `{
@@ -78,10 +78,11 @@ extern class TimeT `{time_t`}
        # Difference in secondes from start (self if the end time)
        fun difftime(start: TimeT): Float `{ return difftime(recv, start); `}
 
-       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is import
-               Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`}
+       private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is
+               extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`}
 end
 
 fun address_is_null: Bool is extern "address_is_null"
 
 fun free `{ free(recv); `}
+
diff --git a/tests/sav/nitpretty_args53.res b/tests/sav/nitpretty_args53.res
new file mode 100644 (file)
index 0000000..8152504
--- /dev/null
@@ -0,0 +1,35 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+class A
+       var a: Int # comment
+       private var b: nullable Int # happy
+       protected var c = 10 # ending
+       var d: Int = 10
+
+
+
+
+
+       # Test test...
+       var e: Int is writable
+       var f: Int is protected writable
+       # Adoc
+       var k: Int = 10 is protected writable
+
+
+
+       # more comments
+end # end
+
diff --git a/tests/sav/nitpretty_args54.res b/tests/sav/nitpretty_args54.res
new file mode 100644 (file)
index 0000000..08a4d49
--- /dev/null
@@ -0,0 +1,24 @@
+# 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.
+
+class Foo
+       var a: Int
+       private var b: nullable Int
+       protected var c = 10
+       var d: Int = 10
+end
+
+var foo = new Foo(1, 2)
+print foo._a
+foo._a = 10
diff --git a/tests/sav/nitpretty_args55.res b/tests/sav/nitpretty_args55.res
new file mode 100644 (file)
index 0000000..2096847
--- /dev/null
@@ -0,0 +1,98 @@
+# 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.
+
+
+# toplevel comment
+
+
+
+
+
+
+# block
+# block
+# block
+
+
+
+
+
+# Adoc1
+class A # ending comments
+
+       super Object
+       # super Int
+
+
+       super String
+       # super Truc
+
+
+
+       # inclass comments
+       # comm
+       #    ented
+       #      blocks
+
+
+
+       # Adoc2
+       fun foo do
+
+               # comment
+
+
+
+               var truc
+
+               # comment
+               # comment
+
+
+
+               # comment
+
+
+               var chose
+
+               # comment
+       end
+
+       # comm
+       #    ented
+       #      blocks
+
+
+       fun bar do end
+
+
+       fun baz do end
+       # comment before end
+
+end # ending comments
+
+# comm
+#    ented
+#      blocks
+
+abstract class B # comment
+end
+
+abstract class C end
+
+abstract class B # comment 2
+
+end
+
+abstract class C end
diff --git a/tests/sav/nitpretty_args56.res b/tests/sav/nitpretty_args56.res
new file mode 100644 (file)
index 0000000..adabdcf
--- /dev/null
@@ -0,0 +1,111 @@
+# 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.
+
+class Foo
+       fun bar: Bool do return true
+
+       fun foo(other: Foo): Foo
+       do
+               if other.bar then
+                       return other
+               else
+                       return self
+               end
+       end
+
+       fun baz: Int do
+               var nb = 0
+               while nb < 10 do
+                       print nb
+                       nb += 1
+               end # 1
+               return nb
+       end
+
+       fun gaz: Int
+       do
+               if bar then # 3
+                       return 1
+               else
+                       return -1 # 4
+               end
+       end
+end
+
+class Test[E]
+       var heap: ArrayHeap[E]
+
+       init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator)
+
+       init from(comparator: Comparator[E], items: Collection[E]) do
+               heap = new ArrayHeap[E].from(comparator, items.to_a)
+       end
+
+       fun count(k: E): Int do
+               if heap.has(k) then
+                       return 1
+               else
+                       return 0
+               end
+       end
+
+       fun node_at_idx(i: Int, k: E) do
+               while heap != null do
+                       if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet
+                               break
+                       end
+               end
+       end
+
+       fun save_those_nodes(nodes: Collection[Object]) do
+               for node in nodes do count(node)
+       end
+end
+
+fun foo do
+       if last_slash > 0 then
+               return substring(last_slash + 1, length)
+       else
+               return null
+       end
+end
+
+print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus."
+
+var lorem = "lorem"
+var ipsum = "ipsum" # for fun
+
+print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending
+
+var title = "title"
+var links = new Array[String] # why not?
+
+var body = """
+<!DOCTYPE html>
+<head>
+       <meta charset="utf-8">
+       <meta http-equiv="X-UA-Compatible" content="IE=edge">
+       <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <title>{{{title}}}</title>
+</head>
+<body>
+       <div class="container">
+               <h1>{{{title}}}</h1>
+               <ul>
+                       <li>{{{links.join("</li>\n\t\t\t<li>")}}}</li>
+               </ul>
+       </div>
+</body>
+</html>"""
+
diff --git a/tests/sav/nitpretty_args57.res b/tests/sav/nitpretty_args57.res
new file mode 100644 (file)
index 0000000..e385838
--- /dev/null
@@ -0,0 +1,48 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# prims
+
+var a = true
+var b = false
+
+var c = 10
+var d = -10
+var e = 1.12
+
+var f = -1.12
+var n = 'a'
+var o = null
+var p = 0x12345678
+
+# strings
+
+var g = "test"
+var h1 = "Hello {g}"
+var h2 = "Hello \"{g}\" Hello"
+var h3 = "Hello {g}"
+var m = """
+bla
+       bla
+
+bla"""
+
+# arrays
+
+var j = [1, 2, 3]
+var k = [1..2[
+var l = [1..2]
+
+
+
diff --git a/tests/sav/nitpretty_args58.res b/tests/sav/nitpretty_args58.res
new file mode 100644 (file)
index 0000000..edc3f3f
--- /dev/null
@@ -0,0 +1,48 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module test_annot1 is platform("android")
+
+class A
+       fun goo is intern
+
+       # test
+       fun foo is a, b
+       fun bar is a, b do print "1"
+       fun baz is
+               a
+               bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb do print "2"
+end
+
+class B
+       fun foo is a, b
+
+
+       fun bar is a, b do print "1"
+
+       fun baz is a, b
+       do
+               bar
+               print "2"
+       end
+
+       fun gaz is
+               a
+               bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+       do
+               bar
+               print "2"
+       end
+
+end
diff --git a/tests/sav/nitpretty_args59.res b/tests/sav/nitpretty_args59.res
new file mode 100644 (file)
index 0000000..dadc656
--- /dev/null
@@ -0,0 +1,47 @@
+# 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.
+
+# comment 1
+class A
+       type FOO: Discrete
+       private var foo: FOO # comment
+       # comment 2
+       var bar: Int = 10
+end
+
+class B
+       super A
+
+       redef type FOO: Int
+       # comment 3
+       redef fun foo do return bar # comment
+       redef fun bar
+       do
+               return 10 # comment 4
+       end
+       fun baz do return # comment 5
+       protected fun baz2 do end
+       fun other: String do
+               return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+                       "aaaaaaaaaaaaaaaaaaaaaaaaaa"
+       end
+
+       fun foo1(arr: Array[String], len: Int, ind: Int): String
+       do
+               return "Hello World!"
+       end
+end
+
+# end
+
diff --git a/tests/sav/nitpretty_args60.res b/tests/sav/nitpretty_args60.res
new file mode 100644 (file)
index 0000000..0fd4ac4
--- /dev/null
@@ -0,0 +1,127 @@
+# 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.
+
+class Foo
+       fun bar: Bool do return true
+
+       fun foo(other: Foo): Foo
+       do
+               if other.bar then
+                       return other
+               else
+                       return self
+               end
+       end
+
+       fun baz: Int do
+               var nb = 0
+               while nb < 10 do
+                       print nb
+                       nb += 1
+               end # 1
+               return nb
+       end
+
+       fun gaz: Int
+       do
+               if bar then # 3
+                       return 1
+               else
+                       return -1 # 4
+               end
+       end
+end
+
+class Test[E]
+       var heap: ArrayHeap[E]
+
+       init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator)
+
+       init from(comparator: Comparator[E], items: Collection[E]) do
+               heap = new ArrayHeap[E].from(comparator, items.to_a)
+       end
+
+       fun count(k: E): Int do
+               if heap.has(k) then
+                       return 1
+               else
+                       return 0
+               end
+       end
+
+       fun node_at_idx(i: Int, k: E) do
+               while heap != null do
+                       if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet
+                               break
+                       end
+               end
+       end
+
+       fun save_those_nodes(nodes: Collection[Object]) do
+               for node in nodes do count(node)
+       end
+end
+
+fun foo do
+       if last_slash > 0 then
+               return substring(last_slash + 1, length)
+       else
+               return null
+       end
+end
+
+print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidun" +
+       "t sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit a" +
+       "met lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus" +
+       " eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pell" +
+       "entesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae" +
+       " consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae" +
+       " lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas" +
+       " turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed phar" +
+       "etra lacus."
+
+var lorem = "lorem"
+var ipsum = "ipsum" # for fun
+
+print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, con" +
+       "sectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius a" +
+       "t non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisi" +
+       "s neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}" +
+       ". Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} pla" +
+       "cerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus pl" +
+       "acerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum " +
+       "augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pell" +
+       "entesque vitae arcu justo. Aliquam sed pharetra lacus." # ending
+
+var title = "title"
+var links = new Array[String] # why not?
+
+var body = """
+<!DOCTYPE html>
+<head>
+       <meta charset="utf-8">
+       <meta http-equiv="X-UA-Compatible" content="IE=edge">
+       <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <title>{{{title}}}</title>
+</head>
+<body>
+       <div class="container">
+               <h1>{{{title}}}</h1>
+               <ul>
+                       <li>{{{links.join("</li>\n\t\t\t<li>")}}}</li>
+               </ul>
+       </div>
+</body>
+</html>"""
+
diff --git a/tests/sav/nitpretty_args61.res b/tests/sav/nitpretty_args61.res
new file mode 100644 (file)
index 0000000..6b5b168
--- /dev/null
@@ -0,0 +1,44 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# comment 1
+class A
+       type FOO: Discrete
+       private var foo: FOO # comment
+       # comment 2
+       var bar: Int = 10
+end
+
+class B
+       super A
+
+       redef type FOO: Int
+       # comment 3
+       redef fun foo do return bar # comment
+       redef fun bar do
+               return 10 # comment 4
+       end
+       fun baz do return # comment 5
+       protected fun baz2 do end
+       fun other: String do
+               return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       end
+
+       fun foo1(arr: Array[String], len: Int, ind: Int): String do
+               return "Hello World!"
+       end
+end
+
+# end
+
diff --git a/tests/sav/nitpretty_args62.res b/tests/sav/nitpretty_args62.res
new file mode 100644 (file)
index 0000000..329aa03
--- /dev/null
@@ -0,0 +1,109 @@
+# 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.
+
+class Foo
+       fun bar: Bool do return true
+
+       fun foo(other: Foo): Foo do
+               if other.bar then
+                       return other
+               else
+                       return self
+               end
+       end
+
+       fun baz: Int do
+               var nb = 0
+               while nb < 10 do
+                       print nb
+                       nb += 1
+               end # 1
+               return nb
+       end
+
+       fun gaz: Int do
+               if bar then # 3
+                       return 1
+               else
+                       return -1 # 4
+               end
+       end
+end
+
+class Test[E]
+       var heap: ArrayHeap[E]
+
+       init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator)
+
+       init from(comparator: Comparator[E], items: Collection[E]) do
+               heap = new ArrayHeap[E].from(comparator, items.to_a)
+       end
+
+       fun count(k: E): Int do
+               if heap.has(k) then
+                       return 1
+               else
+                       return 0
+               end
+       end
+
+       fun node_at_idx(i: Int, k: E) do
+               while heap != null do
+                       if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet
+                               break
+                       end
+               end
+       end
+
+       fun save_those_nodes(nodes: Collection[Object]) do
+               for node in nodes do count(node)
+       end
+end
+
+fun foo do
+       if last_slash > 0 then
+               return substring(last_slash + 1, length)
+       else
+               return null
+       end
+end
+
+print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus."
+
+var lorem = "lorem"
+var ipsum = "ipsum" # for fun
+
+print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending
+
+var title = "title"
+var links = new Array[String] # why not?
+
+var body = """
+<!DOCTYPE html>
+<head>
+       <meta charset="utf-8">
+       <meta http-equiv="X-UA-Compatible" content="IE=edge">
+       <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+       <title>{{{title}}}</title>
+</head>
+<body>
+       <div class="container">
+               <h1>{{{title}}}</h1>
+               <ul>
+                       <li>{{{links.join("</li>\n\t\t\t<li>")}}}</li>
+               </ul>
+       </div>
+</body>
+</html>"""
+
index e404557..ea248bc 100644 (file)
@@ -29,7 +29,8 @@ class B
        # comment 3
        redef fun foo do return bar # comment
 
-       redef fun bar do
+       redef fun bar
+       do
                return 10 # comment 4
        end
 
@@ -37,8 +38,12 @@ class B
        protected fun baz2 do end
 
        fun other: String do
-               return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
-                       "aaaaaaaaaaaaaaaaaaaaaaaaaa"
+               return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+       end
+
+       fun foo1(arr: Array[String], len: Int, ind: Int): String
+       do
+               return "Hello World!"
        end
 end
 
index bd21dbb..46c03e1 100644 (file)
@@ -31,7 +31,5 @@ class A
        fun foo5 do end # comment
 
        fun foo6(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,
-               aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Int) do
-               print 1
-       end
+               aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Int) do print 1
 end # comment
index faaa0c9..91eb55f 100644 (file)
@@ -5,17 +5,17 @@ Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
 TestSuites:
 No test cases found
 Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_nitunit2"><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::foo1"><system-err></system-err><system-out>if true then
+<testsuites><testsuite package="test_nitunit2"><testcase classname="nitunit.test_nitunit2.standard::Object" name="test_nitunit2::Object::foo1"><system-err></system-err><system-out>if true then
 
    assert true
 
 end
-</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::bar2"><system-err></system-err><system-out>if true then
+</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::Object" name="test_nitunit2::Object::bar2"><system-err></system-err><system-out>if true then
 
     assert true
 
 end
-</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::foo3"><system-err></system-err><system-out>var a = 1
+</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::Object" name="test_nitunit2::Object::foo3"><system-err></system-err><system-out>var a = 1
 assert a == 1
 assert a == 1
 </system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
index 7019ad0..356eaf4 100644 (file)
@@ -5,7 +5,7 @@ Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0
 TestSuites:
 No test cases found
 Class suites: 0; Test Cases: 0; Failures: 0
-<testsuites><testsuite package="test_doc2"><testcase classname="nitunit.test_doc2.standard::kernel::Object" name="test_doc2::Object::foo1"><system-err></system-err><system-out>assert true # tested
-</system-out></testcase><testcase classname="nitunit.test_doc2.standard::kernel::Object" name="test_doc2::Object::foo2"><system-err></system-err><system-out>assert true # tested
-</system-out></testcase><testcase classname="nitunit.test_doc2.standard::kernel::Object" name="test_doc2::Object::foo3"><system-err></system-err><system-out>assert true # tested
+<testsuites><testsuite package="test_doc2"><testcase classname="nitunit.test_doc2.standard::Object" name="test_doc2::Object::foo1"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2.standard::Object" name="test_doc2::Object::foo2"><system-err></system-err><system-out>assert true # tested
+</system-out></testcase><testcase classname="nitunit.test_doc2.standard::Object" name="test_doc2::Object::foo3"><system-err></system-err><system-out>assert true # tested
 </system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
diff --git a/tests/sav/opengles2_hello_triangle.res b/tests/sav/opengles2_hello_triangle.res
deleted file mode 100644 (file)
index c1dad34..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-../lib/mnit_linux/linux_app.nit:29,16--31: Redef Error: a virtual type cannot be refined.
-../lib/mnit_linux/linux_app.nit:30,16--29: Redef Error: a virtual type cannot be refined.
index e55ae8a..7277957 100644 (file)
 # limitations under the License.
 
 module test_annot_c_compiler is
-       c_compiler_option("-I /usr/include")
-       c_compiler_option(exec("pkg-config", "--cflags", "sdl"))
-       c_linker_option("-lm")
-       c_linker_option("-lm", "-L /usr/bin")
+       cflags "-I /usr/include"
+       cflags exec("pkg-config", "--cflags", "sdl")
+       ldflags "-lm"
+       ldflags("-lm", "-L /usr/bin")
 end
 
 fun dummy `{ printf("nothing...\n"); `}
index 381cc4f..695020f 100644 (file)
@@ -15,6 +15,9 @@
 module test_annot1 is platform("android")
 
 class A
+       fun goo is intern
+
+       # test
        fun foo is a, b
        fun bar is a, b do print "1"
        fun baz is a, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb do print "2"
index 0b46c27..3b692a5 100644 (file)
@@ -35,6 +35,11 @@ class B
        end # comment 5
        protected fun baz2 do end
        fun other: String do return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+
+       fun foo1(arr: Array[String], len: Int, ind: Int): String
+       do
+               return "Hello World!"
+       end
 end
 
 # end