Merge: Fix chomp and read_lines
authorJean Privat <jean@pryen.org>
Sun, 14 Dec 2014 13:43:49 +0000 (08:43 -0500)
committerJean Privat <jean@pryen.org>
Sun, 14 Dec 2014 13:43:49 +0000 (08:43 -0500)
Implements the POLA behavior of reading lines.

I might have to wait that #932 is merged before integrating this one.

Close #974 and close #975 (see them for details)

Pull-Request: #978
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

contrib/nitiwiki/src/wiki_base.nit
lib/ini.nit
lib/io/test_push_back_reader.nit
lib/standard/collection/m.dot [new file with mode: 0644]
lib/standard/collection/range [new file with mode: 0755]
lib/standard/stream.nit
lib/standard/string.nit
src/nitls.nit
tests/sav/test_exec.res
tests/sav/test_file_read.res
tests/sav/test_filterstream.res

index 2c0276b..d5db709 100644 (file)
@@ -116,7 +116,6 @@ class Nitiwiki
                while not pipe.eof do
                        var file = pipe.read_line
                        if file == "" then break # last line
-                       file = file.substring(0, file.length - 1) # strip last oef
                        var name = file.basename(".md")
                        if name == "header" or name == "footer" or name == "menu" then continue
                        files.add file
index a6de8f1..60d2f12 100644 (file)
@@ -199,7 +199,8 @@ class ConfigTree
                        else if line.has_prefix(";") then
                                continue
                        else if line.has_prefix("[") then
-                               var key = line.trim.substring(1, line.length - 2)
+                               line = line.trim
+                               var key = line.substring(1, line.length - 2)
                                path = key
                                set_node(path, null)
                        else
index 5bace8f..bf394aa 100644 (file)
@@ -99,16 +99,16 @@ efg
        fun test_read_line do
                var subject = sample
 
-               assert "abcd\n" == subject.read_line
-               assert "\n" == subject.read_line
+               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\n" == subject.read_line
-               assert "babcd\n" == subject.read_line
+               assert "a" == subject.read_line
+               assert "babcd" == subject.read_line
        end
 
        fun test_eof do
diff --git a/lib/standard/collection/m.dot b/lib/standard/collection/m.dot
new file mode 100644 (file)
index 0000000..762472d
--- /dev/null
@@ -0,0 +1,14 @@
+digraph g {
+rankdir=BT;node[shape=box];
+subgraph cluster_37053984 {
+label=".."
+       m_41649040 [label="kernel"]
+subgraph cluster_37183344 {
+label="../collection"
+       m_38874592 [label="array"]
+       m_40662960 [label="abstract_collection"]
+}
+}
+       m_38874592 -> m_40662960
+       m_40662960 -> m_41649040
+}
diff --git a/lib/standard/collection/range b/lib/standard/collection/range
new file mode 100755 (executable)
index 0000000..384fffe
Binary files /dev/null and b/lib/standard/collection/range differ
index 933523a..0b7bd0a 100644 (file)
@@ -62,41 +62,43 @@ abstract class IStream
 
        # Read a string until the end of the line.
        #
-       # The line terminator '\n', if any, is preserved in each line.
-       # Use the method `Text::chomp` to safely remove it.
+       # The line terminator '\n' and '\r\n', if any, is removed in each line.
        #
        # ~~~
        # var txt = "Hello\n\nWorld\n"
        # var i = new StringIStream(txt)
-       # assert i.read_line == "Hello\n"
-       # assert i.read_line == "\n"
-       # assert i.read_line == "World\n"
+       # assert i.read_line == "Hello"
+       # assert i.read_line == ""
+       # assert i.read_line == "World"
        # assert i.eof
        # ~~~
        #
-       # If `\n` is not present at the end of the result, it means that
-       # a non-eol terminated last line was returned.
+       # Only LINE FEED (`\n`), CARRIAGE RETURN & LINE FEED (`\r\n`), and
+       # the end or file (EOF) is considered to delimit the end of lines.
+       # CARRIAGE RETURN (`\r`) alone is not used for the end of line.
        #
        # ~~~
-       # var i2 = new StringIStream("hello")
-       # assert not i2.eof
-       # assert i2.read_line == "hello"
+       # var txt2 = "Hello\r\n\n\rWorld"
+       # var i2 = new StringIStream(txt2)
+       # assert i2.read_line == "Hello"
+       # assert i2.read_line == ""
+       # assert i2.read_line == "\rWorld"
        # assert i2.eof
        # ~~~
        #
-       # NOTE: Only LINE FEED (`\n`) is considered to delimit the end of lines.
+       # NOTE: Use `append_line_to` if the line terminator needs to be preserved.
        fun read_line: String
        do
                if last_error != null then return ""
-               assert not eof
+               if eof then return ""
                var s = new FlatBuffer
                append_line_to(s)
-               return s.to_s
+               return s.to_s.chomp
        end
 
        # Read all the lines until the eof.
        #
-       # The line terminator '\n' is removed in each line,
+       # The line terminator '\n' and `\r\n` is removed in each line,
        #
        # ~~~
        # var txt = "Hello\n\nWorld\n"
@@ -107,17 +109,25 @@ abstract class IStream
        # This method is more efficient that splitting
        # the result of `read_all`.
        #
-       # NOTE: Only LINE FEED (`\n`) is considered to delimit the end of lines.
+       # NOTE: SEE `read_line` for details.
        fun read_lines: Array[String]
        do
                var res = new Array[String]
                while not eof do
-                       res.add read_line.chomp
+                       res.add read_line
                end
                return res
        end
 
        # Read all the stream until the eof.
+       #
+       # The content of the file is returned verbatim.
+       #
+       # ~~~
+       # var txt = "Hello\n\nWorld\n"
+       # var i = new StringIStream(txt)
+       # assert i.read_all == txt
+       # ~~~
        fun read_all: String
        do
                if last_error != null then return ""
@@ -131,7 +141,37 @@ abstract class IStream
 
        # Read a string until the end of the line and append it to `s`.
        #
-       # SEE: `read_line` for details.
+       # Unlike `read_line` and other related methods,
+       # the line terminator '\n', if any, is preserved in each line.
+       # Use the method `Text::chomp` to safely remove it.
+       #
+       # ~~~
+       # var txt = "Hello\n\nWorld\n"
+       # var i = new StringIStream(txt)
+       # var b = new FlatBuffer
+       # i.append_line_to(b)
+       # assert b == "Hello\n"
+       # i.append_line_to(b)
+       # assert b == "Hello\n\n"
+       # i.append_line_to(b)
+       # assert b == txt
+       # assert i.eof
+       # ~~~
+       #
+       # If `\n` is not present at the end of the result, it means that
+       # a non-eol terminated last line was returned.
+       #
+       # ~~~
+       # var i2 = new StringIStream("hello")
+       # assert not i2.eof
+       # var b2 = new FlatBuffer
+       # i2.append_line_to(b2)
+       # assert b2 == "hello"
+       # assert i2.eof
+       # ~~~
+       #
+       # NOTE: The single character LINE FEED (`\n`) delimits the end of lines.
+       # Therefore CARRIAGE RETURN & LINE FEED (`\r\n`) is also recognized.
        fun append_line_to(s: Buffer)
        do
                if last_error != null then return
@@ -205,12 +245,9 @@ abstract class BufferedIStream
        super IStream
        redef fun read_char
        do
-               if last_error != null then return 0
-               if eof then last_error = new IOError("Stream has reached eof")
-               if _buffer_pos >= _buffer.length then
-                       fill_buffer
-               end
-               if _buffer_pos >= _buffer.length then
+               if last_error != null then return -1
+               if eof then
+                       last_error = new IOError("Stream has reached eof")
                        return -1
                end
                var c = _buffer.chars[_buffer_pos]
@@ -223,7 +260,6 @@ abstract class BufferedIStream
                if last_error != null then return ""
                if _buffer.length == _buffer_pos then
                        if not eof then
-                               fill_buffer
                                return read(i)
                        end
                        return ""
@@ -261,6 +297,15 @@ abstract class BufferedIStream
                        var i = _buffer_pos
                        while i < _buffer.length and _buffer.chars[i] != '\n' do i += 1
 
+                       var eol
+                       if i < _buffer.length then
+                               assert _buffer.chars[i] == '\n'
+                               i += 1
+                               eol = true
+                       else
+                               eol = false
+                       end
+
                        # if there is something to append
                        if i > _buffer_pos then
                                # Enlarge the string (if needed)
@@ -272,25 +317,30 @@ abstract class BufferedIStream
                                        s.add(_buffer.chars[j])
                                        j += 1
                                end
+                               _buffer_pos = i
+                       else
+                               assert end_reached
+                               return
                        end
 
-                       if i < _buffer.length then
-                               # so \n is in _buffer[i]
-                               _buffer_pos = i + 1 # skip \n
+                       if eol then
+                               # so \n is found
                                return
                        else
                                # so \n is not found
-                               _buffer_pos = i
-                               if end_reached then
-                                       return
-                               else
-                                       fill_buffer
-                               end
+                               if end_reached then return
+                               fill_buffer
                        end
                end
        end
 
-       redef fun eof do return _buffer_pos >= _buffer.length and end_reached
+       redef fun eof
+       do
+               if _buffer_pos < _buffer.length then return false
+               if end_reached then return true
+               fill_buffer
+               return _buffer_pos >= _buffer.length and end_reached
+       end
 
        # The buffer
        private var buffer: nullable FlatBuffer = null
index def85f4..bda2b25 100644 (file)
@@ -385,17 +385,38 @@ abstract class Text
        #     assert "\na\nb\tc\t".trim          == "a\nb\tc"
        fun trim: SELFTYPE do return (self.l_trim).r_trim
 
-       # Returns `self` removed from its last `\n` (if any).
+       # Returns `self` removed from its last line terminator (if any).
        #
        #    assert "Hello\n".chomp == "Hello"
        #    assert "Hello".chomp   == "Hello"
-       #    assert "\n\n\n".chomp  == "\n\n"
        #
-       # This method is mainly used to remove the LINE_FEED character from lines of text.
+       #    assert "\n".chomp == ""
+       #    assert "".chomp   == ""
+       #
+       # Line terminators are `"\n"`, `"\r\n"` and `"\r"`.
+       # A single line terminator, the last one, is removed.
+       #
+       #    assert "\r\n".chomp     == ""
+       #    assert "\r\n\n".chomp   == "\r\n"
+       #    assert "\r\n\r\n".chomp == "\r\n"
+       #    assert "\r\n\r".chomp   == "\r\n"
+       #
+       # Note: unlike with most IO methods like `IStream::read_line`,
+       # a single `\r` is considered here to be a line terminator and will be removed.
        fun chomp: SELFTYPE
        do
-               if self.is_empty or self.chars.last != '\n' then return self
-               return substring(0, length-1)
+               var len = length
+               if len == 0 then return self
+               var l = self.chars.last
+               if l == '\r' then
+                       return substring(0, len-1)
+               else if l != '\n' then
+                       return self
+               else if len > 1 and self.chars[len-2] == '\r' then
+                       return substring(0, len-2)
+               else
+                       return substring(0, len-1)
+               end
        end
 
        # Justify a self in a space of `length`
index f78024b..9412f5d 100644 (file)
@@ -148,7 +148,6 @@ if opt_recursive.value then
                while not pipe.eof do
                        var l = pipe.read_line
                        if l == "" then break # last line
-                       l = l.substring(0,l.length-1) # strip last oef
                        files.add l
                end
                pipe.close
index 5f9676c..2f109dd 100644 (file)
@@ -1,4 +1,3 @@
 A hello world!
-B hello world!
 C hello world!
-D hello world!
+B hello world!D hello world!
\ No newline at end of file
index a5790cb..cd93882 100644 (file)
@@ -27,7 +27,6 @@ f.reopen
 printn(f.read(10))
 printn("|")
 printn(f.read_all)
-
 # This fil|e is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2004-2008 Jean Privat <jean@pryen.org>
index 743492c..33e7ff5 100644 (file)
@@ -1,5 +1,2 @@
 Hello World !!
-
 Hello World !!
-
-