Source code for typped.text_stream
# -*- coding: utf-8 -*-
"""
The `TextStream` class. Not currently used, may become an abstraction layer for
the stream of text to be parsed.
"""
from __future__ import print_function, division, absolute_import
import sys
import collections # for deque
import readline
if sys.version_info[0] >= 3:
get_input = input
import io as io_module
else:
get_input = raw_input
# Python 2.7 has two StringIO versions, io and StringIO. The one in io
# only takes unicode (it is Python 3 ready), but StringIO one takes str
# inits too.
import StringIO as io_module
[docs]class TextStream(object):
"""This is just a unified wrapper for a stream of text characters which may
come from various sources."""
def __init__(self):
"""Initialize the basic object. Some set method must also be called to
determine the type of input before next() can be called."""
self.clear()
[docs] def clear(self):
"""Clear and reset the text stream."""
self.ts = None # the text stream object, set by the set methods
self.EOF = True # false while a stream is open and not at end
self.curr_line_num = 1 # count lines for error reporting, starts at 1
self.curr_char_num = 0 # count chars on line for error reporting, first is 1
self.increment_line_on_next_char = False # used in counting lines
self.char_buffer = collections.deque()
self.raw_in = False
[docs] def set_string_in(self, str_val):
"""Set a string to be the text source."""
self.clear()
self.ts = io_module.StringIO(str_val) # in-memory text stream
#self.ts = StringIO.StringIO(str_val) # in-memory text stream
self.raw_in = False
self.EOF = False
[docs] def set_file_in(self, f_name):
"""Set a file to be the text source."""
self.clear()
self.ts = open(f_name, "r")
self.raw_in = False
self.EOF = False
[docs] def set_raw_in(self):
"""Read input interactively for text source."""
self.clear()
print("Type ^D to leave raw input mode.")
self.raw_in = True
self.EOF = False
def __iter__(self):
"""So statements like: for char in textStr: etc., and comprehensions,
can be used."""
return self # implies that the next() method will be used for iterations
[docs] def next(self):
"""Get the next character in the text stream. Can be used as::
while not ts.end_of_text_stream():
char = ts.next()
...
or else as::
for char in ts:
...
"""
self.refill_char_buffer_if_empty()
if len(self.char_buffer) == 0:
raise StopIteration("no more chars in TextStream for next()")
retval = self.char_buffer.popleft()
self.__increment_counters(retval)
return retval
__next__ = next
[docs] def refill_char_buffer_if_empty(self):
"""Read a line to refill the char buffer. Return `False` if end of stream
is encountered, `True` otherwise."""
if len(self.char_buffer) != 0: # buffer not empty, return
return True
if self.raw_in:
try:
line = get_input("|- ")
except: # got an EOFError or some other unspecified exception
self.EOF = True
self.char_buffer.clear()
return False
self.char_buffer = collections.deque([c for c in line])
self.char_buffer.append("\n") # restore the newline input stripped
else:
line = self.ts.readline() # includes the "\n" at EOL
if line == "": # file reads return "" at EOF
self.EOF = True # got an EOF
self.char_buffer.clear()
return False
self.char_buffer = collections.deque([c for c in line])
return True
[docs] def peek(self):
"""Peeks one character ahead in the text stream. Returns empty string if
peek is attempted after `end_of_text_stream()` is `True`."""
self.refill_char_buffer_if_empty()
if len(self.char_buffer) == 0:
return ""
return self.char_buffer[0]
def __increment_counters(self, char):
"""A utility routine for counting the current lines and characters. Called
each time a character is read."""
self.curr_char_num += 1
if self.increment_line_on_next_char:
self.curr_char_num = 1
self.curr_line_num += 1
self.increment_line_on_next_char = False
if char == "\n":
self.increment_line_on_next_char = True
[docs] def get_pos_of_last_next(self):
"""Return a tuple containing the line number of the last character
returned, numbered from 1, followed by the position of the character on
that line, also numbered from 1. Useful for error messages."""
return (self.curr_line_num, self.curr_char_num)
[docs] def end_of_text_stream(self):
"""True if the last character in the text stream has been returned."""
self.refill_char_buffer_if_empty()
return self.EOF
[docs] def beginning_of_text_stream(self):
return self.curr_line_num == 1 and self.curr_char_num == 0
#
# Run test cases.
#
if __name__ == "__main__":
#import py.test
#py.test.main(["-v", "test/test_text_stream.py"]) # this needs pytest 2.0
# exit(0) # comment this out to test interactive
print("\nTest interactive...\n")
readline.parse_and_bind('set editing-mode vi')
ts = TextStream()
ts.set_raw_in() # reads data from raw_input