+
+ # 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 != null then
+ buf.add(c)
+ while not eof do
+ c = read_char
+ if c == null then break
+ if c.is_whitespace then break
+ buf.add(c)
+ 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.
+ # 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'
+ # 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: nullable Char
+ do
+ var c: nullable Char = null
+ while not eof do
+ c = read_char
+ if c == null or not c.is_whitespace then break
+ end
+ return c
+ end
+end
+
+# Iterator returned by `Reader::each_line`.
+# See the aforementioned method for details.
+class LineIterator
+ super Iterator[String]
+
+ # The original stream
+ var stream: Reader
+
+ redef fun is_ok
+ do
+ var res = not stream.eof
+ if not res and close_on_finish then stream.close
+ return res
+ end
+
+ redef fun item
+ do
+ var line = self.line
+ if line == null then
+ line = stream.read_line
+ end
+ self.line = line
+ return line
+ end
+
+ # The last line read (cache)
+ private var line: nullable String = null
+
+ redef fun next
+ do
+ # force the read
+ if line == null then item
+ # drop the line
+ line = null
+ end
+
+ # Close the stream when the stream is at the EOF.
+ #
+ # Default is false.
+ var close_on_finish = false is writable
+
+ redef fun finish
+ do
+ if close_on_finish then stream.close
+ end
+end
+
+# `Reader` capable of declaring if readable without blocking
+abstract class PollableReader
+ super Reader
+
+ # Is there something to read? (without blocking)
+ fun poll_in: Bool is abstract
+