Merge: Steps on iterations and ranges
authorJean Privat <jean@pryen.org>
Thu, 21 May 2015 00:32:31 +0000 (20:32 -0400)
committerJean Privat <jean@pryen.org>
Thu, 21 May 2015 00:32:31 +0000 (20:32 -0400)
A proposal to solve some things identified in #1339

This PR does 3 things:

* extends any Iterator to locally (`next_by`) or globally (`to_step`) advance with more than a single `next`
* add `Range.step` to have a generic bidirectional and stepable iterator

So now people can write

~~~nit
for i in [1..10].step(2) do print i # in order 1 3 5 7 9
for i in [10..1].step(-2) do print i # in reverse order 10 8 6 5 2
~~~

This is a neat (over?-) engineering. A maybe non-POLA thing is that empty ranges can still be iterated with a negative step.

~~~nit
assert [5..1].is_empty # but
assert [5..1].step(-2).to_a == [5,3,1]
~~~

Pull-Request: #1347
Reviewed-by: ArthurDelamare <arthur.delamare@viacesi.fr>
Reviewed-by: Etienne M. Gagnon <egagnon@j-meg.com>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Ait younes Mehdi Adel <overpex@gmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

65 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
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 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 370305f..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
@@ -335,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.
@@ -413,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
@@ -464,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.
        #
@@ -474,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
@@ -487,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
@@ -501,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
@@ -549,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?
        #
@@ -784,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.
@@ -793,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.
@@ -803,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
@@ -823,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
@@ -1073,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 b5f2d60..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)
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