Merge: share/libgc: option to use a local version of the source pkgs
authorJean Privat <jean@pryen.org>
Thu, 21 May 2015 00:32:38 +0000 (20:32 -0400)
committerJean Privat <jean@pryen.org>
Thu, 21 May 2015 00:32:38 +0000 (20:32 -0400)
This is only for tests servers, and should not be of any use to the regular user who doesn't trash and reclone the Nit repo many times each day.

So on Pratchett, @privat , you could download both source packages in the home directory
and the script will use and reuse them instead of repeatedly downloading them. I can change the directory to whatever you prefer, maybe ~/Downloads? or ~/.cache?

It will save us from some false CI test failure, like in http://gresil.org/jenkins/job/CI_github/2648/, and we will stop spamming the server.

Pull-Request: #1358
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

66 files changed:
examples/calculator/.gitignore [new file with mode: 0644]
examples/calculator/Makefile
examples/calculator/src/calculator.nit [new file with mode: 0644]
examples/calculator/src/calculator_android.nit [deleted file]
examples/calculator/src/calculator_gtk.nit [deleted file]
examples/calculator/src/calculator_test.nit
examples/rosettacode/loops_downward_for.nit
lib/android/README.md
lib/android/examples/Makefile
lib/android/examples/src/ui_test.nit
lib/android/ui/ui.nit
lib/app/README.md [new file with mode: 0644]
lib/app/ui.nit [new file with mode: 0644]
lib/binary/binary.nit [new file with mode: 0644]
lib/bucketed_game.nit
lib/counter.nit
lib/dummy_array.nit
lib/gtk/v3_4/gtk_core.nit
lib/gtk/v3_6.nit
lib/json_serialization.nit
lib/linux/ui.nit [new file with mode: 0644]
lib/neo4j/graph/graph.nit
lib/serialization/serialization.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/collection/range.nit
lib/standard/collection/union_find.nit
lib/standard/file.nit
lib/standard/kernel.nit
lib/standard/stream.nit
lib/trees/bintree.nit
src/doc/console_templates/console_model.nit [new file with mode: 0644]
src/doc/console_templates/console_templates.nit [new file with mode: 0644]
src/doc/doc_base.nit
src/doc/doc_phases/doc_console.nit [new file with mode: 0644]
src/doc/doc_phases/doc_indexing.nit
src/doc/doc_phases/doc_structure.nit
src/doc/html_templates/html_model.nit
src/doc/html_templates/html_templates.nit
src/frontend/serialization_phase.nit
src/location.nit
src/neo.nit
tests/sav/nitg-e/test_serialization.res
tests/sav/nitg-e/test_serialization_redef.res
tests/sav/nitg-e/test_serialization_redef_alt0.res
tests/sav/nitg-e/test_serialization_redef_alt1.res
tests/sav/nitg-e/test_serialization_redef_alt2.res
tests/sav/nitserial_args1.res
tests/sav/test_binary.res [new file with mode: 0644]
tests/sav/test_binary_alt1.res [new file with mode: 0644]
tests/sav/test_binary_alt2.res [new file with mode: 0644]
tests/sav/test_binary_alt3.res [new file with mode: 0644]
tests/sav/test_coll_eq.res [new file with mode: 0644]
tests/sav/test_deserialization_serial.res
tests/sav/test_serialization.res
tests/sav/test_serialization_redef.res
tests/sav/test_serialization_redef_alt0.res
tests/sav/test_serialization_redef_alt1.res
tests/sav/test_serialization_redef_alt2.res
tests/test_binary.nit [new file with mode: 0644]
tests/test_coll_eq.nit [new file with mode: 0644]
tests/test_deserialization.nit
tests/test_deserialization_serial.nit

diff --git a/examples/calculator/.gitignore b/examples/calculator/.gitignore
new file mode 100644 (file)
index 0000000..700b511
--- /dev/null
@@ -0,0 +1 @@
+res/
index dd66960..071fa2b 100644 (file)
@@ -1,12 +1,26 @@
-all:
-       mkdir -p bin/
+NITC=../../bin/nitc
+NITLS=../../bin/nitls
 
-       ../../bin/nitc --dir bin/ src/calculator_gtk.nit src/calculator_test.nit
+all: bin/calculator bin/calculator.apk bin/test
 
-android:
-       mkdir -p bin/ res/
+bin/calculator: $(shell ${NITLS} -M src/calculator.nit ../../lib/linux/ui.nit) ${NITC}
+       mkdir -p bin
+       ${NITC} -o $@ src/calculator.nit -m ../../lib/linux/ui.nit
+
+bin/calculator.apk: $(shell ${NITLS} -M src/calculator.nit ../../lib/android/ui/) ${NITC} ../../contrib/inkscape_tools/bin/svg_to_icons
+       mkdir -p bin res
        ../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
-       ../../bin/nitc -o bin/calculator.apk src/calculator_android.nit
+       ${NITC} -o $@ src/calculator.nit -m ../../lib/android/ui/
+
+../../contrib/inkscape_tools/bin/svg_to_icons:
+       make -C ../../contrib/inkscape_tools/
 
-android-install: android
+android-install: bin/calculator.apk
        adb install -r bin/calculator.apk
+
+bin/test: $(shell ${NITLS} -M src/calculator_test.nit) ${NITC}
+       mkdir -p bin
+       ${NITC} -o $@ src/calculator_test.nit
+
+check: bin/test
+       bin/test
diff --git a/examples/calculator/src/calculator.nit b/examples/calculator/src/calculator.nit
new file mode 100644 (file)
index 0000000..2e2f437
--- /dev/null
@@ -0,0 +1,114 @@
+# 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.
+
+# Portable calculator UI
+module calculator is
+app_name "app.nit Calc."
+       app_version(0, 1, git_revision)
+       app_namespace "org.nitlanguage.calculator"
+
+       # Lock in portrait mode
+       android_manifest_activity """android:screenOrientation="portrait""""
+       android_api_target 15
+end
+
+import app::ui
+import app::data_store
+import android::aware
+
+import calculator_logic
+
+redef class App
+       redef fun on_create
+       do
+               # Create the main window
+               window = new CalculatorWindow
+               super
+       end
+end
+
+# The main (and only) window of this calculator
+class CalculatorWindow
+       super Window
+
+       # Calculator context, our business logic
+       private var context = new CalculatorContext
+
+       # Main window layout
+       private var layout = new VerticalLayout(parent=self)
+
+       # Main display, at the top of the screen
+       private var display = new TextInput(parent=layout)
+
+       # Maps operators as `String` to their `Button`
+       private var buttons = new HashMap[String, Button]
+
+       init
+       do
+               # All the button labels, row by row
+               var rows = [["7", "8", "9", "+"],
+                           ["4", "5", "6", "-"],
+                           ["1", "2", "3", "*"],
+                           ["0", ".", "C", "/"],
+                           ["="]]
+
+               for row in rows do
+                       var row_layout = new HorizontalLayout(parent=layout)
+
+                       for op in row do
+                               var but = new Button(parent=row_layout, text=op)
+                               but.observers.add self
+                               buttons[op] = but
+                       end
+               end
+       end
+
+       redef fun on_event(event)
+       do
+               if event isa ButtonPressEvent then
+
+                       var op = event.sender.text
+                       if op == "." then
+                               event.sender.enabled = false
+                               context.switch_to_decimals
+                       else if op.is_numeric then
+                               var n = op.to_i
+                               context.push_digit n
+                       else
+                               buttons["."].enabled = true
+                               context.push_op op.chars.first
+                       end
+
+                       display.text = context.display_text
+               end
+       end
+
+       redef fun on_save_state
+       do
+               app.data_store["context"] = context.to_json
+               super
+       end
+
+       redef fun on_restore_state
+       do
+               super
+
+               var save = app.data_store["context"]
+               if save == null then return
+               assert save isa String
+
+               self.context = new CalculatorContext.from_json(save)
+               display.text = context.display_text
+       end
+end
diff --git a/examples/calculator/src/calculator_android.nit b/examples/calculator/src/calculator_android.nit
deleted file mode 100644 (file)
index 1b1e686..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Android calculator application
-module calculator_android is
-       app_name "app.nit Calc."
-       app_version(0, 1, git_revision)
-       app_namespace "org.nitlanguage.calculator"
-
-       # Lock in portrait mode
-       android_manifest_activity """android:screenOrientation="portrait""""
-end
-
-# FIXME the next line should import `android` only when it uses nit_activity
-import android::log
-import android::ui
-
-import calculator_logic
-
-redef class Activity
-       super EventCatcher
-
-       private var context = new CalculatorContext
-
-       # The main display, at the top of the screen
-       private var display: EditText
-
-       # Maps operators as `String` to their `Button`
-       private var op2but = new HashMap[String, Button]
-
-       # Has this window been initialized?
-       private var inited = false
-
-       redef fun on_start
-       do
-               super
-
-               if inited then return
-               inited = true
-
-               # Setup UI
-               var layout = new NativeLinearLayout(native)
-               layout.set_vertical
-
-               # Display screen
-               var display = new EditText
-               layout.add_view_with_weight(display.native, 1.0)
-               display.text_size = 36.0
-               self.display = display
-
-               # Buttons; numbers and operators
-               var ops = [["7", "8", "9", "+"],
-                          ["4", "5", "6", "-"],
-                          ["1", "2", "3", "*"],
-                          ["0", ".", "C", "/"],
-                          ["="]]
-
-               for line in ops do
-                       var buts_layout = new NativeLinearLayout(native)
-                       buts_layout.set_horizontal
-                       layout.add_view_with_weight(buts_layout, 1.0)
-
-                       for op in line do
-                               var but = new Button
-                               but.event_catcher = self
-                               but.text = op
-                               but.text_size = 40
-                               buts_layout.add_view_with_weight(but.native, 1.0)
-                               op2but[op] = but
-                       end
-               end
-
-               native.content_view = layout
-       end
-
-       redef fun on_save_instance_state(state)
-       do
-               super
-
-               var nity = new Bundle.from(state)
-               nity["context"] = context.to_json
-       end
-
-       redef fun on_restore_instance_state(state)
-       do
-               super
-
-               var nity = new Bundle.from(state)
-               if not nity.has("context") then return
-
-               var json = nity.string("context")
-               if json == null then return
-
-               context = new CalculatorContext.from_json(json)
-       end
-
-       redef fun on_resume
-       do
-               display.text = context.display_text
-       end
-
-       redef fun catch_event(event)
-       do
-               if event isa ClickEvent then
-                       var sender = event.sender
-                       var op = sender.text
-
-                       if op == "." then
-                               sender.enabled = false
-                               context.switch_to_decimals
-                       else if op.is_numeric then
-                               var n = op.to_i
-                               context.push_digit n
-                       else
-                               op2but["."].enabled = true
-                               context.push_op op.chars.first
-                       end
-
-                       display.text = context.display_text
-               end
-       end
-end
diff --git a/examples/calculator/src/calculator_gtk.nit b/examples/calculator/src/calculator_gtk.nit
deleted file mode 100644 (file)
index 7586230..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2013-2014 Alexis Laferrière <alexis.laf@xymus.net>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# GTK calculator
-module calculator_gtk
-
-import calculator_logic
-
-import gtk
-
-# GTK calculator UI
-class CalculatorGui
-       super GtkCallable
-
-       private var win: GtkWindow is noinit
-       private var container: GtkGrid is noinit
-
-       private var lbl_disp: GtkLabel is noinit
-       private var but_eq: GtkButton is noinit
-       private var but_dot: GtkButton is noinit
-
-       private var context = new CalculatorContext
-
-       redef fun signal(sender, op)
-       do
-               if op isa Char then # is an operation
-                       if op == '.' then
-                               but_dot.sensitive = false
-                               context.switch_to_decimals
-                       else
-                               but_dot.sensitive = true
-                               context.push_op op
-                       end
-               else if op isa Int then # is a number
-                       context.push_digit op
-               end
-
-               lbl_disp.text = context.display_text
-       end
-
-       init
-       do
-               init_gtk
-
-               win = new GtkWindow(new GtkWindowType.toplevel)
-               win.connect_destroy_signal_to_quit
-
-               container = new GtkGrid
-               win.add container
-
-               lbl_disp = new GtkLabel("_")
-               container.attach(lbl_disp, 0, 0, 5, 1)
-
-               # Digits
-               for n in [0..9] do
-                       var but = new GtkButton.with_label(n.to_s)
-                       but.request_size(64, 64)
-                       but.signal_connect("clicked", self, n)
-                       if n == 0 then
-                               container.attach(but, 0, 4, 1, 1)
-                       else container.attach(but, (n-1)%3, 3-(n-1)/3, 1, 1)
-               end
-
-               # Operators
-               var r = 1
-               for op in ['+', '-', '*', '/'] do
-                       var but = new GtkButton.with_label(op.to_s)
-                       but.request_size(64, 64)
-                       but.signal_connect("clicked", self, op)
-                       container.attach(but, 3, r, 1, 1)
-                       r+=1
-               end
-
-               # =
-               but_eq = new GtkButton.with_label("=")
-               but_eq.request_size(64, 64)
-               but_eq.signal_connect("clicked", self, '=')
-               container.attach(but_eq, 4, 3, 1, 2)
-
-               # .
-               but_dot = new GtkButton.with_label(".")
-               but_dot.request_size(64, 64)
-               but_dot.signal_connect("clicked", self, '.')
-               container.attach(but_dot, 1, 4, 1, 1)
-
-               # C
-               var but_c =  new GtkButton.with_label("C")
-               but_c.request_size(64, 64)
-               but_c.signal_connect("clicked", self, 'C')
-               container.attach(but_c, 2, 4, 1, 1)
-
-               win.show_all
-       end
-end
-
-# Do not show GUI in when testing
-if "NIT_TESTING".environ == "true" then exit 0
-
-var app = new CalculatorGui
-run_gtk
index 6937751..270854c 100644 (file)
@@ -29,7 +29,7 @@ context.push_op( '*' )
 context.push_digit( 2 )
 context.push_op( '=' )
 var r = context.result
-assert r == "30.00" else print r or else "-"
+assert r == 30 else print r or else "-"
 
 context = new CalculatorContext
 context.push_digit( 1 )
@@ -40,14 +40,14 @@ context.push_op( '*' )
 context.push_digit( 3 )
 context.push_op( '=' )
 r = context.result
-assert r == "42.30" else print r or else "-"
+assert r == 42.3 else print r or else "-"
 
 context.push_op( '+' )
 context.push_digit( 1 )
 context.push_digit( 1 )
 context.push_op( '=' )
 r = context.result
-assert r == "53.30" else print r or else "-"
+assert r == 53.3 else print r or else "-"
 
 context = new CalculatorContext
 context.push_digit( 4 )
@@ -58,7 +58,7 @@ context.push_op( '/' )
 context.push_digit( 3 )
 context.push_op( '=' )
 r = context.result
-assert r == "14.10" else print r or else "-"
+assert r == 14.1 else print r or else "-"
 
 #test multiple decimals
 context = new CalculatorContext
@@ -72,7 +72,7 @@ context.push_op( '+' )
 context.push_digit( 1 )
 context.push_op( '=' )
 r = context.result
-assert r == "51.123" else print r or else "-"
+assert r == 51.123 else print r or else "-"
 
 #test 'C' button
 context = new CalculatorContext
@@ -84,4 +84,4 @@ context.push_digit( 0 )
 context.push_op( '=' )
 context.push_op( 'C' )
 r = context.result
-assert r == "0.0" else print r or else "-"
+assert r == null else print r
index c5b739e..bfeaff0 100644 (file)
@@ -8,6 +8,6 @@
 # SEE: http://rosettacode.org/wiki/Loops/Downward_for
 module loops_downward_for
 
-for i in [0..10].reverse_iterator do
+for i in [10..0].step(-1) do
        print i
 end
index fb11c65..47c0e4b 100644 (file)
@@ -13,29 +13,15 @@ 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.
+The _app.nit_ framework and this project offers some services to
+customize the generated Android application.
 
-## Module annotations
+## 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.
+* All _app.nit_ annotations are applied to Android projects:
+  `app_name`, `app_namespace` and `app_version`.
 
-    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"`
-
-* `app_namespace` 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}`.
+    See: `../app/README.md`
 
 * Custom information can be added to the Android manifest file
 using the annotations `android_manifest`, `android_manifest_application`
@@ -47,8 +33,8 @@ and `android_manifest_activity`.
     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
+* The API version target can be specified with `android_api_min`,
+`android_api_max` and `android_api_target`. These take a single
 integer as argument. They are applied in the Android manifest as
 `minSdkVesion`, `targetSdkVersion` and `maxSdkVersion`.
 
index f0fd982..17fb8bf 100644 (file)
@@ -2,10 +2,9 @@ android:
        mkdir -p bin/ res/
        ../../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
        ../../../bin/nitc --dir bin/ src/ui_test.nit
-       adb install -r bin/ui_test.apk
 
 install: android
-       adb install -r bin/ui.apk
+       adb install -r bin/ui_test.apk
 
 clean:
        rm -rf bin
index bdb06c5..4f63a92 100644 (file)
@@ -20,44 +20,37 @@ module ui_test is
        app_version(0, 1, git_revision)
        app_namespace "org.nitlanguage.ui_test"
        android_manifest_activity """android:theme="@android:style/Theme.Light""""
+       android_api_target 15
 end
 
-import android
 import android::ui
 import android::toast
 import android::notification
 
 redef class App
-
-       var but_notif: Button
-       var but_toast: Button
-
-       var notif: nullable Notification = null
-
-       var inited = false
-       redef fun init_window
+       redef fun on_create
        do
+               self.window = new Window
                super
+       end
+end
 
-               if inited then return
-               inited = true
+redef class Window
 
-               # Setup UI
-               var context = native_activity
-               var layout = new NativeLinearLayout(context)
-               layout.set_vertical
+       private var layout = new VerticalLayout(parent=self)
 
-               but_notif = new Button
-               but_notif.text = "Show Notification"
-               layout.add_view but_notif.native
+       private var but_notif = new Button(parent=layout, text="Show Notification")
+       private var but_toast = new Button(parent=layout, text="Show Toast")
 
-               but_toast = new Button
-               but_toast.text = "Show Toast"
-               layout.add_view but_toast.native
+       private var notif: nullable Notification = null
 
-               context.content_view = layout
+       init
+       do
+               but_notif.observers.add self
+               but_toast.observers.add self
        end
 
+       # Action when pressing `but_notif`
        fun act_notif
        do
                var notif = self.notif
@@ -72,14 +65,16 @@ redef class App
                end
        end
 
+       # Action when pressing `but_toast`
        fun act_toast
        do
-               toast("Sample toast from app.nit at {get_time}", false)
+               app.toast("Sample toast from app.nit at {get_time}", false)
        end
 
-       redef fun catch_event(event)
+       redef fun on_event(event)
        do
-               if event isa ClickEvent then
+               print "on_event {event}"
+               if event isa ButtonPressEvent then
                        var sender = event.sender
                        if sender == but_notif then
                                act_notif
index a37e572..f534e77 100644 (file)
@@ -1,7 +1,5 @@
 # This file is part of NIT (http://www.nitlanguage.org).
 #
-# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
-#
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # You may obtain a copy of the License at
 # Views and services to use the Android native user interface
 module ui
 
+# Implementation note:
+#
+# We cannot rely on `Activity::on_restore_instance_state` to implement
+# `on_restore_state` is it only invoked if there is a bundled state,
+# and we don't use the Android bundled state.
+
 import native_ui
+import log
+import nit_activity
+
+import app::ui
+private import data_store
+
+redef class Control
+       # The Android element used to implement `self`
+       fun native: NATIVE is abstract
 
-# An event from the `app.nit` framework
-interface AppEvent
-       # Reaction to this event
-       fun react do end
+       # Type of `native`
+       type NATIVE: JavaObject
 end
 
-# A control click event
-class ClickEvent
-       super AppEvent
+redef class Window
+       redef var native = app.native_activity
 
-       # Sender of this event
-       var sender: Button
+       redef type NATIVE: NativeActivity
 
-       redef fun react do sender.click self
-end
+       redef fun add(item)
+       do
+               super
 
-# Receiver of events not handled directly by the sender
-interface EventCatcher
-       fun catch_event(event: AppEvent) do end
+               # FIXME abstract the Android restriction where `content_view` must be a layout
+               assert item isa Layout
+               native.content_view = item.native
+       end
 end
 
-redef class App
-       super EventCatcher
-end
+redef class View
+       redef type NATIVE: NativeView
 
-# An `Object` that raises events
-abstract class Eventful
-       var event_catcher: EventCatcher = app is lazy, writable
+       redef fun enabled=(enabled) do native.enabled = enabled or else true
+       redef fun enabled do return native.enabled
 end
 
-#
-## Nity classes and services
-#
+redef class Layout
+       redef type NATIVE: NativeViewGroup
 
-# An Android control with text
-abstract class TextView
-       super Finalizable
-       super Eventful
-
-       # Native Java variant to this Nity class
-       type NATIVE: NativeTextView
+       redef fun add(item)
+       do
+               super
 
-       # The native Java object encapsulated by `self`
-       var native: NATIVE is noinit
+               assert item isa View
 
-       # Get the text of this view
-       fun text: String
-       do
-               var jstr = native.text
-               var str = jstr.to_s
-               jstr.delete_local_ref
-               return str
+               # FIXME abstract the use either homogeneous or weight to balance views size in a layout
+               native.add_view_with_weight(item.native, 1.0)
        end
+end
 
-       # Set the text of this view
-       fun text=(value: Text)
-       do
-               var jstr = value.to_s.to_java_string
-               native.text = jstr
-               jstr.delete_local_ref
+redef class HorizontalLayout
+       redef var native do
+               var layout = new NativeLinearLayout(app.native_activity)
+               layout.set_horizontal
+               return layout
        end
+end
 
-       # Get whether this view is enabled or not
-       fun enabled: Bool do return native.enabled
-
-       # Set if this view is enabled
-       fun enabled=(val: Bool) do native.enabled = val
-
-       # Set the size of the text in this view at `dpi`
-       fun text_size=(dpi: Numeric) do native.text_size = dpi.to_f
-
-       private var finalized = false
-       redef fun finalize
-       do
-               if not finalized then
-                       native.delete_global_ref
-                       finalized = true
-               end
+redef class VerticalLayout
+       redef var native do
+               var layout = new NativeLinearLayout(app.native_activity)
+               layout.set_vertical
+               return layout
        end
 end
 
-# An Android button
-class Button
-       super TextView
+redef class TextView
+       redef type NATIVE: NativeTextView
 
-       redef type NATIVE: NativeButton
-
-       init
-       do
-               var native = new NativeButton(app.native_activity, self)
-               self.native = native.new_global_ref
+       redef fun text do return native.text.to_s
+       redef fun text=(value) do
+               if value == null then value = ""
+               native.text = value.to_java_string
        end
 
-       # Click event
-       #
-       # By default, this method calls `app.catch_event`. It can be specialized
-       # with custom behavior or the receiver of `catch_event` can be changed
-       # with `event_catcher=`.
-       fun click(event: AppEvent) do event_catcher.catch_event(event)
+       # Size of the text
+       fun text_size: Float do return native.text_size
 
-       private fun click_from_native do click(new ClickEvent(self))
+       # Size of the text
+       fun text_size=(text_size: nullable Float) do
+               if text_size != null then native.text_size = text_size
+       end
 end
 
-# An Android editable text field
-class EditText
-       super TextView
-
+redef class TextInput
        redef type NATIVE: NativeEditText
+       redef var native = (new NativeEditText(app.native_activity)).new_global_ref
+end
 
-       init
-       do
-               var native = new NativeEditText(app.activities.first.native)
-               self.native = native.new_global_ref
-       end
+redef class Button
+       super Finalizable
+
+       redef type NATIVE: NativeButton
+       redef var native = (new NativeButton(app.native_activity, self)).new_global_ref
+
+       private fun on_click do notify_observers new ButtonPressEvent(self)
+
+       redef fun finalize do native.delete_global_ref
 end
 
 redef class NativeButton
-       new (context: NativeActivity, sender_object: Object)
-       import Button.click_from_native in "Java" `{
+       private new (context: NativeActivity, sender_object: Button)
+       import Button.on_click in "Java" `{
                final int final_sender_object = sender_object;
 
                return new android.widget.Button(context){
                        @Override
                        public boolean onTouchEvent(android.view.MotionEvent event) {
                                if(event.getAction() == android.view.MotionEvent.ACTION_DOWN) {
-                                       Button_click_from_native(final_sender_object);
+                                       Button_on_click(final_sender_object);
                                        return true;
                                }
                                return false;
diff --git a/lib/app/README.md b/lib/app/README.md
new file mode 100644 (file)
index 0000000..22f4f77
--- /dev/null
@@ -0,0 +1,184 @@
+_app.nit_ is a framework to create cross-platform applications
+
+The framework provides services to manage common needs of modern mobile applications:
+
+* Life-cycle
+* User interface
+* Persistence
+* Package metadata
+* Compilation and packaging
+
+The features offered by _app.nit_ are common to all platforms, but
+may not be available on all devices.
+
+## Application Life-Cycle
+
+The _app.nit_ application life-cycle is compatible with all target platforms.
+It relies on the following sequence of events, represented here by their callback method name:
+
+1. `on_create`: The application is being created.
+   You should build the UI at this time.
+
+2. `on_start`: The app is starting or restarting, background activities may
+
+3. `on_resume`: The app enters the active state, it is in the foreground.
+
+4. `on_pause`: The app leaves the active state and the foreground.
+   It may still be visible in the background.
+   It may then go back to `on_resume` or `on_stop`.
+
+5. `on_stop`: The app is completely hidden.
+   It may then be destroyed (`on_destroy`) or go back to `on_start`.
+
+6. `on_destroy`: The app is being destroyed.
+
+Life-cycle events related to saving and restoring the application state are provided by two special callback methods:
+
+* `on_save_state`: The app may be destroyed soon, save its state for a future `on_restore_state`.
+  More on how it can be done in the `app::data_store` section.
+
+* `on_restore_state`: The app is launching, restore its state from a previous `on_save_state`.
+
+These events are synchronized to the native platforms applications
+The `App` instance is the first to be notified of these events.
+Other UI elements, from the `ui` submodule, are notified of the same events using a simple depth first visit.
+So all UI elements can react separately to live-cycle events.
+
+## User Interface
+
+The `app::ui` module defines an abstract API to build a portable graphical application.
+The API is composed of interactive `Control`s, visible `View`s and an active `Window`.
+
+Here is a subset of the most useful controls and views:
+
+* The classic pushable `Button` with text (usually rectangular).
+
+* `TextInput` is a field for the user to enter text.
+
+* `HorizontalLayout` and `VerticalLayout` organize other controls in order.
+
+Each control is notified of input events by callbacks to `on_event`.
+All controls have observers that are also notified of the events.
+So there is two ways  to customize the behavior on a given event:
+
+* Create a subclass of the wanted `Control`, let's say `Button`, and specialize `on_event`.
+
+* Add an observer to a `Button` instance, and implement `on_event` in the observer.
+
+### Usage Example
+
+The calculator example (at `../../examples/calculator/src/calculator.nit`) is a concrete,
+simple and complete use of the _app.nit_ portable UI.
+
+### Platform-specific UI
+
+You can go beyond the portable UI API of _app.nit_ by using the natives services of a platform.
+
+The suggested approach is to use platform specific modules to customize the application on a precise platform.
+This module redefine `Window::on_start` to call the native language of the platform and setup a native UI.
+
+_TODO complete description and add concrete examples_
+
+## Persistent State with data\_store
+
+_app.nit_ offers the submodule `app::data_store` to easily save the application state and user preferences.
+The service is accessible by the method `App::data_store`. The `DataStore` itself defines 2 methods:
+
+* `DataStore::[]=` saves and associates any serializable instances to a `String` key.
+Pass `null` to clear the value associated to a key.
+
+* `DataStore::[]` returns the object associated to a `String` key.
+It returns `null` if nothing is associated to the key.
+
+### Usage Example
+
+~~~
+import app::data_store
+
+redef class App
+       var user_name: String
+
+       redef fun on_save_state
+       do
+               app.data_store["first run"] = false
+               app.data_store["user name"] = user_name
+
+               super # call `on_save_state` on all attached instances of `AppComponent`
+       end
+
+       redef fun on_restore_state
+       do
+               var first_run = app.data_store["first run"]
+               if first_run != true then
+                       print "It's the first run of this application"
+               end
+
+               var user_name = app.data_store["user name"]
+               if user_name isa String then
+                       self.user_name = user_name
+               else self.user_name = "Undefined"
+
+               super
+       end
+end
+~~~
+
+## Metadata annotations
+
+The _app.nit_ framework defines three annotations to customize the application package.
+
+* `app_name` takes a single argument, the visible name of the application.
+  This name is used for launchers and window title.
+  By default, the name of the target module.
+
+* `app_namespace` specifies the full namespace (or package name) of the application package.
+  This value usually identify the application uniquely on application stores.
+  It should not change once the application has benn published.
+  By default, the namespace is `org.nitlanguage.{module_name}`.
+
+* `app_version` specifies the version of the application package.
+  This annotation expects at least one argument, usually we use three version numbers:
+  the major, minor and revision.
+  The special function `git_revision` will use the prefix of the hash of the latest git commit.
+  By default, the version is 0.1.
+
+### Usage Example
+
+~~~
+module my_module is
+    app_name "My App"
+    app_namespace "org.example.my_app"
+    app_version(1, 0, git_revision)
+end
+~~~
+
+## Compiling and Packaging an Application
+
+The Nit compiler detects the target platform from the importations and generates the appropriate application format and package.
+
+Applications using only the portable services of _app.nit_ require some special care at compilation.
+Such an application, let's say `calculator.nit`, does not depend on a specific platform and use the portable UI.
+The target platform must be specifed to the compiler for it to produce the correct application package.
+There is two main ways to achieve this goal:
+
+* The the mixin option (`-m path`) loads an additionnal module before compiling.
+  It can be used to load platform specific implementations of the _app.nit_ portable UI.
+
+  ~~~
+  # GNU/Linux version, using GTK
+  nitc calculator.nit -m NIT_DIR/lib/linux/ui.nit
+
+  # Android version
+  nitc calculator.nit -m NIT_DIR/lib/android/ui/
+  ~~~
+
+* A common alternative for larger projects is to use platform specific modules.
+  Continuing with the `calculator.nit` example, it can be accompagnied by the module `calculator_linux.nit`.
+  This module imports both `calculator` and `linux::ui`, and can also use other GNU/Linux specific code.
+
+  ~~~
+  module calculator_linux
+
+  import calculator
+  import linux::ui
+  ~~~
diff --git a/lib/app/ui.nit b/lib/app/ui.nit
new file mode 100644 (file)
index 0000000..2e30c76
--- /dev/null
@@ -0,0 +1,189 @@
+# 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.
+
+# Portable UI API for the _app.nit_ framework
+module ui
+
+import app_base
+
+redef class App
+       super AppComponent
+
+       # The current `Window` of this activity
+       #
+       # This attribute must be set by refinements of `App`.
+       var window: Window is writable
+
+       redef fun on_create do window.on_create
+
+       redef fun on_start do window.on_start
+
+       redef fun on_resume do window.on_resume
+
+       redef fun on_pause do window.on_pause
+
+       redef fun on_stop do window.on_stop
+
+       redef fun on_destroy do window.on_destroy
+
+       redef fun on_restore_state do window.on_restore_state
+
+       redef fun on_save_state do window.on_save_state
+end
+
+# An event created by an `AppComponent` and sent to `AppObserver`s
+interface AppEvent
+end
+
+# Observer of `AppEvent`s raised by `AppComponent`s
+interface AppObserver
+       # Notification of `event` raised by `sender`
+       #
+       # To be implemented in subclasses as needed.
+       fun on_event(event: AppEvent) do end
+end
+
+redef class AppComponent
+       super AppObserver
+
+       # All `AppObserver` notified of events raised by `self`
+       #
+       # By default, only `self` is an observer.
+       # Any other `AppObserver` can be added to this collection.
+       var observers = new HashSet[AppObserver].from([self: AppObserver])
+
+       # Propagate `event` to all `observers` by calling `AppObserver::on_event`
+       fun notify_observers(event: AppEvent)
+       do
+               for observer in observers do
+                       observer.on_event(event)
+               end
+       end
+end
+
+# A control implementing the UI
+class Control
+       super AppComponent
+
+       # Direct parent `Control` in the control tree
+       #
+       # If `null` then `self` is at the root of the tree, or not yet attached.
+       var parent: nullable CompositeControl = null is private writable(set_parent)
+
+       # Direct parent `Control` in the control tree
+       #
+       # Setting `parent` calls `remove` on the old parent and `add` on the new one.
+       fun parent=(parent: nullable CompositeControl)
+       is autoinit     do
+               var old_parent = self.parent
+               if old_parent != null and old_parent != parent then
+                       old_parent.remove self
+               end
+
+               if parent != null then parent.add self
+
+               set_parent parent
+       end
+end
+
+# A `Control` grouping other controls
+class CompositeControl
+       super Control
+
+       private var items = new HashSet[Control]
+
+       # Add `item` as a child of `self`
+       protected fun add(item: Control) do items.add item
+
+       # Remove `item` from `self`
+       protected fun remove(item: Control) do if has(item) then items.remove item
+
+       # Is `item` in `self`?
+       protected fun has(item: Control): Bool do return items.has(item)
+
+       redef fun on_create do for i in items do i.on_create
+
+       redef fun on_start do for i in items do i.on_start
+
+       redef fun on_resume do for i in items do i.on_resume
+
+       redef fun on_pause do for i in items do i.on_pause
+
+       redef fun on_stop do for i in items do i.on_stop
+
+       redef fun on_destroy do for i in items do i.on_destroy
+
+       redef fun on_restore_state do for i in items do i.on_restore_state
+
+       redef fun on_save_state do for i in items do i.on_save_state
+end
+
+# A window, root of the `Control` tree
+class Window
+       super CompositeControl
+end
+
+# A viewable `Control`
+abstract class View
+       super Control
+
+       # Is this control enabled so the user can interact with it?
+       #
+       # By default, or if set to `null`, the control is enabled.
+       var enabled: nullable Bool is writable #, abstract FIXME with #1311
+end
+
+# A control with some `text`
+abstract class TextView
+       super View
+
+       # Main `Text` of this control
+       #
+       # By default, or if set to `null`, no text is shown.
+       var text: nullable Text is writable #, abstract FIXME with #1311
+end
+
+# A control for the user to enter custom `text`
+class TextInput
+       super TextView
+end
+
+# A pushable button, raises `ButtonPressEvent`
+class Button
+       super TextView
+end
+
+# A `Button` press event
+class ButtonPressEvent
+       super AppEvent
+
+       # The `Button` that raised this event
+       var sender: Button
+end
+
+# A layout to visually organize `Control`s
+abstract class Layout
+       super View
+       super CompositeControl
+end
+
+# An horizontal linear organization
+class HorizontalLayout
+       super Layout
+end
+
+# A vertical linear organization
+class VerticalLayout
+       super Layout
+end
diff --git a/lib/binary/binary.nit b/lib/binary/binary.nit
new file mode 100644 (file)
index 0000000..0084227
--- /dev/null
@@ -0,0 +1,327 @@
+# 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.
+
+# Add reading and writing binary services
+#
+# ~~~
+# var w = new FileWriter.open("/tmp/data.bin")
+# w.write "hello"
+# w.write_int64 123456789
+# w.write_byte 3
+# w.write_float 1.25
+# w.write_double 1.234567
+# w.write_bits(true, false, true)
+# assert w.last_error == null
+# w.close
+#
+# var r = new FileReader.open("/tmp/data.bin")
+# assert r.read(5) == "hello"
+# assert r.read_int64 == 123456789
+# assert r.read_byte == 3
+# assert r.read_float == 1.25
+# assert r.read_double == 1.234567
+#
+# var bits = r.read_bits
+# assert bits[0] and not bits[1] and bits[2]
+#
+# assert r.last_error == null
+# r.close
+# ~~~
+module binary
+
+in "C" `{
+       #include <inttypes.h>
+       #include <endian.h>
+
+       // Android compatibility
+       #ifndef be64toh
+               #define be64toh(val) betoh64(val)
+       #endif
+       #ifndef le64toh
+               #define le64toh(val) letoh64(val)
+       #endif
+`}
+
+# A stream of binary data
+abstract class BinaryStream
+       super Stream
+
+       # Use the big-endian convention? otherwise use little-endian.
+       #
+       # By default, `true` to use big-endian.
+       var big_endian = true is writable
+end
+
+redef abstract class Writer
+       super BinaryStream
+
+       # Write a boolean `value` on a byte, using 0 for `false` and 1 for `true`
+       fun write_bool(value: Bool) do write_byte if value then 1 else 0
+
+       # Write up to 8 `Bool` in a byte
+       #
+       # To be used with `BinaryReader::read_bits`.
+       #
+       # Ensure: `bits.length <= 8`
+       fun write_bits(bits: Bool...)
+       do
+               assert bits.length <= 8
+
+               var int = 0
+               for b in bits.length.times do
+                       if bits[b] then int += 2**b
+               end
+
+               write_byte int
+       end
+
+       # Write a floating point `value` on 32 bits
+       #
+       # Using this format may result in a loss of precision as it uses less bits
+       # than Nit `Float`.
+       fun write_float(value: Float)
+       do
+               for i in [0..4[ do write_byte value.float_byte_at(i, big_endian)
+       end
+
+       # Write a floating point `value` on 64 bits
+       fun write_double(value: Float)
+       do
+               for i in [0..8[ do write_byte value.double_byte_at(i, big_endian)
+       end
+
+       # Write `value` as a signed integer on 64 bits
+       #
+       # Using this format may result in a loss of precision as the length of a
+       # Nit `Int` may be more than 64 bits on some platforms.
+       fun write_int64(value: Int)
+       do
+               for i in [0..8[ do write_byte value.int64_byte_at(i, big_endian)
+       end
+
+       # TODO:
+       #
+       # fun write_int8
+       # fun write_uint8 # No need for this one, it is the current `read_char`
+       # fun write_int16
+       # fun write_uint16
+       # fun write_int32
+       # fun write_uint32
+       # fun write_uint64
+       # fun write_long_double?
+end
+
+redef abstract class Reader
+       super BinaryStream
+
+       # Read a single byte and return `true` if its value is different than 0
+       fun read_bool: Bool do return read_byte != 0
+
+       # Get an `Array` of 8 `Bool` by reading a single byte
+       #
+       # To be used with `BinaryWriter::write_bits`.
+       fun read_bits: Array[Bool]
+       do
+               var int = read_byte
+               if int == null then return new Array[Bool]
+               return [for b in 8.times do int.bin_and(2**b) > 0]
+       end
+
+       # Read a floating point on 32 bits and return it as a `Float`
+       #
+       # Using this format may result in a loss of precision as it uses less bits
+       # than Nit `Float`.
+       fun read_float: Float
+       do
+               if last_error != null then return 0.0
+
+               var b0 = read_byte
+               var b1 = read_byte
+               var b2 = read_byte
+               var b3 = read_byte
+
+               # Check for error, `last_error` is set by `read_byte`
+               if b0 == null or b1 == null or b2 == null or b3 == null then return 0.0
+
+               return native_read_float(b0, b1, b2, b3, big_endian)
+       end
+
+       # Utility for `read_float`
+       private fun native_read_float(b0, b1, b2, b3: Int, big_endian: Bool): Float `{
+               union {
+                       unsigned char b[4];
+                       float val;
+                       uint32_t conv;
+               } u;
+
+               u.b[0] = b0;
+               u.b[1] = b1;
+               u.b[2] = b2;
+               u.b[3] = b3;
+
+               if (big_endian)
+                       u.conv = be32toh(u.conv);
+               else u.conv = le32toh(u.conv);
+
+               return u.val;
+       `}
+
+       # Read a floating point on 64 bits and return it as a `Float`
+       fun read_double: Float
+       do
+               if last_error != null then return 0.0
+
+               var b0 = read_byte
+               var b1 = read_byte
+               var b2 = read_byte
+               var b3 = read_byte
+               var b4 = read_byte
+               var b5 = read_byte
+               var b6 = read_byte
+               var b7 = read_byte
+
+               # Check for error, `last_error` is set by `read_byte`
+               if b0 == null or b1 == null or b2 == null or b3 == null or
+                  b4 == null or b5 == null or b6 == null or b7 == null then return 0.0
+
+               return native_read_double(b0, b1, b2, b3, b4, b5, b6, b7, big_endian)
+       end
+
+       # Utility for `read_double`
+       private fun native_read_double(b0, b1, b2, b3, b4, b5, b6, b7: Int, big_endian: Bool): Float `{
+               union {
+                       unsigned char b[8];
+                       double val;
+                       uint64_t conv;
+               } u;
+
+               u.b[0] = b0;
+               u.b[1] = b1;
+               u.b[2] = b2;
+               u.b[3] = b3;
+               u.b[4] = b4;
+               u.b[5] = b5;
+               u.b[6] = b6;
+               u.b[7] = b7;
+
+               if (big_endian)
+                       u.conv = be64toh(u.conv);
+               else u.conv = le64toh(u.conv);
+
+               return u.val;
+       `}
+
+       # Read a signed integer on 64 bits and return is an `Int`
+       #
+       # Using this format may result in a loss of precision as the length of a
+       # Nit `Int` may be less than 64 bits on some platforms.
+       fun read_int64: Int
+       do
+               if last_error != null then return 0
+
+               var b0 = read_byte
+               var b1 = read_byte
+               var b2 = read_byte
+               var b3 = read_byte
+               var b4 = read_byte
+               var b5 = read_byte
+               var b6 = read_byte
+               var b7 = read_byte
+
+               # Check for error, `last_error` is set by `read_byte`
+               if b0 == null or b1 == null or b2 == null or b3 == null or
+                  b4 == null or b5 == null or b6 == null or b7 == null then return 0
+
+               return native_read_int64(b0, b1, b2, b3, b4, b5, b6, b7, big_endian)
+       end
+
+       # Utility for `read_int64`
+       private fun native_read_int64(b0, b1, b2, b3, b4, b5, b6, b7: Int, big_endian: Bool): Int `{
+               union {
+                       unsigned char b[8];
+                       int64_t val;
+                       uint64_t conv;
+               } u;
+
+               u.b[0] = b0;
+               u.b[1] = b1;
+               u.b[2] = b2;
+               u.b[3] = b3;
+               u.b[4] = b4;
+               u.b[5] = b5;
+               u.b[6] = b6;
+               u.b[7] = b7;
+
+               if (big_endian)
+                       u.conv = be64toh(u.conv);
+               else u.conv = le64toh(u.conv);
+
+               return u.val;
+       `}
+end
+
+redef class Int
+       # Utility for `BinaryWriter`
+       private fun int64_byte_at(index: Int, big_endian: Bool): Int `{
+               union {
+                       unsigned char bytes[8];
+                       int64_t val;
+                       uint64_t conv;
+               } u;
+
+               u.val = recv;
+
+               if (big_endian)
+                       u.conv = htobe64(u.conv);
+               else u.conv = htole64(u.conv);
+
+               return u.bytes[index];
+       `}
+end
+
+redef class Float
+       # Utility for `BinaryWriter`
+       private fun float_byte_at(index: Int, big_endian: Bool): Int `{
+               union {
+                       unsigned char bytes[4];
+                       float val;
+                       uint32_t conv;
+               } u;
+
+               u.val = recv;
+
+               if (big_endian)
+                       u.conv = htobe32(u.conv);
+               else u.conv = htole32(u.conv);
+
+               return u.bytes[index];
+       `}
+
+       # Utility for `BinaryWriter`
+       private fun double_byte_at(index: Int, big_endian: Bool): Int `{
+               union {
+                       unsigned char bytes[8];
+                       double val;
+                       uint64_t conv;
+               } u;
+
+               u.val = recv;
+
+               if (big_endian)
+                       u.conv = htobe64(u.conv);
+               else u.conv = htole64(u.conv);
+
+               return u.bytes[index];
+       `}
+end
index eba5d69..a621c54 100644 (file)
 # such as a forest with many individual trees.
 module bucketed_game
 
+import serialization
+
 # Something acting on the game
 class Turnable[G: Game]
+       auto_serializable
 
        # Execute `turn` for this instance.
        fun do_turn(turn: GameTurn[G]) is abstract
@@ -32,6 +35,8 @@ end
 # Something acting on the game from time to time
 class Bucketable[G: Game]
        super Turnable[G]
+       auto_serializable
+
        private var act_at: nullable Int = null
 
        # Cancel the previously registered acting turn
@@ -44,6 +49,7 @@ end
 # Optimized organization of `Bucketable` instances
 class Buckets[G: Game]
        super Turnable[G]
+       auto_serializable
 
        # Bucket type used in this implementation.
        type BUCKET: HashSet[Bucketable[G]]
@@ -112,10 +118,12 @@ end
 # Event raised at the first turn
 class FirstTurnEvent
        super GameEvent
+       auto_serializable
 end
 
 # Game logic on the client
 class ThinGame
+       auto_serializable
 
        # Game tick when `self` should act.
        #
@@ -125,17 +133,19 @@ end
 
 # Game turn on the client
 class ThinGameTurn[G: ThinGame]
+       auto_serializable
 
        # Game tick when `self` should act.
        var tick: Int is protected writable
 
-       # List of game events occured for `self`.
-       var events = new List[GameEvent] is protected writable
+       # Game events occurred for `self`.
+       var events = new Array[GameEvent] is protected writable
 end
 
 # Game turn on the full logic
 class GameTurn[G: Game]
        super ThinGameTurn[G]
+       auto_serializable
 
        # Game that `self` belongs to.
        var game: G
@@ -163,6 +173,7 @@ end
 # Full game logic
 class Game
        super ThinGame
+       auto_serializable
 
        # Game type used in this implementation.
        type G: Game
index 6d5729d..befce9c 100644 (file)
@@ -53,14 +53,14 @@ class Counter[E]
        redef fun iterator do return map.iterator
 
        # The number of counted occurrences of `e`
-       redef fun [](e: E): Int
+       redef fun [](e)
        do
                var map = self.map
                if map.has_key(e) then return map[e]
                return 0
        end
 
-       redef fun []=(e: E, value: Int)
+       redef fun []=(e, value)
        do
                sum -= self[e]
                self.map[e] = value
index 9061e53..85440b4 100644 (file)
@@ -28,9 +28,10 @@ class DummyArray
                _length = l + 1
        end
 
-       redef fun remove(value: Int)
+       redef fun remove(value)
        do
                assert not is_empty
+               if not value isa Int then return
                var l = _length
                if l > 1 then
                        var last = _values[l - 1]
@@ -41,8 +42,9 @@ class DummyArray
                _length = l - 1
        end
 
-       redef fun has(value: Int): Bool
+       redef fun has(value)
        do
+               if not value isa Int then return false
                assert value < _capacity
                var pos = _keys[value]
                if pos < _length then
index 88842a4..88e373a 100644 (file)
@@ -389,11 +389,11 @@ extern class GtkBox `{ GtkBox * `}
        `}
 
        # Give the children of `self` equal space in the box?
-       fun omogeneous: Bool `{ return gtk_box_get_homogeneous(recv); `}
+       fun homogeneous: Bool `{ return gtk_box_get_homogeneous(recv); `}
 
        # Give the children of `self` equal space in the box?
-       fun omogeneous=(omogeneous: Bool) `{
-               gtk_box_set_homogeneous(recv, omogeneous);
+       fun homogeneous=(homogeneous: Bool) `{
+               gtk_box_set_homogeneous(recv, homogeneous);
        `}
 
        # Add `child` and pack it at the start of the box
index 036c2bb..ec2de93 100644 (file)
@@ -29,3 +29,52 @@ extern class GtkSearchEntry `{GtkSearchEntry *`}
                return (GtkSearchEntry *)gtk_search_entry_new();
        `}
 end
+
+redef extern class GtkEntry
+       # Purpose of this text field
+       #
+       # Can be used by on-screen keyboards and other input methods to adjust their behaviour.
+       fun input_purpose: GtkInputPurpose `{
+               return gtk_entry_get_input_purpose(recv);
+       `}
+
+       # Input purpose, tweaks the behavior of this widget
+       #
+       # Can be used by on-screen keyboards and other input methods to adjust their behaviour.
+       fun input_purpose=(purpose: GtkInputPurpose) `{
+               gtk_entry_set_input_purpose(recv, purpose);
+       `}
+end
+
+# Describe the purpose of an input widget
+extern class GtkInputPurpose `{ GtkInputPurpose `}
+       # Allow any character
+       new free_form `{ return GTK_INPUT_PURPOSE_FREE_FORM; `}
+
+       # Allow only alphabetic characters
+       new alpha `{ return GTK_INPUT_PURPOSE_ALPHA; `}
+
+       # Allow only digits
+       new digits `{ return GTK_INPUT_PURPOSE_DIGITS; `}
+
+       # Edited field expects numbers
+       new number `{ return GTK_INPUT_PURPOSE_NUMBER; `}
+
+       # Edited field expects phone number
+       new phone `{ return GTK_INPUT_PURPOSE_PHONE; `}
+
+       # Edited field expects URL
+       new url `{ return GTK_INPUT_PURPOSE_URL; `}
+
+       # Edited field expects email address
+       new email `{ return GTK_INPUT_PURPOSE_EMAIL; `}
+
+       # Edited field expects the name of a person
+       new name `{ return GTK_INPUT_PURPOSE_NAME; `}
+
+       # Like `free_form`, but characters are hidden
+       new password `{ return GTK_INPUT_PURPOSE_PASSWORD; `}
+
+       # Like `digits`, but characters are hidden
+       new pin `{ return GTK_INPUT_PURPOSE_PIN; `}
+end
index e8c76b4..b9667db 100644 (file)
@@ -42,7 +42,7 @@ class JsonSerializer
 
        redef fun serialize_reference(object)
        do
-               if refs_map.keys.has(object) then
+               if refs_map.has_key(object) then
                        # if already serialized, add local reference
                        var id = ref_id_for(object)
                        stream.write "\{\"__kind\": \"ref\", \"__id\": {id}\}"
@@ -53,12 +53,12 @@ class JsonSerializer
        end
 
        # Map of references to already serialized objects.
-       var refs_map = new HashMap[Serializable,Int]
+       private var refs_map = new StrictHashMap[Serializable,Int]
 
        # Get the internal serialized reference for this `object`.
        private fun ref_id_for(object: Serializable): Int
        do
-               if refs_map.keys.has(object) then
+               if refs_map.has_key(object) then
                        return refs_map[object]
                else
                        var id = refs_map.length
@@ -81,8 +81,8 @@ class JsonDeserializer
        # Depth-first path in the serialized object tree.
        var path = new Array[JsonObject]
 
-       # Map of refenrences to already deserialized objects.
-       var id_to_object = new HashMap[Int, Object]
+       # Map of references to already deserialized objects.
+       private var id_to_object = new StrictHashMap[Int, Object]
 
        # Last encountered object reference id.
        #
@@ -111,7 +111,7 @@ class JsonDeserializer
        redef fun notify_of_creation(new_object)
        do
                var id = just_opened_id
-               assert id != null
+               if id == null then return # Register `new_object` only once
                id_to_object[id] = new_object
        end
 
@@ -128,7 +128,7 @@ class JsonDeserializer
                                var id = object["__id"]
                                assert id isa Int
 
-                               assert id_to_object.keys.has(id)
+                               assert id_to_object.has_key(id)
                                return id_to_object[id]
                        end
 
@@ -142,7 +142,7 @@ class JsonDeserializer
                                var class_name = object["__class"]
                                assert class_name isa String
 
-                               assert not id_to_object.keys.has(id) else print "Error: Object with id '{id}' is deserialized twice."
+                               assert not id_to_object.has_key(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice."
 
                                # advance on path
                                path.push object
@@ -219,49 +219,46 @@ redef class NativeString
        redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
 end
 
-redef class Array[E]
-       redef fun serialize_to_json(v)
+redef class Collection[E]
+       # Utility to serialize a normal Json array
+       private fun serialize_to_pure_json(v: JsonSerializer)
        do
-               if class_name == "Array[nullable Serializable]" then
-                       # Using class_name to the the exact type
-                       # We do not want Array[Int] or anything else here
                        v.stream.write "["
                        var is_first = true
                        for e in self do
                                if is_first then
                                        is_first = false
-                               else v.stream.write(", ")
+                               else v.stream.write ", "
 
                                if not v.try_to_serialize(e) then
                                        v.warn("element of type {e.class_name} is not serializable.")
                                end
                        end
                        v.stream.write "]"
-               else
-                       # Register as pseudo object
-                       var id = v.ref_id_for(self)
-                       v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\""
-                       v.stream.write """, "__length": {{{length}}}, "__items": ["""
-                       var is_first = true
-                       for e in self do
-                               if is_first then
-                                       is_first = false
-                               else v.stream.write(", ")
+       end
+end
 
-                               if not v.try_to_serialize(e) then
-                                       v.warn("element of type {e.class_name} is not serializable.")
-                               end
-                       end
-                       v.stream.write "]"
-                       v.stream.write "\}"
-               end
+redef class SimpleCollection[E]
+       redef fun serialize_to_json(v)
+       do
+               # Register as pseudo object
+               var id = v.ref_id_for(self)
+               v.stream.write """{"__kind": "obj", "__id": """
+               v.stream.write id.to_s
+               v.stream.write """, "__class": """"
+               v.stream.write class_name
+               v.stream.write """", "__length": """
+               v.stream.write length.to_s
+               v.stream.write """, "__items": """
+               serialize_to_pure_json v
+               v.stream.write "\}"
        end
 
-       # Instanciate a new `Array` from its serialized representation.
-       init from_deserializer(v: Deserializer)
+       redef init from_deserializer(v: Deserializer)
        do
                if v isa JsonDeserializer then
                        v.notify_of_creation self
+                       init
 
                        var length = v.deserialize_attribute("__length").as(Int)
                        var arr = v.path.last["__items"].as(SequenceRead[nullable Object])
@@ -272,3 +269,112 @@ redef class Array[E]
                end
        end
 end
+
+redef class Array[E]
+       redef fun serialize_to_json(v)
+       do
+               if class_name == "Array[nullable Serializable]" then
+                       # Using class_name to get the exact type,
+                       # we do not want Array[Int] or anything else here.
+
+                       serialize_to_pure_json v
+               else super
+       end
+end
+
+redef class Map[K, V]
+       redef fun serialize_to_json(v)
+       do
+               # Register as pseudo object
+               var id = v.ref_id_for(self)
+
+               v.stream.write """{"__kind": "obj", "__id": """
+               v.stream.write id.to_s
+               v.stream.write """, "__class": """"
+               v.stream.write class_name
+               v.stream.write """", "__length": """
+               v.stream.write length.to_s
+               v.stream.write """, "__keys": """
+
+               keys.serialize_to_pure_json v
+
+               v.stream.write """, "__values": """
+               values.serialize_to_pure_json v
+               v.stream.write "\}"
+       end
+
+       # Instantiate a new `Array` from its serialized representation.
+       redef init from_deserializer(v: Deserializer)
+       do
+               init
+
+               if v isa JsonDeserializer then
+                       v.notify_of_creation self
+
+                       var length = v.deserialize_attribute("__length").as(Int)
+                       var keys = v.path.last["__keys"].as(SequenceRead[nullable Object])
+                       var values = v.path.last["__values"].as(SequenceRead[nullable Object])
+                       for i in length.times do
+                               var key = v.convert_object(keys[i])
+                               var value = v.convert_object(values[i])
+                               self[key] = value
+                       end
+               end
+       end
+end
+
+# Maps instances to a value, uses `is_same_instance`
+#
+# Warning: This class does not implement all the services from `Map`.
+private class StrictHashMap[K, V]
+       super Map[K, V]
+
+       # private
+       var map = new HashMap[K, Array[Couple[K, V]]]
+
+       redef var length = 0
+
+       redef fun is_empty do return length == 0
+
+       # private
+       fun node_at(key: K): nullable Couple[K, V]
+       do
+               if not map.keys.has(key) then return null
+
+               var arr = map[key]
+               for couple in arr do
+                       if couple.first.is_same_serialized(key) then
+                               return couple
+                       end
+               end
+
+               return null
+       end
+
+       redef fun [](key)
+       do
+               var node = node_at(key)
+               assert node != null
+               return node.second
+       end
+
+       redef fun []=(key, value)
+       do
+               var node = node_at(key)
+               if node != null then
+                       node.second = value
+                       return
+               end
+
+               var arr
+               if not map.keys.has(key) then
+                       arr = new Array[Couple[K, V]]
+                       map[key] = arr
+               else arr = map[key]
+
+               arr.add new Couple[K, V](key, value)
+               self.length += 1
+       end
+
+       redef fun has_key(key) do return node_at(key) != null
+end
diff --git a/lib/linux/ui.nit b/lib/linux/ui.nit
new file mode 100644 (file)
index 0000000..4f67eed
--- /dev/null
@@ -0,0 +1,139 @@
+# 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.
+
+# Implementation of the app.nit UI module for GNU/Linux
+module ui
+
+import app::ui
+import gtk
+
+import data_store
+
+redef class App
+       redef fun setup do init_gtk
+
+       # On GNU/Linux, we go through all the callbacks once,
+       # there is no complex life-cycle.
+       redef fun run
+       do
+               app.on_create
+               app.on_restore_state
+               app.on_start
+               app.on_resume
+
+               var window = window
+               window.native.show_all
+               run_gtk
+
+               app.on_pause
+               app.on_stop
+               app.on_save_state
+               app.on_destroy
+       end
+
+       # Spacing between GTK controls, default at 2
+       var control_spacing = 2 is writable
+end
+
+redef class Control
+       super GtkCallable
+       super Finalizable
+
+       # The GTK element used to implement `self`
+       fun native: NATIVE is abstract
+
+       # Type of `native`
+       type NATIVE: GtkWidget
+
+       redef fun finalize
+       do
+               var native = native
+               if not native.address_is_null then native.destroy
+       end
+end
+
+redef class CompositeControl
+       redef type NATIVE: GtkContainer
+
+       redef fun add(item)
+       do
+               super
+               native.add item.native
+       end
+end
+
+redef class Window
+       redef type NATIVE: GtkWindow
+       redef var native do
+               var win = new GtkWindow(new GtkWindowType.toplevel)
+               win.connect_destroy_signal_to_quit
+               return win
+       end
+end
+
+redef class View
+       redef fun enabled do return native.sensitive
+       redef fun enabled=(enabled) do native.sensitive = enabled or else true
+end
+
+redef class Layout
+       redef type NATIVE: GtkBox
+end
+
+redef class HorizontalLayout
+       redef var native = new GtkBox(new GtkOrientation.horizontal, app.control_spacing)
+
+       redef fun add(item)
+       do
+               super
+               native.homogeneous = true
+               native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
+       end
+end
+
+redef class VerticalLayout
+       redef var native = new GtkBox(new GtkOrientation.vertical, app.control_spacing)
+
+       redef fun add(item)
+       do
+               super
+
+               # FIXME abstract the use either homogeneous or weight to balance views size in a layout
+               native.homogeneous = true
+               native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
+       end
+end
+
+redef class Button
+       redef type NATIVE: GtkButton
+       redef var native = new GtkButton
+
+       redef fun text do return native.text
+       redef fun text=(value) do native.text = (value or else "").to_s
+
+       redef fun signal(sender, data) do notify_observers new ButtonPressEvent(self)
+
+       init do native.signal_connect("clicked", self, null)
+end
+
+redef class TextInput
+       redef type NATIVE: GtkEntry
+       redef var native = new GtkEntry
+
+       redef fun text do return native.text
+       redef fun text=(value) do
+               if value == null then value = ""
+               native.text = value.to_s
+       end
+end
index 39063f2..2ffa800 100644 (file)
@@ -133,7 +133,7 @@ abstract class NeoNodeCollection
                for node in self do remove_node(node)
        end
 
-       redef fun remove(node: NeoNode) do
+       redef fun remove(node) do
                for n in self do
                        if node == n then
                                remove_node(n)
@@ -142,7 +142,7 @@ abstract class NeoNodeCollection
                end
        end
 
-       redef fun remove_all(node: NeoNode) do
+       redef fun remove_all(node) do
                for n in self do
                        if node == n then remove_node(n)
                end
index 31f98f0..6e5ce12 100644 (file)
@@ -63,7 +63,7 @@ interface Serializer
        fun serialize_attribute(name: String, value: nullable Object)
        do
                if not try_to_serialize(value) then
-                       warn("argument {value.class_name}::{name} is not serializable.")
+                       warn("argument {name} of type {value.class_name} is not serializable.")
                end
        end
 
@@ -134,6 +134,18 @@ interface Serializable
        # The subclass change the default behavior, which will accept references,
        # to force to always serialize copies of `self`.
        private fun serialize_to_or_delay(v: Serializer) do v.serialize_reference(self)
+
+       # Create an instance of this class from the `deserializer`
+       #
+       # This constructor is refined by subclasses to correctly build their instances.
+       init from_deserializer(deserializer: Deserializer) do end
+end
+
+redef interface Object
+       # Is `self` the same as `other` in a serialization context?
+       #
+       # Used to determine if an object has already been serialized.
+       fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
 end
 
 # Instances of this class are not delayed and instead serialized immediately
@@ -150,4 +162,5 @@ redef class Int super DirectSerializable end
 redef class Float super DirectSerializable end
 redef class NativeString super DirectSerializable end
 redef class String super DirectSerializable end
-redef class Array[E] super Serializable end
+redef class SimpleCollection[E] super Serializable end
+redef class Map[K, V] super Serializable end
index 681e239..b3cb2f9 100644 (file)
@@ -147,6 +147,12 @@ class TCPStream
                socket.write(msg.to_s)
        end
 
+       redef fun write_byte(value)
+       do
+               if closed then return
+               socket.write_byte value
+       end
+
        fun write_ln(msg: Text)
        do
                if end_reached then return
index 8758cdf..a574485 100644 (file)
@@ -132,6 +132,11 @@ extern class NativeSocket `{ int* `}
                return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer));
        `}
 
+       # Write `value` as a single byte
+       fun write_byte(value: Int): Int `{
+               return write(*recv, &value, 1);
+       `}
+
        fun read: String import NativeString.to_s_with_length `{
                static char c[1024];
                int n = read(*recv, c, 1024);
index 2f7e454..8b01780 100644 (file)
@@ -92,7 +92,7 @@ interface Collection[E]
        #     assert [1,2,3].has(9)    == false
        #     assert [1..5[.has(2)     == true
        #     assert [1..5[.has(9)     == false
-       fun has(item: E): Bool
+       fun has(item: nullable Object): Bool
        do
                for i in self do if i == item then return true
                return false
@@ -109,7 +109,7 @@ interface Collection[E]
        #     assert [3..3[.has_only(1)          == true # empty collection
        #
        # ENSURE `is_empty implies result == true`
-       fun has_only(item: E): Bool
+       fun has_only(item: nullable Object): Bool
        do
                for i in self do if i != item then return false
                return true
@@ -119,7 +119,7 @@ interface Collection[E]
        # Comparisons are done with ==
        #
        #     assert [10,20,10].count(10)         == 2
-       fun count(item: E): Int
+       fun count(item: nullable Object): Int
        do
                var nb = 0
                for i in self do if i == item then nb += 1
@@ -147,7 +147,7 @@ interface Collection[E]
        #
        # Note that the default implementation is general and correct for any lawful Collections.
        # It is memory-efficient but relies on `has` so may be CPU-inefficient for some kind of collections.
-       fun has_all(other: Collection[E]): Bool
+       fun has_all(other: Collection[nullable Object]): Bool
        do
                for x in other do if not has(x) then return false
                return true
@@ -171,7 +171,7 @@ interface Collection[E]
        #
        # Note that the default implementation is general and correct for any lawful Collections.
        # It is memory-efficient but relies on `count` so may be CPU-inefficient for some kind of collections.
-       fun has_exactly(other: Collection[E]): Bool
+       fun has_exactly(other: Collection[nullable Object]): Bool
        do
                if length != other.length then return false
                for e in self do if self.count(e) != other.count(e) then return false
@@ -191,6 +191,37 @@ interface Iterator[E]
        # Require `is_ok`.
        fun next is abstract
 
+       # Jump to the next item `step` times.
+       #
+       # ~~~
+       # var i = [11, 22, 33, 44].iterator
+       # assert i.item == 11
+       # i.next_by 2
+       # assert i.item == 33
+       # ~~~
+       #
+       # `next_by` should be used instead of looping on `next` because is takes care
+       # of stopping if the end of iteration is reached prematurely whereas a loop of
+       # `next` will abort because of the precondition on `is_ok`.
+       #
+       # ~~~
+       # i.next_by 100
+       # assert not i.is_ok
+       # ~~~
+       #
+       # If `step` is negative, this method aborts.
+       # But specific subclasses can change this and do something more meaningful instead.
+       #
+       # Require `is_ok`
+       fun next_by(step: Int)
+       do
+               assert step >= 0
+               while is_ok and step > 0 do
+                       next
+                       step -= 1
+               end
+       end
+
        # Is there a current item ?
        fun is_ok: Bool is abstract
 
@@ -206,6 +237,43 @@ interface Iterator[E]
        #
        # Do nothing by default.
        fun finish do end
+
+       # A decorator around `self` that advance self a given number of steps instead of one.
+       #
+       # ~~~
+       # var i = [11, 22, 33, 44, 55].iterator
+       # var i2 = i.to_step(2)
+       #
+       # assert i2.item == 11
+       # i2.next
+       # assert i2.item == 33
+       #
+       # assert i.item == 33
+       # ~~~
+       fun to_step(step: Int): Iterator[E] do return new StepIterator[E](self, step)
+end
+
+# A basic helper class to specialize specific Iterator decorators
+abstract class IteratorDecorator[E]
+       super Iterator[E]
+
+       # The underling iterator
+       protected var real: Iterator[E]
+
+       redef fun is_ok do return real.is_ok
+       redef fun item do return real.item
+       redef fun finish do real.finish
+       redef fun next do real.next
+       redef fun next_by(step) do real.next_by(step)
+end
+
+# A decorator that advance a given number of steps
+private class StepIterator[E]
+       super IteratorDecorator[E]
+       var step: Int
+
+       redef fun next do real.next_by(step)
+       redef fun next_by(step) do real.next_by(step * self.step)
 end
 
 # A collection that contains only one item.
@@ -267,19 +335,19 @@ interface RemovableCollection[E]
        # ENSURE `is_empty`
        fun clear is abstract
 
-       # Remove an occucence of `item`
+       # Remove an occurrence of `item`
        #
        #     var a = [1,2,3,1,2,3]
        #     a.remove 2
        #     assert a == [1,3,1,2,3]
-       fun remove(item: E) is abstract
+       fun remove(item: nullable Object) is abstract
 
-       # Remove all occurences of `item`
+       # Remove all occurrences of `item`
        #
        #     var a = [1,2,3,1,2,3]
        #     a.remove_all 2
        #     assert a == [1,3,1,3]
-       fun remove_all(item: E) do while has(item) do remove(item)
+       fun remove_all(item: nullable Object) do while has(item) do remove(item)
 end
 
 # Items can be added to these collections.
@@ -345,7 +413,7 @@ interface Set[E]
        # Equality is defined on set and means that each set contains the same elements
        redef fun ==(other)
        do
-               if not other isa Set[Object] then return false
+               if not other isa Set[nullable Object] then return false
                if other.length != length then return false
                return has_all(other)
        end
@@ -396,7 +464,7 @@ interface MapRead[K, V]
        #
        # If the key is not in the map, `provide_default_value` is called (that aborts by default)
        # See `get_or_null` and `get_or_default` for safe variations.
-       fun [](key: K): V is abstract
+       fun [](key: nullable Object): V is abstract
 
        # Get the item at `key` or null if `key` is not in the map.
        #
@@ -406,7 +474,7 @@ interface MapRead[K, V]
        #     assert x.get_or_null("five") == null
        #
        # Note: use `has_key` and `[]` if you need the distinction between a key associated with null, and no key.
-       fun get_or_null(key: K): nullable V
+       fun get_or_null(key: nullable Object): nullable V
        do
                if has_key(key) then return self[key]
                return null
@@ -419,7 +487,7 @@ interface MapRead[K, V]
        #     assert x.get_or_default("four", 40) == 4
        #     assert x.get_or_default("five", 50) == 50
        #
-       fun get_or_default(key: K, default: V): V
+       fun get_or_default(key: nullable Object, default: V): V
        do
                if has_key(key) then return self[key]
                return default
@@ -433,7 +501,7 @@ interface MapRead[K, V]
        #     assert x.has_key("five") == false
        #
        # By default it is a synonymous to `keys.has` but could be redefined with a direct implementation.
-       fun has_key(key: K): Bool do return self.keys.has(key)
+       fun has_key(key: nullable Object): Bool do return self.keys.has(key)
 
        # Get a new iterator on the map.
        fun iterator: MapIterator[K, V] is abstract
@@ -481,7 +549,7 @@ interface MapRead[K, V]
        #
        # Note: the value is returned *as is*, implementations may want to store the value in the map before returning it
        # @toimplement
-       protected fun provide_default_value(key: K): V do abort
+       protected fun provide_default_value(key: nullable Object): V do abort
 
        # Does `self` and `other` have the same keys associated with the same values?
        #
@@ -716,7 +784,7 @@ interface SequenceRead[E]
        #     var a = [10,20,30,10,20,30]
        #     assert a.index_of(20)   == 1
        #     assert a.index_of(40)   == -1
-       fun index_of(item: E): Int do return index_of_from(item, 0)
+       fun index_of(item: nullable Object): Int do return index_of_from(item, 0)
 
        # The index of the last occurrence of `item`.
        # Return -1 if `item` is not found.
@@ -725,7 +793,7 @@ interface SequenceRead[E]
        #     var a = [10,20,30,10,20,30]
        #     assert a.last_index_of(20)   == 4
        #     assert a.last_index_of(40)   == -1
-       fun last_index_of(item: E): Int do return last_index_of_from(item, length-1)
+       fun last_index_of(item: nullable Object): Int do return last_index_of_from(item, length-1)
 
        # The index of the first occurrence of `item`, starting from pos.
        # Return -1 if `item` is not found.
@@ -735,7 +803,7 @@ interface SequenceRead[E]
        #     assert a.index_of_from(20, 3)   == 4
        #     assert a.index_of_from(20, 4)   == 4
        #     assert a.index_of_from(20, 5)   == -1
-       fun index_of_from(item: E, pos: Int): Int
+       fun index_of_from(item: nullable Object, pos: Int): Int
        do
                var p = 0
                var i = iterator
@@ -755,7 +823,7 @@ interface SequenceRead[E]
        #     assert a.last_index_of_from(20, 2)   == 1
        #     assert a.last_index_of_from(20, 1)   == 1
        #     assert a.last_index_of_from(20, 0)   == -1
-       fun last_index_of_from(item: E, pos: Int): Int
+       fun last_index_of_from(item: nullable Object, pos: Int): Int
        do
                var res = -1
                var p = 0
@@ -1005,7 +1073,7 @@ interface CoupleMap[K, V]
 
        # Return the couple of the corresponding key
        # Return null if the key is no associated element
-       protected fun couple_at(key: K): nullable Couple[K, V] is abstract
+       protected fun couple_at(key: nullable Object): nullable Couple[K, V] is abstract
 
        # Return a new iteralot on all couples
        # Used to provide `iterator` and others
index cdb5242..b3ec382 100644 (file)
@@ -63,9 +63,9 @@ abstract class AbstractArrayRead[E]
 
        redef fun index_of(item) do return index_of_from(item, 0)
 
-       redef fun last_index_of(item: E): Int do return last_index_of_from(item, length-1)
+       redef fun last_index_of(item) do return last_index_of_from(item, length-1)
 
-       redef fun index_of_from(item: E, pos: Int): Int
+       redef fun index_of_from(item, pos)
        do
                var i = pos
                var len = length
@@ -78,7 +78,7 @@ abstract class AbstractArrayRead[E]
                return -1
        end
 
-       redef fun last_index_of_from(item: E, pos: Int): Int
+       redef fun last_index_of_from(item, pos)
        do
                var i = pos
                while i >= 0 do
index e5ff507..961e560 100644 (file)
@@ -32,13 +32,13 @@ private abstract class HashCollection[K]
        var last_item: nullable N = null # Last added item (same)
 
        # The last key accessed (used for cache)
-       var last_accessed_key: nullable K = null
+       var last_accessed_key: nullable Object = null
 
        # The last node accessed (used for cache)
        var last_accessed_node: nullable N = null
 
        # Return the index of the key k
-       fun index_at(k: K): Int
+       fun index_at(k: nullable Object): Int
        do
                if k == null then return 0
 
@@ -48,7 +48,7 @@ private abstract class HashCollection[K]
        end
 
        # Return the node associated with the key
-       fun node_at(k: K): nullable N
+       fun node_at(k: nullable Object): nullable N
        do
                # cache: `is` is used instead of `==` because it is a faster filter (even if not exact)
                if k.is_same_instance(_last_accessed_key) then return _last_accessed_node
@@ -60,7 +60,7 @@ private abstract class HashCollection[K]
        end
 
        # Return the node associated with the key (but with the index already known)
-       fun node_at_idx(i: Int, k: K): nullable N
+       fun node_at_idx(i: Int, k: nullable Object): nullable N
        do
                var c = _array[i]
                while c != null do
@@ -109,7 +109,7 @@ private abstract class HashCollection[K]
        end
 
        # Remove the node assosiated with the key
-       fun remove_node(k: K)
+       fun remove_node(k: nullable Object)
        do
                var i = index_at(k)
                var node = node_at_idx(i, k)
index 1ad0309..216b676 100644 (file)
@@ -30,7 +30,7 @@ class Range[E: Discrete]
        #     assert [1..10].has(5)
        #     assert [1..10].has(10)
        #     assert not [1..10[.has(10)
-       redef fun has(item) do return item >= first and item <= last
+       redef fun has(item) do return item isa Comparable and item >= first and item <= last
 
        #     assert [1..1].has_only(1)
        #     assert not [1..10].has_only(1)
@@ -101,8 +101,13 @@ class Range[E: Discrete]
        init without_last(from: E, to: E)
        do
                first = from
-               last = to.predecessor(1)
-               after = to
+               if from <= to then
+                       last = to.predecessor(1)
+                       after = to
+               else
+                       last = to.successor(1)
+                       after = to
+               end
        end
 
        # Two ranges are equals if they have the same first and last elements.
@@ -127,6 +132,60 @@ class Range[E: Discrete]
                # 11 and 23 are magic numbers empirically determined to be not so bad.
                return first.hash * 11 + last.hash * 23
        end
+
+       # Gets an iterator that progress with a given step.
+       #
+       # The main usage is in `for` construction.
+       #
+       # ~~~
+       # for i in [10..25].step(10) do assert i == 10 or i == 20
+       # ~~~
+       #
+       # But `step` is usable as any kind of iterator.
+       #
+       # ~~~
+       # assert [10..27].step(5).to_a == [10,15,20,25]
+       # ~~~
+       #
+       # If `step == 1`, then it is equivalent to the default `iterator`.
+       #
+       # ~~~
+       # assert [1..5].step(1).to_a == [1..5].to_a
+       # ~~~
+       #
+       # If `step` is negative, then the iterator will iterate on ranges whose `first` > `last`.
+       #
+       # ~~~
+       # assert [25..12].step(-5).to_a == [25,20,15]
+       # ~~~
+       #
+       # On such ranges, the default `iterator` will be empty
+       #
+       # ~~~
+       # assert [5..1].step(1).to_a.is_empty
+       # assert [5..1].iterator.to_a.is_empty
+       # assert [5..1].to_a.is_empty
+       # assert [5..1].is_empty
+       # ~~~
+       #
+       # Note that on non-empty range, iterating with a negative step will be empty
+       #
+       # ~~~
+       # assert [1..5].step(-1).to_a.is_empty
+       # ~~~
+       fun step(step: Int): Iterator[E]
+       do
+               var i
+               if step >= 0 then
+                       i = iterator
+               else
+                       i = new DowntoIteratorRange[E](self)
+                       step = -step
+               end
+
+               if step == 1 then return i
+               return i.to_step(step)
+       end
 end
 
 # Iterator on ranges.
@@ -161,6 +220,23 @@ private class ReverseIteratorRange[E: Discrete]
        end
 end
 
+# Iterator on ranges.
+private class DowntoIteratorRange[E: Discrete]
+       super IndexedIterator[E]
+       var range: Range[E]
+       redef var item is noinit
+       redef fun index do return _item.distance(_range.first)
+
+       redef fun is_ok do return _item >= _range.last
+
+       redef fun next do _item = _item.predecessor(1)
+
+       init
+       do
+               _item = _range.first
+       end
+end
+
 redef class Int
        # Returns the range from 0 to `self-1`, is used to do:
        #
index 12c9c86..f4c6baa 100644 (file)
@@ -115,7 +115,7 @@ class DisjointSet[E]
        #     s.add(1)
        #     assert s.has(1)
        #     assert not s.has(2)
-       redef fun has(e: E): Bool
+       redef fun has(e)
        do
                return nodes.has_key(e)
        end
index 1b81877..5324f01 100644 (file)
@@ -193,6 +193,26 @@ class FileWriter
                end
        end
 
+       redef fun write_byte(value)
+       do
+               if last_error != null then return
+               if not _is_writable then
+                       last_error = new IOError("Cannot write to non-writable stream")
+                       return
+               end
+               if _file.address_is_null then
+                       last_error = new IOError("Writing on a null stream")
+                       _is_writable = false
+                       return
+               end
+
+               var err = _file.write_byte(value)
+               if err != 1 then
+                       # Big problem
+                       last_error = new IOError("Problem writing a byte: {err}")
+               end
+       end
+
        redef fun close
        do
                super
@@ -1090,6 +1110,10 @@ end
 private extern class NativeFile `{ FILE* `}
        fun io_read(buf: NativeString, len: Int): Int is extern "file_NativeFile_NativeFile_io_read_2"
        fun io_write(buf: NativeString, len: Int): Int is extern "file_NativeFile_NativeFile_io_write_2"
+       fun write_byte(value: Int): Int `{
+               unsigned char b = (unsigned char)value;
+               return fwrite(&b, 1, 1, recv);
+       `}
        fun io_close: Int is extern "file_NativeFile_NativeFile_io_close_0"
        fun file_stat: NativeFileStat is extern "file_NativeFile_NativeFile_file_stat_0"
        fun fileno: Int `{ return fileno(recv); `}
index 3f32a62..85386b4 100644 (file)
@@ -63,7 +63,7 @@ interface Object
 
        # Have `self` and `other` the same value?
        #
-       # The exact meaning of "same value" is let to the subclasses.
+       # The exact meaning of "same value" is left to the subclasses.
        # Implicitly, the default implementation, is `is_same_instance`
        fun ==(other: nullable Object): Bool do return self.is_same_instance(other)
 
@@ -85,7 +85,7 @@ interface Object
        # Display class name on stdout (debug only).
        # This method MUST not be used by programs, it is here for debugging
        # only and can be removed without any notice
-       fun output_class_name is intern 
+       fun output_class_name is intern
 
        # The hash code of the object.
        # Assuming that a == b -> a.hash == b.hash
@@ -223,11 +223,11 @@ end
 # Something that can be cloned
 #
 # This interface introduces the `clone` method used to duplicate an instance
-# Its specific semantic is let to the subclasses.
+# Its specific semantic is left to the subclasses.
 interface Cloneable
        # Duplicate `self`
        #
-       # The specific semantic of this method is let to the subclasses;
+       # The specific semantic of this method is left to the subclasses;
        # Especially, if (and how) attributes are cloned (depth vs. shallow).
        #
        # As a rule of thumb, the principle of least astonishment should
index 6b6d3fe..7c4e606 100644 (file)
@@ -345,6 +345,9 @@ abstract class Writer
        # write a string
        fun write(s: Text) is abstract
 
+       # Write a single byte
+       fun write_byte(value: Int) is abstract
+
        # Can the stream be used to write
        fun is_writable: Bool is abstract
 end
index 6b6b91c..610ec2e 100644 (file)
@@ -64,7 +64,7 @@ class BinTreeMap[K: Comparable, E]
        #     assert not tree.has_key(0)
        #     assert tree.has_key(2)
        #     assert not tree.has_key(6)
-       redef fun has_key(key: K): Bool do
+       redef fun has_key(key) do
                if is_empty then return false
                var res = search_down(root.as(not null), key)
                if res != null then
@@ -85,7 +85,7 @@ class BinTreeMap[K: Comparable, E]
        #     assert tree[1] == "n1"
        #     assert tree.has_key(1)
        #     assert tree[2] == "n2"
-       redef fun [](key: K): E do
+       redef fun [](key) do
                assert not_empty: not is_empty
                if cache_node != null and cache_node.key == key then return cache_node.value
                var res = search_down(root.as(not null), key)
@@ -94,7 +94,8 @@ class BinTreeMap[K: Comparable, E]
        end
 
        # Search `key` in `from` and its children nodes.
-       protected fun search_down(from: N, key: K): nullable N do
+       protected fun search_down(from: N, key: nullable Object): nullable N do
+               if not key isa Comparable then return null
                var cmp = key <=> from.key
                if cmp == 0 then return from
                if from.left != null and cmp < 0 then
diff --git a/src/doc/console_templates/console_model.nit b/src/doc/console_templates/console_model.nit
new file mode 100644 (file)
index 0000000..8de793c
--- /dev/null
@@ -0,0 +1,638 @@
+# 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.
+
+# Console templates for Nit model MEntities.
+#
+# This module introduces console rendering services in model entities.
+module console_model
+
+import console
+import doc_base
+import ordered_tree
+
+redef class MDoc
+       # Returns the full comment formatted for the console.
+       fun cs_comment: String do
+               var res = new FlatBuffer
+               for line in content do
+                       res.append "    {line}\n"
+               end
+               return res.write_to_string
+       end
+
+       # Returns the synopsys formatted for the console.
+       fun cs_short_comment: String do return content.first
+end
+
+redef class MEntity
+
+       # Returns the mentity name with short signature.
+       #
+       # * MProject: `foo`
+       # * MGroup: `foo`
+       # * MModule: `foo`
+       # * MClass: `Foo[E]`
+       # * MClassDef: `Foo[E]`
+       # * MProperty: `foo(e)`
+       # * MPropdef: `foo(e)`
+       var cs_name: String is lazy do return name
+
+       # Returns the list of keyword used in `self` declaration.
+       fun cs_modifiers: Array[String] is abstract
+
+       # Returns the complete MEntity declaration (modifiers + name + signature).
+       #
+       # * MProject: `project foo`
+       # * MGroup: `group foo`
+       # * MModule: `module foo`
+       # * MClass: `private abstract class Foo[E: Object]`
+       # * MClassDef: `redef class Foo[E]`
+       # * MProperty: `private fun foo(e: Object): Int`
+       # * MPropdef: `redef fun foo(e)`
+       var cs_declaration: String is lazy do
+               var tpl = new FlatBuffer
+               tpl.append cs_modifiers.join(" ")
+               tpl.append " "
+               tpl.append cs_name
+               return tpl.write_to_string
+       end
+
+       # Returns `self` namespace formatted for console.
+       #
+       # * MProject: `mproject`
+       # * MGroup: `mproject(::group)`
+       # * MModule: `mgroup::mmodule`
+       # * MClass: `mproject::mclass`
+       # * MClassDef: `mmodule::mclassdef`
+       # * MProperty: `mclass::mprop`
+       # * MPropdef: `mclassdef:mpropdef`
+       fun cs_namespace: String is abstract
+
+       # Returns the comment of this MEntity formatted for console.
+       var cs_comment: nullable String is lazy do
+               var mdoc = mdoc_or_fallback
+               if mdoc == null then return null
+               # FIXME add markdown for console
+               return mdoc.cs_comment
+       end
+
+       # Returns the comment of this MEntity formatted for console.
+       var cs_short_comment: nullable String is lazy do
+               var mdoc = mdoc_or_fallback
+               if mdoc == null then return null
+               return mdoc.cs_short_comment
+       end
+
+       # Returns 1self` as a list element that can be displayed in console.
+       fun cs_list_item: String do
+               var tpl = new FlatBuffer
+               tpl.append " {cs_visibility_color(cs_icon).bold} {cs_name.blue.bold}"
+               var comment = cs_short_comment
+               if comment != null then
+                       tpl.append " # {comment}".green
+               end
+               tpl.append "\n   "
+               tpl.append cs_namespace.gray.bold
+               tpl.append "\n   "
+               tpl.append cs_declaration
+               tpl.append "\n   "
+               tpl.append cs_location.gray
+               return tpl.write_to_string
+       end
+
+       # ASCII icon to be displayed in front of the list item.
+       fun cs_icon: String do return "*"
+
+       # Source code location of this MEntity formatted for console.
+       fun cs_location: String is abstract
+
+       # Sets text color depending on visibility.
+       #
+       # See module `console`.
+       fun cs_visibility_color(string: String): String do return string.green
+end
+
+redef class MProject
+       redef var cs_modifiers = ["project"]
+       redef fun cs_namespace do return cs_name
+       redef fun cs_icon do return "P"
+       redef fun cs_location do return root.mmodules.first.location.to_s
+end
+
+redef class MGroup
+       redef var cs_modifiers = ["group"]
+       redef fun cs_icon do return "G"
+
+       # Depends if `self` is root or not.
+       #
+       # * If root `mproject`.
+       # * Else `mproject::self`.
+       redef fun cs_namespace do
+               var tpl = new FlatBuffer
+               tpl.append mproject.cs_namespace
+               if mproject.root != self then
+                       tpl.append "::"
+                       tpl.append cs_name
+               end
+               return tpl.write_to_string
+       end
+
+       redef fun cs_location do return mmodules.first.location.to_s
+end
+
+redef class MModule
+       redef var cs_modifiers = ["module"]
+       redef fun cs_icon do return "M"
+
+       # Depends if `self` belongs to a MGroup.
+       #
+       # * If mgroup `mgroup::self`.
+       # * Else `self`.
+       redef fun cs_namespace do
+               var tpl = new FlatBuffer
+               if mgroup != null then
+                       tpl.append mgroup.cs_namespace
+                       tpl.append "::"
+               end
+               tpl.append cs_name
+               return tpl.write_to_string
+       end
+
+       redef fun cs_location do return location.to_s
+end
+
+redef class MClass
+       redef fun mdoc_or_fallback do return intro.mdoc
+       redef fun cs_icon do return intro.cs_icon
+
+       # Format: `Foo[E]`
+       redef var cs_name is lazy do
+               var tpl = new FlatBuffer
+               tpl.append name
+               if arity > 0 then
+                       tpl.append "["
+                       var parameter_names = new Array[String]
+                       for p in mparameters do
+                               parameter_names.add(p.cs_name)
+                       end
+                       tpl.append parameter_names.join(", ")
+                       tpl.append "]"
+               end
+               return tpl.write_to_string
+       end
+
+       redef fun cs_modifiers do return intro.cs_modifiers
+       redef fun cs_declaration do return intro.cs_declaration
+
+       # Returns `mproject::self`.
+       redef fun cs_namespace do
+               var tpl = new FlatBuffer
+               tpl.append intro_mmodule.mgroup.mproject.cs_namespace
+               tpl.append "::"
+               tpl.append cs_name
+               return tpl.write_to_string
+       end
+
+       # Returns `intro.cs_short_signature`.
+       fun cs_short_signature: String do return intro.cs_short_signature
+
+       # Returns `intro.cs_signature`.
+       fun cs_signature: String do return intro.cs_signature
+
+       redef fun cs_visibility_color(string) do
+               if visibility == private_visibility then
+                       return string.red
+               else if visibility == protected_visibility then
+                       return string.yellow
+               end
+               return string.green
+       end
+
+       redef fun cs_location do return intro.location.to_s
+end
+
+redef class MClassDef
+       redef fun mdoc_or_fallback do return mdoc or else mclass.mdoc_or_fallback
+       redef fun cs_icon do return "C"
+
+       # Depends if `self` is an intro or not.
+       #
+       # * If intro contains the visibility and kind.
+       # * If redef contains the `redef` keyword and kind.
+       redef fun cs_modifiers do
+               var res = new Array[String]
+               if not is_intro then
+                       res.add "redef"
+               else
+                       if mclass.visibility != public_visibility then
+                               res.add mclass.visibility.to_s
+                       end
+               end
+               res.add mclass.kind.to_s
+               return res
+       end
+
+       # Depends if `self` is an intro or not.
+       #
+       # For intro: `private abstract class Foo[E: Object]`
+       # For redef: `redef class Foo[E]`
+       redef fun cs_declaration do
+               var tpl = new FlatBuffer
+               tpl.append cs_modifiers.join(" ")
+               tpl.append " "
+               tpl.append cs_name
+               if is_intro then
+                       tpl.append cs_signature
+               else
+                       tpl.append cs_short_signature
+               end
+               return tpl.write_to_string.write_to_string.write_to_string
+       end
+
+       # Returns `mmodule::self`
+       redef fun cs_namespace do
+               var tpl = new FlatBuffer
+               tpl.append mmodule.cs_namespace
+               tpl.append "::"
+               tpl.append mclass.cs_name
+               return tpl.write_to_string.write_to_string
+       end
+
+       # Returns the MClassDef generic signature without static bounds.
+       fun cs_short_signature: String do
+               var tpl = new FlatBuffer
+               var mparameters = mclass.mparameters
+               if not mparameters.is_empty then
+                       tpl.append "["
+                       for i in [0..mparameters.length[ do
+                               tpl.append mparameters[i].cs_name
+                               if i < mparameters.length - 1 then tpl.append ", "
+                       end
+                       tpl.append "]"
+               end
+               return tpl.write_to_string
+       end
+
+       # Returns the MClassDef generic signature with static bounds.
+       fun cs_signature: String do
+               var tpl = new FlatBuffer
+               var mparameters = mclass.mparameters
+               if not mparameters.is_empty then
+                       tpl.append "["
+                       for i in [0..mparameters.length[ do
+                               tpl.append "{mparameters[i].cs_name}: "
+                               tpl.append bound_mtype.arguments[i].cs_signature
+                               if i < mparameters.length - 1 then tpl.append ", "
+                       end
+                       tpl.append "]"
+               end
+               return tpl.write_to_string
+       end
+
+       redef fun cs_location do return location.to_s
+end
+
+redef class MProperty
+       redef fun mdoc_or_fallback do return intro.mdoc
+       redef fun cs_modifiers do return intro.cs_modifiers
+       redef fun cs_declaration do return intro.cs_declaration
+       redef fun cs_icon do return intro.cs_icon
+
+       # Returns `mclass::self`.
+       redef fun cs_namespace do
+               var tpl = new FlatBuffer
+               tpl.append intro_mclassdef.mclass.cs_namespace
+               tpl.append "::"
+               tpl.append intro.cs_name
+               return tpl.write_to_string
+       end
+
+       # Returns `intro.cs_short_signature`.
+       fun cs_short_signature: String do return intro.cs_short_signature
+
+       # Returns `intro.cs_signature`.
+       fun cs_signature: String do return intro.cs_signature
+
+       redef fun cs_visibility_color(string) do
+               if visibility == private_visibility then
+                       return string.red
+               else if visibility == protected_visibility then
+                       return string.yellow
+               end
+               return string.green
+       end
+
+       # Returns `intro.cs_location`.
+       redef fun cs_location do return intro.location.to_s
+end
+
+redef class MPropDef
+       redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback
+
+       # Depends if `self` is an intro or not.
+       #
+       # * If intro contains the visibility and kind.
+       # * If redef contains the `redef` keyword and kind.
+       redef fun cs_modifiers do
+               var res = new Array[String]
+               if not is_intro then
+                       res.add "redef"
+               else
+                       if mproperty.visibility != public_visibility then
+                               res.add mproperty.visibility.to_s
+                       end
+               end
+               return res
+       end
+
+       # Depends if `self` is an intro or not.
+       #
+       # For intro: `private fun foo(e: Object): Bar is abstract`
+       # For redef: `redef fun foo(e) is cached`
+       redef fun cs_declaration do
+               var tpl = new FlatBuffer
+               tpl.append cs_modifiers.join(" ")
+               tpl.append " "
+               if is_intro then
+                       tpl.append cs_name
+                       tpl.append cs_signature
+               else
+                       tpl.append mproperty.intro.cs_name
+                       tpl.append cs_short_signature
+               end
+               return tpl.write_to_string
+       end
+
+       # Returns `mclassdef::self`
+       redef fun cs_namespace do
+               var tpl = new FlatBuffer
+               tpl.append mclassdef.cs_namespace
+               tpl.append "::"
+               tpl.append cs_name
+               return tpl.write_to_string
+       end
+
+       redef fun cs_location do return location.to_s
+
+       # Returns the MPropdDef signature without static types.
+       fun cs_short_signature: String is abstract
+
+       # Returns the MPropDef signature with static types.
+       fun cs_signature: String is abstract
+end
+
+redef class MAttributeDef
+
+       redef fun cs_modifiers do
+               var res = super
+               res.add "var"
+               return res
+       end
+
+       redef fun cs_short_signature do return ""
+
+       redef fun cs_signature do
+               var tpl = new FlatBuffer
+               if static_mtype != null then
+                       tpl.append ": "
+                       tpl.append static_mtype.cs_signature
+               end
+               return tpl.write_to_string
+       end
+
+       redef fun cs_icon do return "A"
+end
+
+redef class MMethodDef
+
+       redef fun cs_modifiers do
+               if mproperty.is_init then
+                       var res = new Array[String]
+                       if mproperty.visibility != public_visibility then
+                               res.add mproperty.visibility.to_s
+                       end
+                       return res
+               end
+               var res = super
+               if is_abstract then
+                       res.add "abstract"
+               else if is_intern then
+                       res.add "intern"
+               end
+               res.add "fun"
+               return res
+       end
+
+       redef fun cs_declaration do
+               if mproperty.is_init then
+                       var tpl = new FlatBuffer
+                       if not cs_modifiers.is_empty then
+                               tpl.append cs_modifiers.join(" ")
+                               tpl.append " "
+                       end
+                       tpl.append cs_name
+                       tpl.append cs_signature
+                       return tpl.write_to_string
+               end
+               return super
+       end
+
+       redef fun cs_short_signature do
+               if mproperty.is_root_init then
+                       return new_msignature.cs_short_signature
+               end
+               return msignature.cs_short_signature
+       end
+
+       redef fun cs_signature do
+               if mproperty.is_root_init then
+                       return new_msignature.cs_signature
+               end
+               return msignature.cs_signature
+       end
+
+       redef fun cs_icon do
+               if mproperty.is_init then
+                       return "I"
+               end
+               return "F"
+       end
+end
+
+redef class MVirtualTypeDef
+
+       redef fun cs_modifiers do
+               var res = super
+               res.add "type"
+               return res
+       end
+
+       # Short signature for `MVirtualType` is always empty.
+       redef fun cs_short_signature do return ""
+
+       redef fun cs_signature do
+               var tpl = new FlatBuffer
+               if bound == null then return tpl.write_to_string
+               tpl.append ": "
+               tpl.append bound.cs_signature
+               return tpl.write_to_string
+       end
+       redef fun cs_icon do return "V"
+end
+
+redef class MType
+       # Returns the signature of this type whithout bounds.
+       fun cs_short_signature: String is abstract
+
+       # Returns the signature of this type.
+       fun cs_signature: String is abstract
+
+       redef fun cs_icon do return "T"
+end
+
+redef class MClassType
+       redef fun cs_short_signature do return cs_name
+       redef fun cs_signature do return cs_name
+end
+
+redef class MNullableType
+
+       redef fun cs_short_signature do
+               var tpl = new FlatBuffer
+               tpl.append "nullable "
+               tpl.append mtype.cs_short_signature
+               return tpl.write_to_string
+       end
+
+       redef fun cs_signature do
+               var tpl = new FlatBuffer
+               tpl.append "nullable "
+               tpl.append mtype.cs_signature
+               return tpl.write_to_string
+       end
+end
+
+redef class MGenericType
+       redef fun cs_short_signature do
+               var tpl = new FlatBuffer
+               tpl.append name
+               tpl.append "["
+               for i in [0..arguments.length[ do
+                       tpl.append arguments[i].cs_short_signature
+                       if i < arguments.length - 1 then tpl.append ", "
+               end
+               tpl.append "]"
+               return tpl.write_to_string
+       end
+
+       redef fun cs_signature do
+               var tpl = new FlatBuffer
+               tpl.append mclass.name
+               tpl.append "["
+               for i in [0..arguments.length[ do
+                       tpl.append arguments[i].cs_signature
+                       if i < arguments.length - 1 then tpl.append ", "
+               end
+               tpl.append "]"
+               return tpl.write_to_string
+       end
+end
+
+redef class MParameterType
+       redef fun cs_short_signature do return cs_name
+       redef fun cs_signature do return cs_name
+end
+
+redef class MVirtualType
+       redef fun cs_signature do return cs_name
+end
+
+redef class MSignature
+
+       redef fun cs_short_signature do
+               var tpl = new FlatBuffer
+               if not mparameters.is_empty then
+                       tpl.append "("
+                       for i in [0..mparameters.length[ do
+                               tpl.append mparameters[i].cs_short_signature
+                               if i < mparameters.length - 1 then tpl.append ", "
+                       end
+                       tpl.append ")"
+               end
+               return tpl.write_to_string
+       end
+
+       redef fun cs_signature do
+               var tpl = new FlatBuffer
+               if not mparameters.is_empty then
+                       tpl.append "("
+                       for i in [0..mparameters.length[ do
+                               tpl.append mparameters[i].cs_signature
+                               if i < mparameters.length - 1 then tpl.append ", "
+                       end
+                       tpl.append ")"
+               end
+               if return_mtype != null then
+                       tpl.append ": "
+                       tpl.append return_mtype.cs_signature
+               end
+               return tpl.write_to_string
+       end
+end
+
+redef class MParameter
+
+       # Returns `self` name and ellipsys if any.
+       fun cs_short_signature: String do
+               var tpl = new FlatBuffer
+               tpl.append name
+               if is_vararg then tpl.append "..."
+               return tpl.write_to_string
+       end
+
+       # Returns `self` name with it's static type and ellipsys if any.
+       fun cs_signature: String do
+               var tpl = new FlatBuffer
+               tpl.append "{name}: "
+               tpl.append mtype.cs_signature
+               if is_vararg then tpl.append "..."
+               return tpl.write_to_string
+       end
+end
+
+################################################################################
+# Additions to `model_ext`.
+
+redef class MRawType
+       redef fun cs_signature do
+               var tpl = new FlatBuffer
+
+               for part in parts do
+                       if part.target != null then
+                               tpl.append part.target.as(not null).cs_name
+                       else
+                               tpl.append part.text
+                       end
+               end
+               return tpl.write_to_string
+       end
+end
+
+redef class MInnerClass
+       redef fun cs_signature do return inner.cs_signature
+end
+
+redef class MInnerClassDef
+       redef fun cs_signature do return inner.cs_signature
+end
diff --git a/src/doc/console_templates/console_templates.nit b/src/doc/console_templates/console_templates.nit
new file mode 100644 (file)
index 0000000..45bc33e
--- /dev/null
@@ -0,0 +1,142 @@
+# 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.
+
+# Introduces templates that compose the documentation in console rendering.
+module console_templates
+
+import console_model
+import doc_phases::doc_structure
+
+# Renders the page displayable as ASCII.
+redef class DocPage
+       super Template
+
+       # Renders the footer and content.
+       private fun render_content do add root
+
+       # Renders the whole page
+       redef fun rendering do render_content
+end
+
+redef class DocComposite
+       super Template
+
+       # Title that can be decorated for console display.
+       #
+       # Set as `null` if you don't want to display a title.
+       var cs_title: nullable String is noinit, writable
+
+       # Subtitle that can be decorated for console display.
+       #
+       # Set as `null` if you don't want to display a subtitle.
+       var cs_subtitle: nullable String is noinit, writable
+
+       # Renders the element `cs_title` and `cs_subtitle` as any.
+       fun render_title do
+               if cs_title != null then
+                       add "{"#" * depth} ".blue.bold
+                       addn cs_title.blue.bold
+               end
+               if cs_subtitle != null then
+                       addn cs_subtitle.gray.bold
+               end
+               if cs_title != null or cs_subtitle != null then addn ""
+       end
+
+       # Renders the element body.
+       fun render_body do for child in children do add child.write_to_string
+
+       redef fun rendering do
+               render_title
+               render_body
+       end
+end
+
+redef class DocRoot
+       redef fun rendering do render_body
+end
+
+redef class ConcernSection
+       redef var cs_title is lazy do return "in {mentity.cs_namespace}"
+       redef var cs_subtitle is lazy do return mentity.cs_declaration
+
+       redef fun rendering do
+               var mentity = self.mentity
+               if mentity isa MGroup and mentity.mproject.root == mentity then
+                       render_body
+               else
+                       super
+               end
+       end
+end
+
+redef class ConstructorsSection
+       redef var cs_title = "Constructors"
+       redef var cs_subtitle = null
+end
+
+redef class MEntityComposite
+       redef var cs_title is lazy do return mentity.cs_name
+       redef var cs_subtitle is lazy do return mentity.cs_namespace
+end
+
+redef class IntroArticle
+       redef var cs_title = null
+       redef var cs_subtitle = null
+
+       redef fun render_body do
+               addn "    {mentity.cs_declaration.bold}"
+               addn "    {mentity.cs_location.gray.bold}"
+               addn ""
+               var comment = mentity.cs_comment
+               if comment != null then
+                       add comment
+               end
+               addn ""
+               super
+       end
+end
+
+redef class ConcernsArticle
+       redef var cs_title = "Concerns"
+       redef var cs_subtitle = null
+
+       redef fun render_body do
+               var w = new StringWriter
+               concerns.write_to(w)
+               addn w.to_s
+       end
+end
+
+redef class DefinitionArticle
+       # If short, displays only synopsyses.
+       var is_short = false is writable
+
+       redef fun render_body do
+               addn "    {mentity.cs_visibility_color(mentity.cs_declaration).bold}"
+               addn "    {mentity.cs_location.gray.bold}"
+               addn ""
+               var comment
+               if is_short then
+                       comment = mentity.cs_short_comment
+               else
+                       comment = mentity.cs_comment
+               end
+               if comment != null then
+                       add comment
+               end
+               addn ""
+               super
+       end
+end
index 64b2022..6cd6083 100644 (file)
@@ -88,6 +88,12 @@ abstract class DocComposite
                child.parent = self
                children.add child
        end
+
+       # Depth of `self` in the composite tree.
+       fun depth: Int do
+               if parent == null then return 0
+               return parent.depth + 1
+       end
 end
 
 # The `DocComposite` element that contains all the other.
diff --git a/src/doc/doc_phases/doc_console.nit b/src/doc/doc_phases/doc_console.nit
new file mode 100644 (file)
index 0000000..a7ef6b1
--- /dev/null
@@ -0,0 +1,328 @@
+# 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.
+
+# Nitx related components
+#
+# This module is a place holder for `nitx` related services.
+# No `doc_phase` can be found here, only components used by Nitx tool.
+module doc_console
+
+import semantize
+import doc::console_templates
+
+# Nitx handles console I/O.
+#
+# Using `prompt`, the command line can be turned on an interactive tool.
+class Nitx
+
+       # ToolContext used to access options.
+       var ctx: ToolContext
+
+       # DocModel that contains the informations to display.
+       var doc: DocModel
+
+       # Comparator used to sort MEntities.
+       var sorter = new MEntityNameSorter
+
+       # Displays the welcome message and start prompt.
+       fun start do
+               welcome
+               prompt
+       end
+
+       # Displays the welcome message and the list of loaded modules.
+       fun welcome do
+               print "Welcome in the Nit Index."
+               print ""
+               print "Loaded modules:"
+               var mmodules = doc.mmodules.to_a
+               sorter.sort(mmodules)
+               for m in mmodules do
+                       print "\t{m.name}"
+               end
+               print ""
+               help
+       end
+
+       # Displays the list of available commands.
+       fun help do
+               print "\nCommands:"
+               print "\tname\t\tlookup module, class and property with the corresponding 'name'"
+               print "\t:h\t\tdisplay this help message"
+               print "\t:q\t\tquit interactive mode"
+               print ""
+       end
+
+       # Prompts the user for a command.
+       fun prompt do
+               printn ">> "
+               do_query(sys.stdin.read_line)
+               prompt
+       end
+
+       # Processes the query string and performs it.
+       fun do_query(str: String) do
+               var query = parse_query(str)
+               var res = query.perform(self, doc)
+               var page = query.make_results(self, res)
+               print page.write_to_string
+       end
+
+       # Returns an `NitxQuery` from a raw query string.
+       fun parse_query(str: String): NitxQuery do
+               var query = new NitxQuery(str)
+               if query isa NitxCommand then
+                       query.execute(self)
+               end
+               return query
+       end
+end
+
+# A query performed on Nitx.
+#
+# Queries are responsible to collect matching results and render them as a
+# DocPage.
+#
+# Used as a factory to concrete instances.
+interface NitxQuery
+
+       # Original query string.
+       fun query_string: String is abstract
+
+       # Query factory.
+       #
+       # Will return a concrete instance of NitxQuery.
+       new(query_string: String) do
+               if query_string == ":q" then
+                       return new NitxQuit
+               else if query_string == ":h" then
+                       return new NitxHelp
+               else if query_string.has_prefix("comment:") then
+                       return new CommentQuery(query_string)
+               end
+               return new CommentQuery("comment: {query_string}")
+       end
+
+       # Looks up the `doc` model and returns possible matches.
+       fun perform(nitx: Nitx, doc: DocModel): Array[NitxMatch] is abstract
+
+       # Pretty prints the results for the console.
+       fun make_results(nitx: Nitx, results: Array[NitxMatch]): DocPage do
+               var page = new DocPage("Results")
+               page.root.add_child(new QueryResultArticle(self, results))
+               return page
+       end
+
+       redef fun to_s do return query_string
+end
+
+# Something that matches a `NitxQuery`.
+abstract class NitxMatch
+
+       # Query matched by `self`.
+       var query: NitxQuery
+
+       # Pretty prints `self` for console.
+       fun make_list_item: String is abstract
+end
+
+# A query that contains a meta command.
+#
+# In Nitx, commands are written such as `command: args...`.
+abstract class MetaQuery
+       super NitxQuery
+
+       redef var query_string
+
+       # Meta command used.
+       var command: String is noinit
+
+       # Arguments passed to the `command`.
+       var args = new Array[String]
+
+       init do
+               # parse command
+               var str = new FlatBuffer
+               var i = 0
+               while i < query_string.length do
+                       var c = query_string[i]
+                       i += 1
+                       if c == ':' then break
+                       str.add c
+               end
+               command = str.write_to_string
+               # parse args
+               args.add query_string.substring_from(i).trim
+       end
+end
+
+# A match between a `NitxQuery` and a `MEntity`.
+class MEntityMatch
+       super NitxMatch
+
+       # MEntity matched.
+       var mentity: MEntity
+
+       redef fun make_list_item do return mentity.cs_list_item
+end
+
+# A query to search a `MEntity` comment by its name or namespace.
+class CommentQuery
+       super MetaQuery
+
+       redef fun perform(nitx, doc) do
+               var name = args.first
+               var res = new Array[NitxMatch]
+               for mentity in doc.search_mentities(name) do
+                       res.add new MEntityMatch(self, mentity)
+               end
+               return res
+       end
+
+       redef fun make_results(nitx, results) do
+               var len = results.length
+               if len == 1 then
+                       var res = results.first.as(MEntityMatch)
+                       var mentity = res.mentity
+                       var page = new DocPage("Results")
+                       var article = new DefinitionArticle(mentity)
+                       article.cs_title = mentity.name
+                       article.cs_subtitle = mentity.cs_declaration
+                       page.root.add_child article
+                       return page
+               else
+                       return super
+               end
+       end
+end
+
+# A query that contains a nitx command.
+#
+# These commands are prefixed with `:` and are used to control the execution of
+# `nitx` like displaying the help or quiting.
+interface NitxCommand
+       super NitxQuery
+
+       # Executes the command.
+       fun execute(nitx: Nitx) is abstract
+end
+
+# Exits nitx.
+class NitxQuit
+       super NitxCommand
+
+       redef fun execute(nitx) do exit 0
+end
+
+# Displays the help message.
+class NitxHelp
+       super NitxCommand
+
+       redef fun execute(nitx) do nitx.help
+end
+
+## exploration
+
+redef class DocModel
+
+       # Lists all MEntities in the model.
+       private var mentities: Collection[MEntity] is lazy do
+               var res = new HashSet[MEntity]
+               res.add_all mprojects
+               res.add_all mgroups
+               res.add_all mmodules
+               res.add_all mclasses
+               res.add_all mclassdefs
+               res.add_all mproperties
+               res.add_all mpropdefs
+               return res
+       end
+
+       # Search MEntities that match `name` by their name or namespace.
+       private fun search_mentities(name: String): Array[MEntity] do
+               var res = new Array[MEntity]
+               for mentity in mentities do
+                       if mentity.name != name and mentity.cs_namespace != name then continue
+                       res.add mentity
+               end
+               return res
+       end
+end
+
+# display
+
+# A `DocArticle` that displays query results.
+private class QueryResultArticle
+       super DocArticle
+
+       # Query linked to the results to display.
+       var query: NitxQuery
+
+       # Results to display.
+       var results: Array[NitxMatch]
+
+       redef fun render_title do
+               var len = results.length
+               if len == 0 then
+                       add "No result found for '{query.query_string}'..."
+               else
+                       add "# {len} result(s) for '{query.query_string}'".green.bold
+               end
+       end
+
+       redef fun render_body do
+               addn ""
+               for result in results do
+                       addn ""
+                       addn result.make_list_item
+               end
+       end
+end
+
+# A Pager is used to display data into a unix `less` container.
+private class Pager
+
+       # Content to display.
+       var content = new FlatBuffer
+
+       # Adds text to the pager.
+       fun add(text: String) do
+               content.append(escape(text))
+       end
+
+       fun render do sys.system("echo \"{content}\" | less -r")
+
+       fun escape(str: String): String
+       do
+               var b = new FlatBuffer
+               for c in str.chars do
+                       if c == '\n' then
+                               b.append("\\n")
+                       else if c == '\0' then
+                               b.append("\\0")
+                       else if c == '"' then
+                               b.append("\\\"")
+                       else if c == '\\' then
+                               b.append("\\\\")
+                       else if c == '`' then
+                               b.append("'")
+                       else if c.ascii < 32 then
+                               b.append("\\{c.ascii.to_base(8, false)}")
+                       else
+                               b.add(c)
+                       end
+               end
+               return b.to_s
+       end
+end
index 3e71df2..72df73c 100644 (file)
@@ -80,6 +80,7 @@ private class QuickSearchTable
 
        redef fun provide_default_value(key) do
                var v = new QuickSearchResultList
+               assert key isa String
                self[key] = v
                return v
        end
index d392961..507a0a4 100644 (file)
@@ -16,6 +16,7 @@
 module doc_structure
 
 import doc_concerns
+import modelize
 
 # StructurePhase populates the DocPage content with section and article.
 #
@@ -177,6 +178,12 @@ redef class MClassPage
                mentity.intro_mmodule.mgroup.mproject.booster_rank = 0
                mentity.intro_mmodule.mgroup.booster_rank = 0
                mentity.intro_mmodule.booster_rank = 0
+               var constructors = new ConstructorsSection(mentity)
+               var minit = mentity.root_init
+               if minit != null then
+                       constructors.add_child new DefinitionArticle(minit)
+               end
+               section.add_child constructors
                section.add_child new ConcernsArticle(mentity, concerns)
                for mentity in concerns do
                        var ssection = new ConcernSection(mentity)
@@ -187,7 +194,12 @@ redef class MClassPage
                                        v.name_sorter.sort(group)
                                        for mprop in group do
                                                for mpropdef in mpropdefs_for(mprop, mentity) do
-                                                       ssection.add_child new DefinitionArticle(mpropdef)
+                                                       if mpropdef isa MMethodDef and mpropdef.mproperty.is_init then
+                                                               if mpropdef == minit then continue
+                                                               constructors.add_child new DefinitionArticle(mpropdef)
+                                                       else
+                                                               ssection.add_child new DefinitionArticle(mpropdef)
+                                                       end
                                                end
                                        end
                                end
@@ -289,6 +301,11 @@ class MEntityComposite
        var mentity: MEntity
 end
 
+# A list of constructors.
+class ConstructorsSection
+       super MEntitySection
+end
+
 # A Section about a Concern.
 #
 # Those sections are used to build the page summary.
index c112e25..cd6974c 100644 (file)
@@ -462,22 +462,50 @@ redef class MMethodDef
 
        # FIXME annotation should be handled in their own way
        redef fun html_modifiers do
+               if mproperty.is_init then
+                       var res = new Array[String]
+                       if mproperty.visibility != public_visibility then
+                               res.add mproperty.visibility.to_s
+                       end
+                       return res
+               end
                var res = super
                if is_abstract then
                        res.add "abstract"
                else if is_intern then
                        res.add "intern"
                end
+               res.add "fun"
+               return res
+       end
+
+       redef fun html_declaration do
                if mproperty.is_init then
-                       res.add "init"
-               else
-                       res.add "fun"
+                       var tpl = new Template
+                       tpl.add "<span>"
+                       tpl.add html_modifiers.join(" ")
+                       tpl.add " "
+                       tpl.add html_link
+                       tpl.add html_signature
+                       tpl.add "</span>"
+                       return tpl
                end
-               return res
+               return super
+       end
+
+       redef fun html_short_signature do
+               if mproperty.is_root_init and new_msignature != null then
+                       return new_msignature.html_short_signature
+               end
+               return msignature.html_short_signature
        end
 
-       redef fun html_short_signature do return msignature.html_short_signature
-       redef fun html_signature do return msignature.html_signature
+       redef fun html_signature do
+               if mproperty.is_root_init and new_msignature != null then
+                       return new_msignature.html_signature
+               end
+               return msignature.html_signature
+       end
 end
 
 redef class MVirtualTypeProp
index 4841f02..ed680f0 100644 (file)
@@ -284,10 +284,7 @@ redef class DocComposite
        end
 
        # Level <hX> for HTML heading.
-       private fun hlvl: Int do
-               if parent == null then return 0
-               return parent.hlvl + 1
-       end
+       private fun hlvl: Int do return depth
 
        # Is `self` not displayed in the page.
        #
@@ -447,6 +444,13 @@ redef class MEntitySection
        redef var html_subtitle is lazy do return mentity.html_declaration
 end
 
+redef class ConstructorsSection
+       redef var html_id is lazy do return "article:{mentity.nitdoc_id}.constructors"
+       redef var html_title = "Constructors"
+       redef var html_subtitle = null
+       redef fun is_toc_hidden do return is_empty
+end
+
 redef class ConcernSection
        redef var html_id is lazy do return "concern:{mentity.nitdoc_id}"
        redef var html_title is lazy do return "in {mentity.nitdoc_name}"
index 976d532..90924f2 100644 (file)
@@ -99,14 +99,12 @@ private class SerializationPhasePreModel
        # Add a constructor to the automated nclassdef
        fun generate_deserialization_init(nclassdef: AStdClassdef)
        do
-               # Do not generate constructors for abstract classes
-               if nclassdef.n_classkind isa AAbstractClasskind then return
-
                var npropdefs = nclassdef.n_propdefs
 
                var code = new Array[String]
-               code.add "init from_deserializer(v: Deserializer)"
+               code.add "redef init from_deserializer(v: Deserializer)"
                code.add "do"
+               code.add "      super"
                code.add "      v.notify_of_creation self"
 
                for attribute in npropdefs do if attribute isa AAttrPropdef then
@@ -122,7 +120,7 @@ private class SerializationPhasePreModel
 
                        code.add ""
                        code.add "\tvar {name} = v.deserialize_attribute(\"{name}\")"
-                       code.add "\tassert {name} isa {type_name} else print \"Unsupported type for attribute '{name}', got '\{{name}.class_name\}' (ex {type_name})\""
+                       code.add "\tassert {name} isa {type_name} else print \"Unsupported type for `\{class_name\}::{name}`, got '\{{name}.class_name\}'; expected {type_name}\""
                        code.add "\tself.{name} = {name}"
                end
 
index b65511d..1a27697 100644 (file)
@@ -74,6 +74,67 @@ class Location
        # End of this location on `line_end`
        var column_end: Int
 
+       # Builds a location instance from its string representation.
+       #
+       # Examples:
+       #
+       # ~~~
+       # var loc = new Location.from_string("location.nit:82,2--105,8")
+       # assert loc.to_s == "location.nit:82,2--105,8"
+       #
+       # loc = new Location.from_string("location.nit")
+       # assert loc.to_s == "location.nit"
+       #
+       # loc = new Location.from_string("location.nit:82,2")
+       # assert loc.to_s == "location.nit:82,2--0,0"
+       #
+       # loc = new Location.from_string("location.nit:82--105")
+       # assert loc.to_s == "location.nit:82,0--105,0"
+       #
+       # loc = new Location.from_string("location.nit:82,2--105")
+       # assert loc.to_s == "location.nit:82,2--105,0"
+       #
+       # loc = new Location.from_string("location.nit:82--105,8")
+       # assert loc.to_s == "location.nit:82,0--105,8"
+       # ~~~
+       init from_string(string: String) do
+               self.line_start = 0
+               self.line_end = 0
+               self.column_start = 0
+               self.column_end = 0
+               # parses the location string and init position vars
+               var parts = string.split_with(":")
+               var filename = parts.shift
+               self.file = new SourceFile(filename, new FileReader.open(filename))
+               # split position
+               if parts.is_empty then return
+               var pos = parts.first.split_with("--")
+               # split start position
+               if pos.first.has(",") then
+                       var pos1 = pos.first.split_with(",")
+                       self.line_start = pos1[0].to_i
+                       if pos1.length > 1 then
+                               self.column_start = pos1[1].to_i
+                       end
+               else
+                       self.line_start = pos.first.to_i
+               end
+               # split end position
+               if pos.length <= 1 then return
+               if pos[1].has(",") then
+                       var pos2 = pos[1].split_with(",")
+                       if pos2.length > 1 then
+                               self.line_end = pos2[0].to_i
+                               self.column_end = pos2[1].to_i
+                       else
+                               self.line_end = self.line_start
+                               self.column_end = pos2[0].to_i
+                       end
+               else
+                       self.line_end = pos[1].to_i
+               end
+       end
+
        # The index in the start character in the source
        fun pstart: Int do return file.line_starts[line_start-1] + column_start-1
 
index f0e2911..5e28695 100644 (file)
@@ -869,21 +869,7 @@ class NeoModel
 
        # Get a `Location` from its string representation.
        private fun to_location(loc: String): Location do
-               #TODO filepath
-               var parts = loc.split_with(":")
-               var file = new SourceFile.from_string(parts[0], "")
-               if parts.length == 1 then
-                       return new Location(file, 0, 0, 0, 0)
-               end
-               var pos = parts[1].split_with("--")
-               var pos1 = pos[0].split_with(",")
-               var pos2 = pos[1].split_with(",")
-               var line_s = pos1[0].to_i
-               var line_e = pos2[0].to_i
-               var column_s = pos1[1].to_i
-               var column_e = 0
-               if pos2.length == 2 then pos2[1].to_i
-               return new Location(file, line_s, line_e, column_s, column_e)
+               return new Location.from_string(loc)
        end
 
        # Get a `MVisibility` from its string representation.
index ff615b1..4217115 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index ff615b1..4217115 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index 195a36c..53a12e3 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index 040a8df..60bf2d1 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index efb0ccc..32f461f 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index b69dc33..22ebc32 100644 (file)
@@ -13,6 +13,9 @@ redef class Deserializer
                if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
                if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
                if name == "Array[String]" then return new Array[String].from_deserializer(self)
+               if name == "StrictHashMap[Serializable, Int]" then return new StrictHashMap[Serializable, Int].from_deserializer(self)
+               if name == "HashMap[Serializable, Array[Couple[Serializable, Int]]]" then return new HashMap[Serializable, Array[Couple[Serializable, Int]]].from_deserializer(self)
+               if name == "Array[Couple[Serializable, Int]]" then return new Array[Couple[Serializable, Int]].from_deserializer(self)
                return super
        end
 end
diff --git a/tests/sav/test_binary.res b/tests/sav/test_binary.res
new file mode 100644 (file)
index 0000000..f071d47
--- /dev/null
@@ -0,0 +1,7 @@
+no error
+hello
+77
+1.235
+1.235
+123456789
+no error
diff --git a/tests/sav/test_binary_alt1.res b/tests/sav/test_binary_alt1.res
new file mode 100644 (file)
index 0000000..eb0468f
--- /dev/null
@@ -0,0 +1,7 @@
+no error
+hello
+77
+144545136640.0
+0.0
+1571011930645069824
+no error
diff --git a/tests/sav/test_binary_alt2.res b/tests/sav/test_binary_alt2.res
new file mode 100644 (file)
index 0000000..eb0468f
--- /dev/null
@@ -0,0 +1,7 @@
+no error
+hello
+77
+144545136640.0
+0.0
+1571011930645069824
+no error
diff --git a/tests/sav/test_binary_alt3.res b/tests/sav/test_binary_alt3.res
new file mode 100644 (file)
index 0000000..f071d47
--- /dev/null
@@ -0,0 +1,7 @@
+no error
+hello
+77
+1.235
+1.235
+123456789
+no error
diff --git a/tests/sav/test_coll_eq.res b/tests/sav/test_coll_eq.res
new file mode 100644 (file)
index 0000000..abe1e00
--- /dev/null
@@ -0,0 +1,13 @@
+true
+true
+true
+true
+true
+true
+
+true
+true
+true
+
+true
+hello
index 189c39d..58293c5 100644 (file)
 # Back in Nit:
 <E: 33.33>
 
+# Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
+# Json:
+{"__kind": "obj", "__id": 0, "__class": "G", "hs": {"__kind": "obj", "__id": 1, "__class": "HashSet[Int]", "__length": 2, "__items": [-1, 0]}, "s": {"__kind": "obj", "__id": 2, "__class": "ArraySet[String]", "__length": 2, "__items": ["one", "two"]}, "hm": {"__kind": "obj", "__id": 3, "__class": "HashMap[String, Int]", "__length": 2, "__keys": ["one", "two"], "__values": [1, 2]}, "am": {"__kind": "obj", "__id": 4, "__class": "ArrayMap[String, String]", "__length": 2, "__keys": ["three", "four"], "__values": ["3", "4"]}}
+
+# Back in Nit:
+<G: hs: -1, 0; s: one, two; hm: one. 1, two. 2; am: three. 3, four. 4>
+
index 734ad5e..3281baf 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index 734ad5e..3281baf 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index 11c2a78..797b30d 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer"}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index cb9461b..2809790 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
index f5aff78..e176977 100644 (file)
@@ -14,7 +14,7 @@
 <C: <A: true a 0.123 1234 asdf false> <B: <A: false b 123.123 2345 hjkl false> 1111 qwer>>
 
 # Json:
-{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "ref", "__id": 2}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
+{"__kind": "obj", "__id": 0, "__class": "C", "a": {"__kind": "obj", "__id": 1, "__class": "A", "b": true, "c": {"__kind": "char", "__val": "a"}, "f": 0.123, "i": 1234, "s": "asdf", "n": null, "array": {"__kind": "obj", "__id": 2, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef"}, "b": {"__kind": "obj", "__id": 3, "__class": "B", "b": false, "c": {"__kind": "char", "__val": "b"}, "f": 123.123, "i": 2345, "s": "hjkl", "n": null, "array": {"__kind": "obj", "__id": 4, "__class": "Array[nullable Object]", "__length": 3, "__items": [88, "hello", null]}, "iii": 6789, "sss": "redef", "ii": 1111, "ss": "qwer", "ffff": 6.789, "bbbb": false}, "aa": {"__kind": "ref", "__id": 1}}
 
 # Nit:
 <D: <B: <A: false b 123.123 2345 new line ->
diff --git a/tests/test_binary.nit b/tests/test_binary.nit
new file mode 100644 (file)
index 0000000..a38619b
--- /dev/null
@@ -0,0 +1,38 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import binary
+
+var path = "/tmp/bin"
+var writer = new FileWriter.open(path)
+#alt1# writer.big_endian = false
+#alt3# writer.big_endian = false
+writer.write "hello"
+writer.write_byte 77
+writer.write_float 1.23456789
+writer.write_double 1.23456789
+writer.write_int64 123456789
+print writer.last_error or else "no error"
+writer.close
+
+var reader = new FileReader.open(path)
+#alt2# reader.big_endian = false
+#alt3# reader.big_endian = false
+print reader.read(5)
+print reader.read_byte or else "null"
+print reader.read_float
+print reader.read_double
+print reader.read_int64
+print reader.last_error or else "no error"
+reader.close
diff --git a/tests/test_coll_eq.nit b/tests/test_coll_eq.nit
new file mode 100644 (file)
index 0000000..bf13bd9
--- /dev/null
@@ -0,0 +1,38 @@
+# 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,2,3]
+var l = new List[Int].from(a)
+var l2 = new List[Object].from(a)
+
+print a == l
+print a == l2
+print l == a
+print l == l2
+print l2 == a
+print l2 == l
+print ""
+
+var aa = [a]
+print aa.has(a)
+print aa.has(l)
+print aa.has(l2)
+print ""
+
+var map = new Map[List[Int], String]
+map[l] = "hello"
+
+var mapr: MapRead[Object, String] = map
+print mapr.has_key(a)
+print mapr[l2]
index deb07b7..f26bcbf 100644 (file)
@@ -98,7 +98,7 @@ class E
        redef fun to_s do return "<E: a: {a.join(", ")}; b: {b.join(", ")}>"
 end
 
-# Class with generics
+# Parameterized class
 class F[N: Numeric]
        auto_serializable
 
@@ -108,6 +108,31 @@ class F[N: Numeric]
        redef fun to_s do return "<E: {n}>"
 end
 
+# Other collections
+class G
+       auto_serializable
+
+       var hs = new HashSet[Int]
+       var s: Set[String] = new ArraySet[String]
+       var hm = new HashMap[String, Int]
+       var am = new ArrayMap[String, String]
+
+       init
+       do
+               hs.add -1
+               hs.add 0
+               s.add "one"
+               s.add "two"
+               hm["one"] = 1
+               hm["two"] = 2
+               am["three"] = 3.to_s
+               am["four"] = 4.to_s
+       end
+
+       redef fun to_s do return "<G: hs: {hs.join(", ")}; s: {s.join(", ")}; "+
+               "hm: {hm.join(", ", ". ")}; am: {am.join(", ", ". ")}>"
+end
+
 var a = new A(true, 'a', 0.1234, 1234, "asdf", null)
 var b = new B(false, 'b', 123.123, 2345, "hjkl", 12, 1111, "qwer")
 var c = new C(a, b)
@@ -116,9 +141,10 @@ d.d = d
 var e = new E
 var fi = new F[Int](2222)
 var ff = new F[Float](33.33)
+var g = new G
 
 # Default works only with Nit serial
-var tests = new Array[Serializable].with_items(a, b, c, d, e, fi, ff)
+var tests = [a, b, c, d, e, fi, ff, g: Serializable]
 
 # Alt1 should work without nitserial
 #alt1# tests = new Array[Serializable].with_items(a, b, c, d)
index 1279538..7d1d443 100644 (file)
@@ -22,16 +22,24 @@ import serialization
 redef class Deserializer
        redef fun deserialize_class(name)
        do
-                       if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
-                       if name == "Array[nullable Serializable]" then return new Array[nullable Serializable].from_deserializer(self)
-                       if name == "F[Int]" then return new F[Int].from_deserializer(self)
-                       if name == "F[Float]" then return new F[Float].from_deserializer(self)
-                       if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
-                       if name == "Array[String]" then return new Array[String].from_deserializer(self)
-                       if name == "Array[HashMap[String, nullable Object]]" then return new Array[HashMap[String, nullable Object]].from_deserializer(self)
-                       if name == "Array[Match]" then return new Array[Match].from_deserializer(self)
-                       if name == "Array[nullable Object]" then return new Array[nullable Object].from_deserializer(self)
-                       if name == "Array[FlatBuffer]" then return new Array[FlatBuffer].from_deserializer(self)
+               # Module: test_deserialization
+               if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
+               if name == "Array[nullable Serializable]" then return new Array[nullable Serializable].from_deserializer(self)
+               if name == "F[Int]" then return new F[Int].from_deserializer(self)
+               if name == "F[Float]" then return new F[Float].from_deserializer(self)
+               if name == "HashSet[Int]" then return new HashSet[Int].from_deserializer(self)
+               if name == "ArraySet[String]" then return new ArraySet[String].from_deserializer(self)
+               if name == "HashMap[String, Int]" then return new HashMap[String, Int].from_deserializer(self)
+               if name == "ArrayMap[String, String]" then return new ArrayMap[String, String].from_deserializer(self)
+               if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
+               if name == "Array[String]" then return new Array[String].from_deserializer(self)
+               if name == "HashMap[Serializable, Int]" then return new HashMap[Serializable, Int].from_deserializer(self)
+               if name == "Array[JsonObject]" then return new Array[JsonObject].from_deserializer(self)
+               if name == "HashMap[Int, Object]" then return new HashMap[Int, Object].from_deserializer(self)
+               if name == "Array[Node]" then return new Array[Node].from_deserializer(self)
+               if name == "Array[LRState]" then return new Array[LRState].from_deserializer(self)
+               if name == "Array[Couple[String, String]]" then return new Array[Couple[String, String]].from_deserializer(self)
+               if name == "Array[nullable Jsonable]" then return new Array[nullable Jsonable].from_deserializer(self)
                return super
        end
 end