May be useful when implementing backtracking algorithms.
Signed-off-by: Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
fun show_graph(content: String) do "show_graph('{content.escape_to_c}');".run_js
end
-class StringIStream
- super BufferedIStream
-
- init(str: String) do _buffer = new FlatBuffer.from(str)
-
- redef fun fill_buffer do end_reached = true
- redef var end_reached: Bool = false
-end
-
redef class NativeString
fun run_analysis do manager.run to_s
end
--- /dev/null
+# 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
--- /dev/null
+# 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.
+interface PushBackReader
+ super IStream
+
+ # 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: IStream
+
+ # 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
--- /dev/null
+# 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 StringIStream("""
+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 StringIStream(""))
+
+ 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\n" == subject.read_line
+ assert "\n" == 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
+ 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 StringIStream(""))
+
+ 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