Merge: Fix nitunit for `String::format`
authorJean Privat <jean@pryen.org>
Thu, 14 May 2015 23:36:39 +0000 (19:36 -0400)
committerJean Privat <jean@pryen.org>
Thu, 14 May 2015 23:36:39 +0000 (19:36 -0400)
Since the code block was badly indented, it was not tested by `nitunit`.

Once the indentation was corrected, `nitunit` found that the test was failing.

Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

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

66 files changed:
examples/mnit_simple/src/complete_simple_android.nit
examples/mnit_simple/src/test_target_api.nit
examples/rosettacode/loops_break.nit [new file with mode: 0644]
examples/rosettacode/loops_continue.nit [new file with mode: 0644]
examples/rosettacode/loops_do_while.nit [new file with mode: 0644]
examples/rosettacode/loops_downward_for.nit [new file with mode: 0644]
examples/rosettacode/loops_for.nit [new file with mode: 0644]
examples/rosettacode/loops_infinite.nit [new file with mode: 0644]
examples/rosettacode/loops_n_plus_one_half.nit [new file with mode: 0644]
examples/rosettacode/loops_while.nit [new file with mode: 0644]
examples/rosettacode/read_entire_file.nit [new file with mode: 0644]
examples/rosettacode/tree_traversal.nit [new file with mode: 0644]
lib/android/android.nit
lib/android/aware.nit
lib/android/intent/intent_api11.nit
lib/android/intent/intent_api12.nit
lib/android/intent/intent_api14.nit
lib/android/intent/intent_api15.nit
lib/android/intent/intent_api16.nit
lib/android/intent/intent_api17.nit
lib/android/intent/intent_api18.nit
lib/android/intent/intent_api19.nit
lib/android/log.nit
lib/android/notification/native_notification.nit
lib/android/shared_preferences/shared_preferences_api11.nit
lib/android/ui/native_ui.nit
lib/app/app_base.nit
lib/buffered_ropes.nit
lib/csv/csv.nit
lib/filter_stream.nit
lib/io/io.nit [deleted file]
lib/io/push_back_reader.nit [deleted file]
lib/io/test_push_back_reader.nit [deleted file]
lib/linux/data_store.nit
lib/mnit/assets.nit
lib/mnit_linux/linux_assets.nit
lib/saxophonit/lexer.nit
lib/standard/bytes.nit [new file with mode: 0644]
lib/standard/collection/range.nit
lib/standard/exec.nit
lib/standard/file.nit
lib/standard/ropes.nit
lib/standard/standard.nit
lib/standard/stream.nit
lib/standard/string.nit
lib/websocket/websocket.nit
share/nitdoc/css/nitdoc.css
src/doc/doc_phases/doc_graphs.nit
src/doc/doc_phases/doc_hierarchies.nit
src/doc/doc_phases/doc_intros_redefs.nit
src/doc/doc_phases/doc_structure.nit
src/doc/html_templates/html_components.nit
src/doc/html_templates/html_templates.nit
src/platform/android_annotations.nit
tests/example_wc.nit
tests/exec.skip
tests/sav/loops_break.res [new file with mode: 0644]
tests/sav/loops_continue.res [new file with mode: 0644]
tests/sav/loops_do_while.res [new file with mode: 0644]
tests/sav/loops_downward_for.res [new file with mode: 0644]
tests/sav/loops_for.res [new file with mode: 0644]
tests/sav/loops_n_plus_one_half.res [new file with mode: 0644]
tests/sav/loops_while.res [new file with mode: 0644]
tests/sav/test_peek.res [new file with mode: 0644]
tests/sav/tree_traversal.res [new file with mode: 0644]
tests/test_peek.nit [new file with mode: 0644]

index f8cff4f..68f3a85 100644 (file)
@@ -16,7 +16,7 @@
 
 module complete_simple_android is
        app_namespace "org.nitlanguage.test_all"
-       target_api_version 19
+       android_api_target 19
 end
 
 import test_bundle
index 6a2503a..3992c09 100644 (file)
@@ -16,8 +16,8 @@
 
 # Test for the API level related annotations
 module test_target_api is
-       min_api_version(10)
-       max_api_version(19)
+       android_api_min 10
+       android_api_max 19
 end
 
 import simple_android
diff --git a/examples/rosettacode/loops_break.nit b/examples/rosettacode/loops_break.nit
new file mode 100644 (file)
index 0000000..b34d480
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/Break
+#
+# SEE: http://rosettacode.org/wiki/Loops/Break
+module loops_break
+
+loop
+       var a = 20.rand
+       print a
+       if a == 10 then break
+       print 20.rand
+end
diff --git a/examples/rosettacode/loops_continue.nit b/examples/rosettacode/loops_continue.nit
new file mode 100644 (file)
index 0000000..805f05d
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/Continue
+#
+# SEE: http://rosettacode.org/wiki/Loops/Continue
+module loops_continue
+
+for i in [1..10] do
+       printn i
+       if i % 5 == 0 then
+               printn "\n"
+               continue
+       end
+       printn ", "
+end
diff --git a/examples/rosettacode/loops_do_while.nit b/examples/rosettacode/loops_do_while.nit
new file mode 100644 (file)
index 0000000..0e02e25
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/Do-while
+#
+# SEE: http://rosettacode.org/wiki/Loops/Do-while
+module loops_do_while
+
+var val = 0
+loop
+       val += 1
+       print val
+       if val % 6 == 0 then break
+end
diff --git a/examples/rosettacode/loops_downward_for.nit b/examples/rosettacode/loops_downward_for.nit
new file mode 100644 (file)
index 0000000..c5b739e
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/Downward for
+#
+# SEE: http://rosettacode.org/wiki/Loops/Downward_for
+module loops_downward_for
+
+for i in [0..10].reverse_iterator do
+       print i
+end
diff --git a/examples/rosettacode/loops_for.nit b/examples/rosettacode/loops_for.nit
new file mode 100644 (file)
index 0000000..712658d
--- /dev/null
@@ -0,0 +1,14 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/For
+#
+# SEE: http://rosettacode.org/wiki/Loops/For
+module loops_for
+
+for i in [1..5] do
+       for j in [1..i] do printn "*"
+       print ""
+end
diff --git a/examples/rosettacode/loops_infinite.nit b/examples/rosettacode/loops_infinite.nit
new file mode 100644 (file)
index 0000000..75a1a70
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/Infinite
+#
+# SEE: http://rosettacode.org/wiki/Loops/Infinite
+module loops_infinite
+
+loop print "SPAM"
diff --git a/examples/rosettacode/loops_n_plus_one_half.nit b/examples/rosettacode/loops_n_plus_one_half.nit
new file mode 100644 (file)
index 0000000..ebd32f3
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/n + 1/2
+#
+# SEE: http://rosettacode.org/wiki/Loops/N_plus_one_half
+module loops_n_plus_one_half
+
+for i in [0..10] do
+       printn i
+       if i == 10 then break
+       printn ", "
+end
+print ""
+
+# Here the idiomatic version using `join`
+print([0..10].join(", "))
diff --git a/examples/rosettacode/loops_while.nit b/examples/rosettacode/loops_while.nit
new file mode 100644 (file)
index 0000000..9493d00
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Loops/While
+#
+# SEE: http://rosettacode.org/wiki/Loops/While
+module loops_while
+
+var i = 2014
+while i > 0 do
+       print i
+       i /= 2
+end
diff --git a/examples/rosettacode/read_entire_file.nit b/examples/rosettacode/read_entire_file.nit
new file mode 100644 (file)
index 0000000..57e0345
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Read entire file
+#
+# SEE: http://rosettacode.org/wiki/Read_entire_file
+module read_entire_file
+
+var filename = "/etc/issue"
+var content  = filename.to_path.read_all
+print content
diff --git a/examples/rosettacode/tree_traversal.nit b/examples/rosettacode/tree_traversal.nit
new file mode 100644 (file)
index 0000000..dcea146
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Tree traversal
+#
+# SEE: http://rosettacode.org/wiki/Tree_traversal
+module tree_traversal
+
+class Node
+       var value: Int
+       var left: nullable Node
+       var right: nullable Node
+
+       fun preorder
+       do
+               print value
+               var l = left
+               if l != null then l.preorder
+               var r = right
+               if r != null then r.preorder
+       end
+
+       fun inorder
+       do
+               var l = left
+               if l != null then l.inorder
+               print value
+               var r = right
+               if r != null then r.inorder
+       end
+
+       fun postorder
+       do
+               var l = left
+               if l != null then l.postorder
+               var r = right
+               if r != null then r.postorder
+               print value
+       end
+
+       fun levelorder
+       do
+               var q = new List[Node]
+               q.add self
+               while q.not_empty do
+                       var n = q.shift
+                       print n.value
+                       var l = n.left
+                       if l != null then q.push l
+                       var r = n.right
+                       if r != null then q.push r
+               end
+       end
+end
+
+var tree = new Node(1,
+       new Node(2,
+               new Node(4,
+                       new Node(7)),
+               new Node(5)),
+       new Node(3,
+               new Node(6,
+                       new Node(8),
+                       new Node(9))))
+
+print "preorder:"
+tree.preorder
+print "inorder:"
+tree.inorder
+print "postorder:"
+tree.postorder
+print "levelorder:"
+tree.levelorder
index 6c6fb9b..42f3471 100644 (file)
@@ -26,14 +26,7 @@ import dalvik
 private import log
 private import data_store
 
-# Uses Android logs to print everything
-redef fun print(text) do log_write(priority_info, app.log_prefix.to_cstring, text.to_s.to_cstring)
-
 redef class App
-       redef fun log_error(msg) do log_write(priority_error, log_prefix.to_cstring, msg.to_cstring)
-
-       redef fun log_warning(msg) do log_write(priority_warn, log_prefix.to_cstring, msg.to_cstring)
-
        redef fun init_window
        do
                super
index fec387d..0257dc8 100644 (file)
 
 # Android compatibility module
 #
-# Defines all Android related annoations including the `@android` annotations
-# used to tag `ldflags` annotations.
+# Defines all Android related annotations, including `ldflags@android`.
 module aware is
        new_annotation android
-       new_annotation min_api_version
-       new_annotation max_api_version
-       new_annotation target_api_version
+       new_annotation android_api_min
+       new_annotation android_api_max
+       new_annotation android_api_target
        new_annotation android_manifest
        new_annotation android_manifest_application
        new_annotation android_manifest_activity
index 1ba5e88..db0415a 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 11 services
-module intent_api11 is min_api_version(11)
+module intent_api11 is android_api_min 11
 
 import intent
 
index ddf2cff..9af4402 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 12 services
-module intent_api12 is min_api_version(12)
+module intent_api12 is android_api_min 12
 
 import intent_api11
 
index 3bb6576..dc323d2 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 14 services
-module intent_api14 is min_api_version(14)
+module intent_api14 is android_api_min 14
 
 import intent_api12
 
index 4df53ce..cab60aa 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 15 services
-module intent_api15 is min_api_version(15)
+module intent_api15 is android_api_min 15
 
 import intent_api14
 
index 74f6b0a..1513ab8 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 16 services
-module intent_api16 is min_api_version(16)
+module intent_api16 is android_api_min 16
 
 import intent_api15
 
index 0299a21..1d92cdc 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 17 services
-module intent_api17 is min_api_version(17)
+module intent_api17 is android_api_min 17
 
 import intent_api16
 
index 840bceb..b348b3d 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 18 services
-module intent_api18 is min_api_version(18)
+module intent_api18 is android_api_min 18
 
 import intent_api17
 
index 73b1d8b..08af94a 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines intent module to add API 19 services
-module intent_api19 is min_api_version(19)
+module intent_api19 is android_api_min 19
 
 import intent_api18
 
index 6b0faea..b7f7e64 100644 (file)
@@ -23,31 +23,54 @@ in "C" `{
        #include <android/log.h>
 `}
 
+# Use Android logs to print
+redef fun print(object)
+do
+       log_write(priority_info, app.log_prefix.to_cstring, object.to_s.to_cstring)
+end
+
+# Use Android logs to print errors
+redef fun print_error(object)
+do
+       log_write(priority_error, app.log_prefix.to_cstring, object.to_s.to_cstring)
+end
+
+# Use Android logs to print warnings
+redef fun print_warning(object)
+do
+       log_write(priority_warn, app.log_prefix.to_cstring, object.to_s.to_cstring)
+end
+
+redef class App
+       # Prefix to all log messages
+       protected fun log_prefix: String do return "app.nit"
+end
+
 # Default Android log priority
-fun priority_default: Int do return 1
+private fun priority_default: Int do return 1
 
 # Verbose Android log priority
-fun priority_verbose: Int do return 2
+private fun priority_verbose: Int do return 2
 
 # Debug Android log priority
-fun priority_debug: Int do return 3
+private fun priority_debug: Int do return 3
 
 # Info Android log priority
-fun priority_info: Int do return 4
+private fun priority_info: Int do return 4
 
 # Warn Android log priority
-fun priority_warn: Int do return 5
+private fun priority_warn: Int do return 5
 
 # Error Android log priority
-fun priority_error: Int do return 6
+private fun priority_error: Int do return 6
 
 # Fatal Android log priority
-fun priority_fatal: Int do return 7
+private fun priority_fatal: Int do return 7
 
 # Silent Android log priority
-fun priority_silent: Int do return 8
+private fun priority_silent: Int do return 8
 
 # Write `text` to Android log at priority `level` with tag `tag`
-fun log_write(level: Int, tag, text: NativeString) `{
+private fun log_write(level: Int, tag, text: NativeString) `{
        __android_log_write(level, tag, text);
 `}
index 988727f..82c873f 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Native Java classes for notifications
-module native_notification is min_api_version 11
+module native_notification is android_api_min 11
 
 import android::assets_and_resources
 
index cb55acc..48d600a 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Refines shared_preferences module to add API 11 services
-module shared_preferences_api11 is min_api_version(11)
+module shared_preferences_api11 is android_api_min 11
 
 import shared_preferences
 
index bd008c1..79b75a5 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Native services from the `android.view` and `android.widget` namespaces
-module native_ui is min_api_version 14
+module native_ui is android_api_min 14
 
 import nit_activity
 
index 2a335c4..76fbd09 100644 (file)
@@ -34,15 +34,6 @@ class App
        # Main entry point of your application
        fun run do end
 
-       # Prefix to all log messages, used by `log_error`, `log_warning` and `log_info`.
-       fun log_prefix: String do return "app.nit"
-
-       # Helper function for logging errors
-       fun log_error(msg: String) do sys.stderr.write "{log_prefix} error: {msg}\n"
-
-       # Helper function for logging warnings
-       fun log_warning(msg: String) do sys.stderr.write "{log_prefix} warn: {msg}\n"
-
        # Main init method for graphical stuff
        # Is called when display is ready so graphical assets
        # can be loaded at this time.
@@ -52,6 +43,14 @@ class App
        fun window_closing do end
 end
 
+# Print a warning
+fun print_warning(object: Object)
+do
+       sys.stderr.write object.to_s
+       sys.stderr.write "\n"
+end
+
+# The running `App`
 fun app: App do return once new App
 app.setup
 app.run
index bbed4ea..1114264 100644 (file)
@@ -66,10 +66,10 @@ end
 #
 # Basically just returns `self` encapsulated in a `FlatString`.
 private class LeafSubstrings
-       super IndexedIterator[Text]
+       super IndexedIterator[FlatText]
 
        var leaf: Leaf
-       var str: String is noinit
+       var str: FlatString is noinit
        var avail = true
 
        init do
index c6f0b41..f76d41c 100644 (file)
@@ -314,10 +314,9 @@ class CsvReader
                var got_delimiter = false
 
                loop
-                       var i = istream.read_char
-                       var c: Char
+                       var c = istream.read_char
 
-                       if i < 0 then
+                       if c == null then
                                if got_row then
                                        row.add value.to_s
                                        return row
@@ -325,7 +324,6 @@ class CsvReader
                                        return null
                                end
                        end
-                       c = i.ascii
 
                        if c == format.delimiter then
                                if got_delimiter and unescaped == 0 then
@@ -334,11 +332,10 @@ class CsvReader
                                end
                                # Read all bytes until the delimiter.
                                loop
-                                       i = istream.read_char
-                                       assert not_eof: i >= 0 else
+                                       c = istream.read_char
+                                       assert not_eof: c != null else
                                                sys.stderr.write "Unexpected end of file before the end of a delimited value.\n"
                                        end
-                                       c = i.ascii
                                        if c == format.delimiter then break
                                        value.add c
                                end
index c5baa32..f1537e4 100644 (file)
@@ -65,7 +65,7 @@ class StreamCat
                return res
        end
 
-       redef fun read_char: Int
+       redef fun read_char
        do
                assert not eof
                return stream.read_char
diff --git a/lib/io/io.nit b/lib/io/io.nit
deleted file mode 100644 (file)
index c48b171..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# This file is free software, which comes along with NIT. This software is
-# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. You can modify it is you want, provided this header
-# is kept unaltered, and a notification of the changes is added.
-# You are allowed to redistribute it and sell it, alone or is a part of
-# another product.
-
-# Additional services for streams.
-module io
-
-import push_back_reader
diff --git a/lib/io/push_back_reader.nit b/lib/io/push_back_reader.nit
deleted file mode 100644 (file)
index 2fa2d19..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# This file is free software, which comes along with NIT. This software is
-# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. You can modify it is you want, provided this header
-# is kept unaltered, and a notification of the changes is added.
-# You are allowed to redistribute it and sell it, alone or is a part of
-# another product.
-
-# Input stream that permits to push bytes back to the stream.
-module io::push_back_reader
-
-# Input stream that permits to push bytes back to the stream.
-class PushBackReader
-       super Reader
-
-       # Push the specified byte back to the stream.
-       #
-       # The specified byte is usually the last read byte that is not
-       # “unread” already.
-       fun unread_char(c: Char) is abstract
-
-       # Push the specified string back to the stream.
-       #
-       # The specified string is usually the last read string that is not
-       # “unread” already.
-       fun unread(s: String) do
-               for c in s.chars.reverse_iterator do unread_char(c)
-       end
-end
-
-# Decorates an input stream to permit to push bytes back to the input stream.
-class PushBackDecorator
-       super PushBackReader
-
-       # The parent stream.
-       var parent: Reader
-
-       # The stack of the pushed-back bytes.
-       #
-       # Bytes are in the reverse order they will reappear in the stream.
-       # `unread` pushes bytes after already pushed-back bytes.
-       #
-       # TODO: With optimized bulk array copy operations, a reversed stack (like in
-       # OpenJDK) would be more efficient.
-       private var buf: Sequence[Char] = new Array[Char]
-
-       redef fun read_char: Int do
-               if buf.length <= 0 then return parent.read_char
-               return buf.pop.ascii
-       end
-
-       redef fun read(i: Int): String do
-               if i <= 0 then return ""
-               if buf.length <= 0 then return parent.read(i)
-               var s = new FlatBuffer.with_capacity(i)
-
-               loop
-                       s.chars.push(buf.pop)
-                       i -= 1
-                       if i <= 0 then
-                               return s.to_s
-                       else if buf.length <= 0 then
-                               s.append(parent.read(i))
-                               return s.to_s
-                       end
-               end
-       end
-
-       redef fun read_all: String do
-               if buf.length <= 0 then return parent.read_all
-               var s = new FlatBuffer
-
-               loop
-                       s.chars.push(buf.pop)
-                       if buf.length <= 0 then
-                               s.append(parent.read_all)
-                               return s.to_s
-                       end
-               end
-       end
-
-       redef fun append_line_to(s: Buffer) do
-               if buf.length > 0 then
-                       var c: Char
-
-                       loop
-                               c = buf.pop
-                               s.chars.push(c)
-                               if c == '\n' then return
-                               if buf.length <= 0 then break
-                       end
-               end
-               parent.append_line_to(s)
-       end
-
-       redef fun eof: Bool do return buf.length <= 0 and parent.eof
-
-       redef fun close do
-               buf.clear
-               parent.close
-       end
-
-       redef fun unread_char(c: Char) do buf.push(c)
-
-       redef fun unread(s: String) do
-               for c in s.chars.reverse_iterator do buf.push(c)
-       end
-end
diff --git a/lib/io/test_push_back_reader.nit b/lib/io/test_push_back_reader.nit
deleted file mode 100644 (file)
index 1498ccf..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# This file is free software, which comes along with NIT. This software is
-# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
-# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE. You can modify it is you want, provided this header
-# is kept unaltered, and a notification of the changes is added.
-# You are allowed to redistribute it and sell it, alone or is a part of
-# another product.
-
-# Test suites for module `push_back_reader`
-module test_push_back_reader is test_suite
-
-import test_suite
-import io::push_back_reader
-
-class TestPushBackDecorator
-       super TestSuite
-
-       private fun sample: PushBackDecorator do
-               return new PushBackDecorator(new StringReader("""
-abcd
-
-efg
-"""))
-       end
-
-       fun test_read_char do
-               var subject = sample
-
-               assert 'a' == subject.read_char.ascii
-       end
-
-       fun test_read_char_eof do
-               var subject = new PushBackDecorator(new StringReader(""))
-
-               assert -1 == subject.read_char
-       end
-
-       fun test_unread_read_char do
-               var subject = sample
-
-               subject.unread_char('z')
-               assert 'z' == subject.read_char.ascii
-               assert 'a' == subject.read_char.ascii
-       end
-
-       fun test_read_partial do
-               var subject = sample
-
-               assert "abcd" == subject.read(4)
-       end
-
-       fun test_read_too_much do
-               var subject = sample
-               var exp = """
-abcd
-
-efg
-"""
-               assert exp == subject.read(100)
-       end
-
-       fun test_unread_read do
-               var subject = sample
-
-               subject.unread("a")
-               assert "aab" == subject.read(3)
-       end
-
-       fun test_unread_read_mixed do
-               var subject = sample
-
-               subject.unread("a")
-               assert "aab" == subject.read(3)
-       end
-
-       fun test_read_all do
-               var subject = sample
-               var exp = """
-abcd
-
-efg
-"""
-               assert exp == subject.read_all
-       end
-
-       fun test_unread_read_all do
-               var subject = sample
-               var exp = """
-fooabcd
-
-efg
-"""
-               subject.unread("foo")
-               assert exp == subject.read_all
-       end
-
-       fun test_read_line do
-               var subject = sample
-
-               assert "abcd" == subject.read_line
-               assert "" == subject.read_line
-       end
-
-       fun test_unread_read_line do
-               var subject = sample
-
-               subject.unread("a\nb")
-               assert "a" == subject.read_line
-               assert "babcd" == subject.read_line
-       end
-
-       fun test_eof do
-               var subject = sample
-
-               assert not subject.eof
-               subject.read_all
-               assert subject.eof
-       end
-
-       fun test_eof_empty do
-               var subject = new PushBackDecorator(new StringReader(""))
-
-               assert subject.eof
-       end
-
-       fun test_close do
-               var subject = sample
-
-               subject.close
-               assert subject.eof
-       end
-
-       fun test_unread_close do
-               var subject = sample
-
-               subject.unread("foo")
-               subject.close
-               assert subject.eof
-       end
-
-       fun test_unread_char_order do
-               var subject = sample
-
-               subject.unread_char('z')
-               subject.unread_char('y')
-               assert "yzab" == subject.read(4)
-       end
-
-       fun test_unread_order do
-               var subject = sample
-
-               subject.unread("bar")
-               subject.unread("foo")
-               assert "foobarab" == subject.read(8)
-       end
-end
index 3a017dc..5743d82 100644 (file)
@@ -52,7 +52,7 @@ private class LinuxStore
                # Open DB connection
                db = new Sqlite3DB.open(db_path)
                if not db.is_open then
-                       app.log_error "Data store unavaible, cannot load/save data. (at '{db_path}' with '{db.error or else "unknown"}')"
+                       print_error "Data store unavaible, cannot load/save data. (at '{db_path}' with '{db.error or else "unknown"}')"
                        return null
                end
 
index f0d1892..d46b5e4 100644 (file)
@@ -42,7 +42,7 @@ redef class App
        do
                var asset = try_loading_asset( id )
                if asset == null then # error
-                       log_error( "asset <{id}> could not be loaded." )
+                       print_error "asset <{id}> could not be loaded."
                        abort
                else
                        return asset
@@ -57,7 +57,7 @@ redef class App
                if asset isa Image then
                        return asset
                else
-                       log_error( "asset <{id}> is not an image." )
+                       print_error "asset <{id}> is not an image."
                        abort
                end
        end
index 6749d30..393b77d 100644 (file)
@@ -33,7 +33,7 @@ redef class App
        do
                var path = "{assets_dir}/{id}"
                if not path.file_exists then
-                       log_error( "asset <{id}> does not exists." )
+                       print_error "asset <{id}> does not exists."
                        exit(1)
                        abort
                else
index f95e5e3..a84802d 100644 (file)
@@ -254,17 +254,21 @@ class XophonLexer
                        locator.column_number += 1
                end
 
-               last_char = input.read_char
-               if last_char < 0 then
+               var s = input.read_byte
+               if s == null then
+                       last_char = -1
                        return
                end
+               last_char = s
 
                # XML 1.0 end-of-line handling
                # Note: Regardless the XML version, any EOL defined by the
                # recommandation MUST be reported as a single LINE FEED.
                if was_cr and last_char == '\n'.ascii then
                        # EOL already reported. => Skip this byte.
-                       last_char = input.read_char
+                       s = input.read_byte
+                       if s == null then s = -1
+                       last_char = s
                end
                was_cr = last_char == '\r'.ascii
                if was_cr then
diff --git a/lib/standard/bytes.nit b/lib/standard/bytes.nit
new file mode 100644 (file)
index 0000000..b300298
--- /dev/null
@@ -0,0 +1,168 @@
+# 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.
+
+# Services for byte streams and arrays
+module bytes
+
+import kernel
+import collection::array
+intrude import string
+
+# A buffer containing Byte-manipulation facilities
+#
+# Uses Copy-On-Write when persisted
+#
+# TODO: Change the bound to Byte when available in stdlib and bootstrap
+class Bytes
+       super AbstractArray[Int]
+
+       # A NativeString being a char*, it can be used as underlying representation here.
+       private var items: NativeString
+
+       # Number of bytes in the array
+       redef var length: Int
+
+       # Capacity of the array
+       private var capacity: Int
+
+       # Has this buffer been persisted (to_s'd)?
+       #
+       # Used for Copy-On-Write
+       private var persisted = false
+
+       #     var b = new Bytes.empty
+       #     assert b.to_s == ""
+       init empty do
+               var ns = new NativeString(0)
+               init(ns, 0, 0)
+       end
+
+       init with_capacity(cap: Int) do
+               var ns = new NativeString(cap)
+               init(ns, 0, cap)
+       end
+
+       redef fun is_empty do return length != 0
+
+       #     var b = new Bytes.empty
+       #     b.add 101
+       #     assert b[0] == 101
+       redef fun [](i: Int): Int do
+               assert i >= 0
+               assert i < length
+               return items[i].ascii
+       end
+
+       #     var b = new Bytes.with_capacity(1)
+       #     b[0] = 101
+       #     assert b.to_s == "e"
+       redef fun []=(i: Int, v: Int) do
+               if persisted then regen
+               assert i >= 0
+               assert i <= length
+               if i == length then add(v)
+               items[i] = v.ascii
+       end
+
+       #     var b = new Bytes.empty
+       #     b.add 101
+       #     assert b.to_s == "e"
+       redef fun add(c: Int) do
+               if persisted then regen
+               if length >= capacity then
+                       enlarge(length)
+               end
+               items[length] = c.ascii
+               length += 1
+       end
+
+       #     var b = new Bytes.empty
+       #     b.append([104, 101, 108, 108, 111])
+       #     assert b.to_s == "hello"
+       redef fun append(arr: Collection[Int]) do
+               if arr isa Bytes then
+                       append_ns(arr.items, arr.length)
+               else
+                       for i in arr do add i
+               end
+       end
+
+       redef fun clear do length = 0
+
+       # Regenerates the buffer, necessary when it was persisted
+       private fun regen do
+               var nns = new NativeString(capacity)
+               items.copy_to(nns, length, 0, 0)
+               persisted = false
+       end
+
+       # Appends the `ln` first bytes of `ns` to self
+       fun append_ns(ns: NativeString, ln: Int) do
+               if persisted then regen
+               var nlen = length + ln
+               if nlen > capacity then enlarge(nlen)
+               ns.copy_to(items, ln, 0, length)
+               length += ln
+       end
+
+       # Appends `ln` bytes from `ns` starting at index `from` to self
+       fun append_ns_from(ns: NativeString, ln, from: Int) do
+               if persisted then regen
+               var nlen = length + ln
+               if nlen > capacity then enlarge(nlen)
+               ns.copy_to(items, ln, from, length)
+               length += ln
+       end
+
+       redef fun enlarge(sz) do
+               if capacity >= sz then return
+               persisted = false
+               while capacity < sz do capacity = capacity * 2 + 2
+               var ns = new NativeString(capacity)
+               items.copy_to(ns, length, 0, 0)
+               items = ns
+       end
+
+       redef fun to_s do
+               persisted = true
+               return new FlatString.with_infos(items, length, 0, length -1)
+       end
+
+       redef fun iterator do return new BytesIterator.with_buffer(self)
+end
+
+private class BytesIterator
+       super IndexedIterator[Int]
+
+       var tgt: NativeString
+
+       redef var index: Int
+
+       var max: Int
+
+       init with_buffer(b: Bytes) do init(b.items, 0, b.length - 1)
+
+       redef fun is_ok do return index < max
+
+       redef fun next do index += 1
+
+       redef fun item do return tgt[index].ascii
+end
+
+redef class NativeString
+       fun to_bytes: Bytes do
+               var len = cstring_length
+               return new Bytes(self, len, len)
+       end
+end
index da68294..1ad0309 100644 (file)
@@ -49,6 +49,15 @@ class Range[E: Discrete]
 
        redef fun iterator do return new IteratorRange[E](self)
 
+       # Gets an iterator starting at the end and going backwards
+       #
+       #     var reviter = [1..4].reverse_iterator
+       #     assert reviter.to_a == [4,3,2,1]
+       #
+       #     reviter = [1..4[.reverse_iterator
+       #     assert reviter.to_a == [3,2,1]
+       fun reverse_iterator: Iterator[E] do return new ReverseIteratorRange[E](self)
+
        #     assert [1..10].length             == 10
        #     assert [1..10[.length             == 9
        #     assert [1..1].length              == 1
@@ -120,22 +129,38 @@ class Range[E: Discrete]
        end
 end
 
+# Iterator on ranges.
 private class IteratorRange[E: Discrete]
-       # Iterator on ranges.
        super Iterator[E]
        var range: Range[E]
        redef var item is noinit
 
        redef fun is_ok do return _item < _range.after
-       
+
        redef fun next do _item = _item.successor(1)
-       
+
        init
        do
                _item = _range.first
        end
 end
 
+# Reverse iterator on ranges.
+private class ReverseIteratorRange[E: Discrete]
+       super Iterator[E]
+       var range: Range[E]
+       redef var item is noinit
+
+       redef fun is_ok do return _item >= _range.first
+
+       redef fun next do _item = _item.predecessor(1)
+
+       init
+       do
+               _item = _range.last
+       end
+end
+
 redef class Int
        # Returns the range from 0 to `self-1`, is used to do:
        #
index 8cd2f70..cb29312 100644 (file)
@@ -99,6 +99,8 @@ class ProcessReader
 
        redef fun read_char do return stream_in.read_char
 
+       redef fun read_byte do return stream_in.read_byte
+
        redef fun eof do return stream_in.eof
 
        redef fun pipeflags do return 2
index bb87a6f..1b81877 100644 (file)
@@ -1214,10 +1214,19 @@ do
        sys.stdout.write("\n")
 end
 
+# Print `object` on the error output (`stderr` or a log system)
+fun print_error(object: Object)
+do
+       sys.stderr.write object.to_s
+       sys.stderr.write "\n"
+end
+
 # Read a character from the standard input (`stdin`).
 fun getc: Char
 do
-       return sys.stdin.read_char.ascii
+       var c = sys.stdin.read_char
+       if c == null then return '\1'
+       return c
 end
 
 # Read a line from the standard input (`stdin`).
index 644e3dc..5ac6ee2 100644 (file)
@@ -157,6 +157,27 @@ private class Concat
                        return new Concat(left, r + s)
                end
        end
+
+       redef fun copy_to_native(dest, n, src_offset, dest_offset) do
+               var remlen = n
+               var subs = new RopeSubstrings.from(self, src_offset)
+               var st = src_offset - subs.pos
+               var off = dest_offset
+               while n > 0 do
+                       var it = subs.item
+                       if n > it.length then
+                               var cplen = it.length - st
+                               it.items.copy_to(dest, cplen, st, off)
+                               off += cplen
+                               n -= cplen
+                       else
+                               it.items.copy_to(dest, n, st, off)
+                               n = 0
+                       end
+                       subs.next
+                       st = 0
+               end
+       end
 end
 
 # Mutable `Rope`, optimized for concatenation operations
@@ -204,7 +225,7 @@ class RopeBuffer
        # mutable native string (`ns`)
        private var buf_size: Int is noinit
 
-       redef fun substrings: Iterator[String] do return new RopeBufSubstringIterator(self)
+       redef fun substrings do return new RopeBufSubstringIterator(self)
 
        # Builds an empty `RopeBuffer`
        init do
@@ -648,12 +669,12 @@ private class ReverseRopeSubstrings
 end
 
 private class RopeBufSubstringIterator
-       super Iterator[String]
+       super Iterator[FlatText]
 
        # Iterator on the substrings of the building string
-       var iter: Iterator[String]
+       var iter: Iterator[FlatText]
        # Makes a String out of the buffered part of the Ropebuffer
-       var nsstr: String
+       var nsstr: FlatString
        # Did we attain the buffered part ?
        var nsstr_done = false
 
@@ -682,7 +703,7 @@ end
 
 # Substrings of a Rope (i.e. Postfix iterator on leaves)
 private class RopeSubstrings
-       super IndexedIterator[String]
+       super IndexedIterator[FlatString]
 
        # Visit Stack
        var iter: RopeIterPiece is noinit
@@ -692,7 +713,7 @@ private class RopeSubstrings
        var max: Int is noinit
 
        # Current leaf
-       var str: String is noinit
+       var str: FlatString is noinit
 
        init(root: RopeString) is old_style_init do
                var r = new RopeIterPiece(root, true, false, null)
@@ -704,7 +725,7 @@ private class RopeSubstrings
                                rnod = rnod.left
                                r = new RopeIterPiece(rnod, true, false, r)
                        else
-                               str = rnod
+                               str = rnod.as(FlatString)
                                r.rdone = true
                                iter = r
                                break
@@ -729,7 +750,7 @@ private class RopeSubstrings
                                        r = new RopeIterPiece(rnod, true, false, r)
                                end
                        else
-                               str = rnod
+                               str = rnod.as(FlatString)
                                r.rdone = true
                                iter = r
                                self.pos = pos - off
@@ -753,7 +774,7 @@ private class RopeSubstrings
                        if not rnod isa Concat then
                                it.ldone = true
                                it.rdone = true
-                               str = rnod
+                               str = rnod.as(FlatString)
                                iter = it.as(not null)
                                break
                        end
index a487d98..a4ec83a 100644 (file)
@@ -32,3 +32,4 @@ import queue
 import numeric
 import error
 import re
+import bytes
index e0d3a67..6b6d3fe 100644 (file)
@@ -42,8 +42,11 @@ end
 # A `Stream` that can be read from
 abstract class Reader
        super Stream
-       # Read a character. Return its ASCII value, -1 on EOF or timeout
-       fun read_char: Int is abstract
+       # Reads a character. Returns `null` on EOF or timeout
+       fun read_char: nullable Char is abstract
+
+       # Reads a byte. Returns `null` on EOF or timeout
+       fun read_byte: nullable Int is abstract
 
        # Read at most i bytes
        fun read(i: Int): String
@@ -52,8 +55,8 @@ abstract class Reader
                var s = new FlatBuffer.with_capacity(i)
                while i > 0 and not eof do
                        var c = read_char
-                       if c >= 0 then
-                               s.add(c.ascii)
+                       if c != null then
+                               s.add(c)
                                i -= 1
                        end
                end
@@ -164,7 +167,7 @@ abstract class Reader
                var s = new FlatBuffer
                while not eof do
                        var c = read_char
-                       if c >= 0 then s.add(c.ascii)
+                       if c != null then s.add(c)
                end
                return s.to_s
        end
@@ -207,12 +210,11 @@ abstract class Reader
                if last_error != null then return
                loop
                        var x = read_char
-                       if x == -1 then
+                       if x == null then
                                if eof then return
                        else
-                               var c = x.ascii
-                               s.chars.push(c)
-                               if c == '\n' then return
+                               s.chars.push(x)
+                               if x == '\n' then return
                        end
                end
        end
@@ -241,14 +243,13 @@ abstract class Reader
        do
                var buf = new FlatBuffer
                var c = read_nonwhitespace
-               if c > 0 then
-                       buf.add(c.ascii)
+               if c != null then
+                       buf.add(c)
                        while not eof do
                                c = read_char
-                               if c < 0 then break
-                               var a = c.ascii
-                               if a.is_whitespace then break
-                               buf.add(a)
+                               if c == null then break
+                               if c.is_whitespace then break
+                               buf.add(c)
                        end
                end
                var res = buf.to_s
@@ -258,25 +259,25 @@ abstract class Reader
        # Skip whitespace characters (if any) then return the following non-whitespace character.
        #
        # Returns the code point of the character.
-       # Return -1 on end of file or error.
+       # Returns `null` on end of file or error.
        #
        # In fact, this method works like `read_char` except it skips whitespace.
        #
        # ~~~
        # var w = new StringReader(" \nab\tc")
-       # assert w.read_nonwhitespace == 'a'.ascii
-       # assert w.read_nonwhitespace == 'b'.ascii
-       # assert w.read_nonwhitespace == 'c'.ascii
-       # assert w.read_nonwhitespace == -1
+       # assert w.read_nonwhitespace == 'a'
+       # assert w.read_nonwhitespace == 'b'
+       # assert w.read_nonwhitespace == 'c'
+       # assert w.read_nonwhitespace == null
        # ~~~
        #
        # `Char::is_whitespace` determines what is a whitespace.
-       fun read_nonwhitespace: Int
+       fun read_nonwhitespace: nullable Char
        do
-               var c = -1
+               var c: nullable Char = null
                while not eof do
                        c = read_char
-                       if c < 0 or not c.ascii.is_whitespace then break
+                       if c == null or not c.is_whitespace then break
                end
                return c
        end
@@ -382,14 +383,54 @@ abstract class BufferedReader
        super Reader
        redef fun read_char
        do
-               if last_error != null then return -1
+               if last_error != null then return null
                if eof then
                        last_error = new IOError("Stream has reached eof")
-                       return -1
+                       return null
                end
-               var c = _buffer.chars[_buffer_pos]
+               var c = _buffer[_buffer_pos]
                _buffer_pos += 1
-               return c.ascii
+               return c
+       end
+
+       redef fun read_byte
+       do
+               if last_error != null then return null
+               if eof then
+                       last_error = new IOError("Stream has reached eof")
+                       return null
+               end
+               var c = _buffer[_buffer_pos].ascii
+               _buffer_pos += 1
+               return c
+       end
+
+       # Peeks up to `n` bytes in the buffer, returns an empty string on EOF
+       #
+       # The operation does not consume the buffer
+       #
+       # ~~~nitish
+       #       var x = new FileReader("File.txt")
+       #       assert x.peek(5) == x.read(5)
+       # ~~~
+       fun peek(i: Int): String do
+               if eof then return ""
+               var b = new FlatBuffer.with_capacity(i)
+               while i > 0 and not eof do
+                       b.add _buffer[_buffer_pos]
+                       _buffer_pos += 1
+                       i -= 1
+               end
+               var nbuflen = b.length + (_buffer.length - _buffer_pos)
+               var nbuf = new FlatBuffer.with_capacity(nbuflen)
+               nbuf.append(b)
+               while _buffer_pos < _buffer.length do
+                       nbuf.add(_buffer[_buffer_pos])
+                       _buffer_pos += 1
+               end
+               _buffer_pos = 0
+               _buffer = nbuf
+               return b.to_s
        end
 
        redef fun read(i)
@@ -431,11 +472,11 @@ abstract class BufferedReader
                loop
                        # First phase: look for a '\n'
                        var i = _buffer_pos
-                       while i < _buffer.length and _buffer.chars[i] != '\n' do i += 1
+                       while i < _buffer.length and _buffer[i] != '\n' do i += 1
 
                        var eol
                        if i < _buffer.length then
-                               assert _buffer.chars[i] == '\n'
+                               assert _buffer[i] == '\n'
                                i += 1
                                eol = true
                        else
@@ -450,7 +491,7 @@ abstract class BufferedReader
                                # Copy from the buffer to the string
                                var j = _buffer_pos
                                while j < i do
-                                       s.add(_buffer.chars[j])
+                                       s.add(_buffer[j])
                                        j += 1
                                end
                                _buffer_pos = i
@@ -539,12 +580,23 @@ class StringReader
 
        redef fun read_char do
                if cursor < source.length then
-                       var c = source[cursor].ascii
+                       var c = source[cursor]
 
                        cursor += 1
                        return c
                else
-                       return -1
+                       return null
+               end
+       end
+
+       redef fun read_byte do
+               if cursor < source.length then
+                       var c = source[cursor]
+
+                       cursor += 1
+                       return c.ascii
+               else
+                       return null
                end
        end
 
index b10affb..2344659 100644 (file)
@@ -60,7 +60,7 @@ abstract class Text
        fun substring(from: Int, count: Int): SELFTYPE is abstract
 
        # Iterates on the substrings of self if any
-       fun substrings: Iterator[Text] is abstract
+       fun substrings: Iterator[FlatText] is abstract
 
        # Is the current Text empty (== "")
        #
@@ -882,6 +882,27 @@ abstract class Text
                return s.to_s
        end
 
+       # Copies `n` bytes from `self` at `src_offset` into `dest` starting at `dest_offset`
+       #
+       # Basically a high-level synonym of NativeString::copy_to
+       #
+       # REQUIRE: `n` must be large enough to contain `len` bytes
+       #
+       #       var ns = new NativeString(8)
+       #       "Text is String".copy_to_native(ns, 8, 2, 0)
+       #       assert ns.to_s_with_length(8) == "xt is St"
+       #
+       fun copy_to_native(dest: NativeString, n, src_offset, dest_offset: Int) do
+               var mypos = src_offset
+               var itspos = dest_offset
+               while n > 0 do
+                       dest[itspos] = self.chars[mypos]
+                       itspos += 1
+                       mypos += 1
+                       n -= 1
+               end
+       end
+
 end
 
 # All kinds of array-based text representations.
@@ -926,6 +947,10 @@ abstract class FlatText
        end
 
        redef fun flatten do return self
+
+       redef fun copy_to_native(dest, n, src_offset, dest_offset) do
+               items.copy_to(dest, n, src_offset, dest_offset)
+       end
 end
 
 # Abstract class for the SequenceRead compatible
@@ -986,7 +1011,7 @@ abstract class String
        #     assert "helloworld".insert_at(" ", 5)     == "hello world"
        fun insert_at(s: String, pos: Int): SELFTYPE is abstract
 
-       redef fun substrings: Iterator[String] is abstract
+       redef fun substrings is abstract
 
        # Returns a reversed version of self
        #
index 7e4d329..a22c5b2 100644 (file)
@@ -150,8 +150,13 @@ class WebsocketConnection
        private fun unpad_message do
                var fin = false
                while not fin do
-                       var fst_char = client.read_char
-                       var snd_char = client.read_char
+                       var fst_byte = client.read_byte
+                       var snd_byte = client.read_byte
+                       if fst_byte == null or snd_byte == null then
+                               last_error = new IOError("Error: bad frame")
+                               client.close
+                               return
+                       end
                        # First byte in msg is formatted this way :
                        # |(fin - 1bit)|(RSV1 - 1bit)|(RSV2 - 1bit)|(RSV3 - 1bit)|(opcode - 4bits)
                        # fin = Flag indicating if current frame is the last one
@@ -165,9 +170,9 @@ class WebsocketConnection
                        #       %x9 denotes a ping
                        #       %xA denotes a pong
                        #       %xB-F are reserved for further control frames
-                       var fin_flag = fst_char.bin_and(128)
+                       var fin_flag = fst_byte.bin_and(128)
                        if fin_flag != 0 then fin = true
-                       var opcode = fst_char.bin_and(15)
+                       var opcode = fst_byte.bin_and(15)
                        if opcode == 9 then
                                _buffer.add(138.ascii)
                                _buffer.add('\0')
@@ -183,20 +188,30 @@ class WebsocketConnection
                        # |(mask - 1bit)|(payload length - 7 bits)
                        # As specified, if the payload length is 126 or 127
                        # The next 16 or 64 bits contain an extended payload length
-                       var mask_flag = snd_char.bin_and(128)
-                       var len = snd_char.bin_and(127)
+                       var mask_flag = snd_byte.bin_and(128)
+                       var len = snd_byte.bin_and(127)
                        var payload_ext_len = 0
                        if len == 126 then
-                               payload_ext_len = client.read_char.lshift(8)
-                               payload_ext_len += client.read_char
+                               var tmp = client.read(2)
+                               if tmp.length != 2 then
+                                       last_error = new IOError("Error: received interrupted frame")
+                                       client.close
+                                       return
+                               end
+                               payload_ext_len = tmp[1].ascii + tmp[0].ascii.lshift(8)
                        else if len == 127 then
                                # 64 bits for length are not supported,
                                # only the last 32 will be interpreted as a Nit Integer
-                               for i in [0..4[ do client.read_char
-                               payload_ext_len = client.read_char.lshift(24)
-                               payload_ext_len += client.read_char.lshift(16)
-                               payload_ext_len += client.read_char.lshift(8)
-                               payload_ext_len += client.read_char
+                               var tmp = client.read(8)
+                               if tmp.length != 8 then
+                                       last_error = new IOError("Error: received interrupted frame")
+                                       client.close
+                                       return
+                               end
+                               for pos in [0 .. tmp.length[ do
+                                       var i = tmp[pos].ascii
+                                       payload_ext_len += i.lshift(8 * (7 - pos))
+                               end
                        end
                        if mask_flag != 0 then
                                if payload_ext_len != 0 then
index 41a10b4..e47f3aa 100644 (file)
@@ -197,6 +197,20 @@ article.nospace {
        border-left: 2px solid #0d8921;
 }
 
+
+.pull-right .dropdown-toggle {
+       padding: 0 5px;
+}
+
+/* Hide the "..." link */
+
+article .dropdown, article .dropdown {
+       visibility: hidden;
+}
+article:hover .dropdown, article:target .dropdown {
+       visibility: visible;
+}
+
 /*
  * Page parts
  */
@@ -273,20 +287,6 @@ article.nospace {
        margin-left: 30px;
 }
 
-.source-link {
-       display: none;
-       float: right;
-       margin-top: 10px;
-}
-
-.source-link a {
-       color: #0d8921;
-}
-
-article:hover .source-link, article:target .source-link {
-       display: block;
-}
-
 /*
  * Code Highlighting
  */
index 0a011e8..07b7c99 100644 (file)
@@ -126,4 +126,6 @@ class GraphArticle
 
        # Dot script of the graph.
        var dot: Text
+
+       redef var is_empty = false
 end
index 23da906..5bfbfb1 100644 (file)
@@ -41,12 +41,14 @@ end
 redef class MModulePage
        redef fun build_inh_list(v, doc) do
                var section = new ImportationListSection(mentity)
+               var group = new PanelGroup("List")
                var imports = self.imports.to_a
                v.name_sorter.sort(imports)
-               section.add_child new HierarchyListArticle(mentity, "Imports", imports)
+               group.add_child new HierarchyListArticle(mentity, "Imports", imports)
                var clients = self.clients.to_a
                v.name_sorter.sort(clients)
-               section.add_child new HierarchyListArticle(mentity, "Clients", clients)
+               group.add_child new HierarchyListArticle(mentity, "Clients", clients)
+               section.add_child group
                section.parent = root.children.first
                root.children.first.children.insert(section, 1)
        end
@@ -55,18 +57,20 @@ end
 redef class MClassPage
        redef fun build_inh_list(v, doc) do
                var section = new InheritanceListSection(mentity)
+               var group = new PanelGroup("List")
                var parents = self.parents.to_a
                v.name_sorter.sort(parents)
-               section.add_child new HierarchyListArticle(mentity, "Parents", parents)
+               group.add_child new HierarchyListArticle(mentity, "Parents", parents)
                var ancestors = self.ancestors.to_a
                v.name_sorter.sort(ancestors)
-               section.add_child new HierarchyListArticle(mentity, "Ancestors", ancestors)
+               group.add_child new HierarchyListArticle(mentity, "Ancestors", ancestors)
                var children = self.children.to_a
                v.name_sorter.sort(children)
-               section.add_child new HierarchyListArticle(mentity, "Children", children)
+               group.add_child new HierarchyListArticle(mentity, "Children", children)
                var descendants = self.descendants.to_a
                v.name_sorter.sort(descendants)
-               section.add_child new HierarchyListArticle(mentity, "Descendants", descendants)
+               group.add_child new HierarchyListArticle(mentity, "Descendants", descendants)
+               section.add_child group
                section.parent = root.children.first
                root.children.first.children.insert(section, 1)
        end
@@ -74,13 +78,13 @@ end
 
 # FIXME diff hack
 class ImportationListSection
-       super DocSection
+       super TabbedGroup
        super MEntityComposite
 end
 
 # FIXME diff hack
 class InheritanceListSection
-       super DocSection
+       super TabbedGroup
        super MEntityComposite
 end
 
index afd8e55..54f7c18 100644 (file)
@@ -54,28 +54,42 @@ redef class DefinitionArticle
 
        # TODO this should move to MEntity?
        private fun build_mmodule_list(v: IntroRedefListPhase, doc: DocModel, mmodule: MModule) do
+               var section = new IntrosRedefsSection(mentity)
+               var group = new PanelGroup("List")
                var intros = mmodule.intro_mclassdefs(v.ctx.min_visibility).to_a
                doc.mainmodule.linearize_mclassdefs(intros)
-               add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
+               group.add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
                var redefs = mmodule.redef_mclassdefs(v.ctx.min_visibility).to_a
                doc.mainmodule.linearize_mclassdefs(redefs)
-               add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
+               group.add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
+               section.add_child group
+               add_child(section)
        end
 
        # TODO this should move to MEntity?
        private fun build_mclassdef_list(v: IntroRedefListPhase, doc: DocModel, mclassdef: MClassDef) do
+               var section = new IntrosRedefsSection(mentity)
+               var group = new PanelGroup("List")
                var intros = mclassdef.collect_intro_mpropdefs(v.ctx.min_visibility).to_a
                # FIXME avoid diff changes
                # v.ctx.mainmodule.linearize_mpropdefs(intros)
-               add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
+               group.add_child new IntrosRedefsListArticle(mentity, "Introduces", intros)
                var redefs = mclassdef.collect_redef_mpropdefs(v.ctx.min_visibility).to_a
                # FIXME avoid diff changes
                # v.ctx.mainmodule.linearize_mpropdefs(redefs)
-               add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
+               group.add_child new IntrosRedefsListArticle(mentity, "Redefines", redefs)
+               section.add_child group
+               add_child(section)
        end
 
 end
 
+# Section that contains the intros and redefs lists.
+class IntrosRedefsSection
+       super TabbedGroup
+       super MEntitySection
+end
+
 # An article that displays a list of introduced / refined mentities.
 #
 # FIXME diff hack
index fc2dc17..d392961 100644 (file)
@@ -266,6 +266,21 @@ redef class MPropertyPage
        end
 end
 
+# A group of sections that can be displayed together in a tab.
+#
+# Display the first child and hide less relevant data in other panels.
+class TabbedGroup
+       super DocSection
+end
+
+# A group of sections that can be displayed together in a tab panel.
+class PanelGroup
+       super DocSection
+
+       # The title of this group.
+       var group_title: String
+end
+
 # A DocComposite element about a MEntity.
 class MEntityComposite
        super DocComposite
@@ -316,6 +331,7 @@ end
 
 # An article that displaus a list of definition belonging to a MEntity.
 class DefinitionListArticle
+       super TabbedGroup
        super MEntityArticle
 end
 
index 221162b..630d28e 100644 (file)
@@ -36,6 +36,120 @@ class DocHTMLLabel
        end
 end
 
+# A component that display tabbed data.
+class DocTabs
+       super BSComponent
+
+       # HTML id of this component.
+       var html_id: String
+
+       # Text displayed on the tabs dropdown button.
+       var drop_text: String
+
+       # Panels to display in this tab group.
+       var panels = new Array[DocTabPanel]
+
+       # Droplist containing links to panels.
+       #
+       # Can also be used to add external links.
+       var drop_list: DocTabsDrop is lazy do return new DocTabsDrop(html_id, drop_text)
+
+       # Adds a new `panel` to that tab.
+       #
+       # You should always use this instead of `panels.add` because it also set the
+       # `drop_list` entry.
+       fun add_panel(panel: DocTabPanel) do
+               drop_list.add_li panel.render_tab
+               panels.add panel
+       end
+
+       redef fun rendering do
+               if panels.is_empty then return
+               panels.first.is_active = true
+               add "<div role=\"tabpanel\">"
+               if drop_list.items.length > 1 then add drop_list
+               add " <div class=\"tab-content\">"
+               for panel in panels do
+                       add panel
+               end
+               add " </div>"
+               add "</div>"
+       end
+end
+
+# A list of tab regrouped in a dropdown
+class DocTabsDrop
+       super UnorderedList
+
+       # HTML id used by the tabs group.
+       var html_id: String
+
+       # Title to display in the tab item.
+       var html_title: String
+
+       redef fun rendering do
+               add """<ul id="{{{html_id}}}-tabs" class="nav pull-right" role="tablist">"""
+               add """ <li role="presentation" class="dropdown pull-right">"""
+               add """  <a href="#" id="{{{html_id}}}-drop" class="dropdown-toggle"
+                         data-toggle="dropdown" aria-controls="{{{html_id}}}-contents"
+                         aria-expanded="false">"""
+        add html_title
+               add """ <span class="glyphicon glyphicon-menu-hamburger"></span>"""
+               add """ </a>"""
+               add """ <ul class="dropdown-menu" role="menu"
+                        aria-labelledby="{{{html_id}}}-drop" id="{{{html_id}}}-contents">"""
+               for item in items do add item
+               add "  </ul>"
+        add " </li>"
+        add "</ul>"
+       end
+end
+
+# A panel that goes in a DocTabs.
+class DocTabPanel
+       super BSComponent
+
+       # HTML id of this panel.
+       var html_id: String
+
+       # Title of this panel as displayed in the tab label.
+       var tab_title: String
+
+       # HTML content of this panel.
+       var html_content: Writable is writable
+
+       # Is this panel visible by default?
+       var is_active = false
+
+       redef fun rendering do
+               var active = ""
+               if is_active then active = "active in"
+               add "<div role=\"tabpanel\" class=\"tab-pane fade {active}\""
+               add "  id=\"{html_id}\" aria-labelledby=\"{html_id}-tab\">"
+               add html_content
+               add "</div>"
+       end
+
+       private fun render_tab: DocTabItem do return new DocTabItem(tab_title, html_id)
+end
+
+# A ListItem that goes in a DocTabsDrop.
+private class DocTabItem
+       super ListItem
+
+       # Panel id to trigger when the link is clicked.
+       var target_id: String
+
+       redef fun rendering do
+               add "<li{render_css_classes}>"
+               add " <a role=\"tab\" data-toggle=\"tab\" aria-expanded=\"false\" tabindex=\"-1\""
+               add "   id=\"{target_id}-tab\" href=\"#{target_id}\" aria-controls=\"{target_id}\">"
+               add text
+               add " </a>"
+               add "</li>"
+       end
+end
+
 # A HTML tag attribute
 #  `<tag attr="value">`
 #
index a461ba0..4841f02 100644 (file)
@@ -319,6 +319,15 @@ redef class DocComposite
                end
                lst.add_li new ListItem(content)
        end
+
+       # ID used in HTML tab labels.
+       #
+       # We sanitize it for Boostrap JS panels that do not like ":" and "." in ids.
+       var html_tab_id: String is lazy do
+               var id = html_id.replace(":", "")
+               id = id.replace(".", "")
+               return "{id}-tab"
+       end
 end
 
 redef class DocSection
@@ -348,6 +357,24 @@ redef class DocArticle
        end
 end
 
+redef class TabbedGroup
+       redef fun render_body do
+               var tabs = new DocTabs("{html_id}.tabs", "")
+               for child in children do
+                       if child.is_hidden then continue
+                       tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
+               end
+               addn tabs
+       end
+end
+
+redef class PanelGroup
+       redef var html_id is lazy do return "group:{group_title.to_lower.to_snake_case}"
+       redef var html_title = null
+       redef var toc_title is lazy do return group_title
+       redef var is_toc_hidden = true
+end
+
 redef class HomeArticle
        redef var html_id = "article:home"
        redef var html_title = "Overview"
@@ -445,20 +472,21 @@ redef class IntroArticle
        # Link to source to display if any.
        var html_source_link: nullable Writable is noinit, writable
 
-       redef fun render_title do
+       redef fun render_body do
+               var tabs = new DocTabs("{html_id}.tabs", "")
+               var comment = mentity.html_comment
+               if comment != null then
+                       tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
+               end
+               for child in children do
+                       if child.is_hidden then continue
+                       tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
+               end
                var lnk = html_source_link
                if lnk != null then
-                       add "<div class='source-link'>"
-                       add lnk
-                       addn "</div>"
+                       tabs.drop_list.items.add new ListItem(lnk)
                end
-               super
-       end
-
-       redef fun render_body do
-               var comment = mentity.html_comment
-               if comment != null then addn comment
-               super
+               addn tabs
        end
 end
 
@@ -502,17 +530,8 @@ redef class DefinitionArticle
        # Link to source to display if any.
        var html_source_link: nullable Writable is noinit, writable
 
-       redef fun render_title do
-               var lnk = html_source_link
-               if lnk != null then
-                       add "<div class='source-link'>"
-                       add lnk
-                       addn "</div>"
-               end
-               super
-       end
-
        redef fun render_body do
+               var tabs = new DocTabs("{html_id}.tabs", "")
                if not is_no_body then
                        var comment
                        if is_short_comment then
@@ -520,9 +539,19 @@ redef class DefinitionArticle
                        else
                                comment = mentity.html_comment
                        end
-                       if comment != null then addn comment
+                       if comment != null then
+                               tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
+                       end
                end
-               super
+               for child in children do
+                       if child.is_hidden then continue
+                       tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
+               end
+               var lnk = html_source_link
+               if lnk != null then
+                       tabs.drop_list.items.add new ListItem(lnk)
+               end
+               addn tabs
        end
 end
 
@@ -530,7 +559,7 @@ redef class HierarchyListArticle
        redef var html_id is lazy do return "article:{list_title}_{mentity.nitdoc_id}.hierarchy"
        redef var html_title is lazy do return list_title
        redef fun is_empty do return mentities.is_empty
-       redef fun is_toc_hidden do return mentities.is_empty
+       redef var is_toc_hidden = true
 
        redef fun render_body do
                var lst = new UnorderedList
@@ -542,8 +571,16 @@ redef class HierarchyListArticle
        end
 end
 
-redef class IntrosRedefsListArticle
+redef class IntrosRedefsSection
        redef var html_id is lazy do return "article:{mentity.nitdoc_id}.intros_redefs"
+       redef var toc_title do return "Intros / Redefs"
+       redef var html_title = null
+       redef var html_subtitle = null
+       redef var is_toc_hidden = true
+end
+
+redef class IntrosRedefsListArticle
+       redef var html_id is lazy do return "article:{list_title}_{mentity.nitdoc_id}.intros_redefs"
        redef var html_title is lazy do return list_title
        redef fun is_hidden do return mentities.is_empty
        redef var is_toc_hidden = true
index 347c4dc..1f83e61 100644 (file)
@@ -46,7 +46,7 @@ class AndroidProject
 
        init
        do
-               var annots = modelbuilder.collect_annotations_on_modules("min_api_version", mainmodule)
+               var annots = modelbuilder.collect_annotations_on_modules("android_api_min", mainmodule)
                if not annots.is_empty then
                        var i = annots.pop.arg_as_int(modelbuilder)
                        if i == null then i = 0
@@ -58,7 +58,7 @@ class AndroidProject
                        end
                end
 
-               annots = modelbuilder.collect_annotations_on_modules("max_api_version", mainmodule)
+               annots = modelbuilder.collect_annotations_on_modules("android_api_max", mainmodule)
                if not annots.is_empty then
                        var i = annots.pop.arg_as_int(modelbuilder)
                        if i == null then i = 0
@@ -70,7 +70,7 @@ class AndroidProject
                        end
                end
 
-               var annot = modelbuilder.lookup_annotation_on_modules("target_api_version", mainmodule)
+               var annot = modelbuilder.lookup_annotation_on_modules("android_api_target", mainmodule)
                if annot != null then target_api = annot.arg_as_int(modelbuilder) or else 0
 
                annots = modelbuilder.collect_annotations_on_modules("android_manifest", mainmodule)
index 0bd73fd..7a9e9bd 100644 (file)
@@ -23,17 +23,16 @@ var l = 0
 var inw = false
 while not stdin.eof do
        var i = stdin.read_char
-       if i >= 0 then
-               var x = i.ascii
+       if i != null then
                c = c + 1
-               if x > ' ' then
+               if i > ' ' then
                        if not inw then
                                inw = true
                                w = w + 1
                        end
                else
                        inw = false
-                       if x == '\n' then
+                       if i == '\n' then
                                l = l + 1
                        end
                end
index 49599a2..42fe68f 100644 (file)
@@ -4,3 +4,5 @@ websocket_server
 converter
 show_basedir
 langannot
+loops_infinite
+read_entire_file
diff --git a/tests/sav/loops_break.res b/tests/sav/loops_break.res
new file mode 100644 (file)
index 0000000..13600be
--- /dev/null
@@ -0,0 +1,31 @@
+16
+7
+15
+15
+18
+3
+6
+15
+5
+11
+9
+12
+7
+10
+19
+18
+12
+14
+2
+12
+0
+4
+2
+16
+3
+8
+2
+2
+19
+4
+10
diff --git a/tests/sav/loops_continue.res b/tests/sav/loops_continue.res
new file mode 100644 (file)
index 0000000..0865d3f
--- /dev/null
@@ -0,0 +1,2 @@
+1, 2, 3, 4, 5
+6, 7, 8, 9, 10
diff --git a/tests/sav/loops_do_while.res b/tests/sav/loops_do_while.res
new file mode 100644 (file)
index 0000000..b414108
--- /dev/null
@@ -0,0 +1,6 @@
+1
+2
+3
+4
+5
+6
diff --git a/tests/sav/loops_downward_for.res b/tests/sav/loops_downward_for.res
new file mode 100644 (file)
index 0000000..30b148d
--- /dev/null
@@ -0,0 +1,11 @@
+10
+9
+8
+7
+6
+5
+4
+3
+2
+1
+0
diff --git a/tests/sav/loops_for.res b/tests/sav/loops_for.res
new file mode 100644 (file)
index 0000000..9a639e0
--- /dev/null
@@ -0,0 +1,5 @@
+*
+**
+***
+****
+*****
diff --git a/tests/sav/loops_n_plus_one_half.res b/tests/sav/loops_n_plus_one_half.res
new file mode 100644 (file)
index 0000000..52f7392
--- /dev/null
@@ -0,0 +1,2 @@
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
diff --git a/tests/sav/loops_while.res b/tests/sav/loops_while.res
new file mode 100644 (file)
index 0000000..10dca53
--- /dev/null
@@ -0,0 +1,11 @@
+2014
+1007
+503
+251
+125
+62
+31
+15
+7
+3
+1
diff --git a/tests/sav/test_peek.res b/tests/sav/test_peek.res
new file mode 100644 (file)
index 0000000..edc1121
--- /dev/null
@@ -0,0 +1,33 @@
+# Thi
+# Thi
+s file is pa
+s file is pa
+rt 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 f = new FileReader.open("test_peek.nit")
+
+print f.peek(5)
+print f.read(5)
+
+print f.peek(12)
+print f.read(12)
+
+print f.read_all
+
+print f.peek(2)
+
+f.close
+
+
diff --git a/tests/sav/tree_traversal.res b/tests/sav/tree_traversal.res
new file mode 100644 (file)
index 0000000..7c14034
--- /dev/null
@@ -0,0 +1,40 @@
+preorder:
+1
+2
+4
+7
+5
+3
+6
+8
+9
+inorder:
+7
+4
+2
+5
+1
+8
+6
+9
+3
+postorder:
+7
+4
+5
+2
+8
+9
+6
+3
+1
+levelorder:
+1
+2
+3
+4
+5
+6
+7
+8
+9
diff --git a/tests/test_peek.nit b/tests/test_peek.nit
new file mode 100644 (file)
index 0000000..4c1d433
--- /dev/null
@@ -0,0 +1,27 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+var f = new FileReader.open("test_peek.nit")
+
+print f.peek(5)
+print f.read(5)
+
+print f.peek(12)
+print f.read(12)
+
+print f.read_all
+
+print f.peek(2)
+
+f.close