parser: add `is_loose`, `prev_looses` and `next_looses`
[nit.git] / lib / standard / stream.nit
index 24681fe..4ad6af1 100644 (file)
@@ -25,7 +25,7 @@ class IOError
        super Error
 end
 
-# Abstract stream class
+# Any kind of stream to read/write/both to or from a source
 abstract class Stream
        # Error produced by the file stream
        #
@@ -39,7 +39,7 @@ abstract class Stream
        fun close is abstract
 end
 
-# Abstract input streams
+# A `Stream` that can be read from
 abstract class Reader
        super Stream
        # Read a character. Return its ASCII value, -1 on EOF or timeout
@@ -220,6 +220,66 @@ abstract class Reader
        # Is there something to read.
        # This function returns 'false' if there is something to read.
        fun eof: Bool is abstract
+
+       # Read the next sequence of non whitespace characters.
+       #
+       # Leading whitespace characters are skipped.
+       # The first whitespace character that follows the result is consumed.
+       #
+       # An empty string is returned if the end of the file or an error is encounter.
+       #
+       # ~~~
+       # var w = new StringReader(" Hello, \n\t World!")
+       # assert w.read_word == "Hello,"
+       # assert w.read_char == '\n'.ascii
+       # assert w.read_word == "World!"
+       # assert w.read_word == ""
+       # ~~~
+       #
+       # `Char::is_whitespace` determines what is a whitespace.
+       fun read_word: String
+       do
+               var buf = new FlatBuffer
+               var c = read_nonwhitespace
+               if c > 0 then
+                       buf.add(c.ascii)
+                       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)
+                       end
+               end
+               var res = buf.to_s
+               return res
+       end
+
+       # 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.
+       #
+       # 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
+       # ~~~
+       #
+       # `Char::is_whitespace` determines what is a whitespace.
+       fun read_nonwhitespace: Int
+       do
+               var c = -1
+               while not eof do
+                       c = read_char
+                       if c < 0 or not c.ascii.is_whitespace then break
+               end
+               return c
+       end
 end
 
 # Iterator returned by `Reader::each_line`.
@@ -269,7 +329,7 @@ class LineIterator
        end
 end
 
-# `ReadStream` capable of declaring if readable without blocking
+# `Reader` capable of declaring if readable without blocking
 abstract class PollableReader
        super Reader
 
@@ -278,7 +338,7 @@ abstract class PollableReader
 
 end
 
-# Abstract output stream
+# A `Stream` that can be written to
 abstract class Writer
        super Stream
        # write a string
@@ -288,12 +348,12 @@ abstract class Writer
        fun is_writable: Bool is abstract
 end
 
-# Things that can be efficienlty writen to a `WriteStream`
+# Things that can be efficienlty written to a `Writer`
 #
-# The point of this interface it to allow is instance to be efficenty
-# writen into a `WriteStream` without having to allocate a big String object
+# The point of this interface is to allow the instance to be efficiently
+# written into a `Writer`.
 #
-# ready-to-save documents usually provide this interface.
+# Ready-to-save documents usually provide this interface.
 interface Writable
        # Write itself to a `stream`
        # The specific logic it let to the concrete subclasses
@@ -317,7 +377,7 @@ redef class Text
        redef fun write_to(stream) do stream.write(self)
 end
 
-# Input streams with a buffer
+# Input streams with a buffered input for efficiency purposes
 abstract class BufferedReader
        super Reader
        redef fun read_char
@@ -332,22 +392,49 @@ abstract class BufferedReader
                return c.ascii
        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)
        do
                if last_error != null then return ""
-               if _buffer.length == _buffer_pos then
-                       if not eof then
-                               return read(i)
-                       end
-                       return ""
-               end
-               if _buffer_pos + i >= _buffer.length then
-                       var from = _buffer_pos
-                       _buffer_pos = _buffer.length
-                       return _buffer.substring_from(from).to_s
+               if eof then return ""
+               var p = _buffer_pos
+               var bufsp = _buffer.length - p
+               if bufsp >= i then
+                       _buffer_pos += i
+                       return _buffer.substring(p, i).to_s
                end
-               _buffer_pos += i
-               return _buffer.substring(_buffer_pos - i, i).to_s
+               _buffer_pos = _buffer.length
+               var readln = _buffer.length - p
+               var s = _buffer.substring(p, readln).to_s
+               fill_buffer
+               return s + read(i - readln)
        end
 
        redef fun read_all
@@ -439,13 +526,13 @@ abstract class BufferedReader
        end
 end
 
-# An Input/Output Stream
+# A `Stream` that can be written to and read from
 abstract class Duplex
        super Reader
        super Writer
 end
 
-# Stream to a String.
+# `Stream` that can be used to write to a `String`
 #
 # Mainly used for compatibility with Writer type and tests.
 class StringWriter
@@ -466,7 +553,7 @@ class StringWriter
        redef fun close do closed = true
 end
 
-# Stream from a String.
+# `Stream` used to read from a `String`
 #
 # Mainly used for compatibility with Reader type and tests.
 class StringReader