forked from timkchan/scheme
-
Notifications
You must be signed in to change notification settings - Fork 0
/
buffer.py
127 lines (110 loc) · 3.72 KB
/
buffer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
"""The buffer module assists in iterating through lines and tokens."""
import math
class Buffer:
"""A Buffer provides a way of accessing a sequence of tokens across lines.
Its constructor takes an iterator, called "the source", that returns the
next line of tokens as a list each time it is queried, or None to indicate
the end of data.
The Buffer in effect concatenates the sequences returned from its source
and then supplies the items from them one at a time through its pop()
method, calling the source for more sequences of items only when needed.
In addition, Buffer provides a current method to look at the
next item to be supplied, without sequencing past it.
The __str__ method prints all tokens read so far, up to the end of the
current line, and marks the current token with >>.
>>> buf = Buffer(iter([['(', '+'], [15], [12, ')']]))
>>> buf.pop()
'('
>>> buf.pop()
'+'
>>> buf.current()
15
>>> print(buf)
1: ( +
2: >> 15
>>> buf.pop()
15
>>> buf.current()
12
>>> buf.pop()
12
>>> print(buf)
1: ( +
2: 15
3: 12 >> )
>>> buf.pop()
')'
>>> print(buf)
1: ( +
2: 15
3: 12 ) >>
>>> buf.pop() # returns None
"""
def __init__(self, source):
self.index = 0
self.lines = []
self.source = source
self.current_line = ()
self.current()
def pop(self):
"""Remove the next item from self and return it. If self has
exhausted its source, returns None."""
current = self.current()
self.index += 1
return current
def current(self):
"""Return the current element, or None if none exists."""
while not self.more_on_line:
self.index = 0
try:
self.current_line = next(self.source)
self.lines.append(self.current_line)
except StopIteration:
self.current_line = ()
return None
return self.current_line[self.index]
@property
def more_on_line(self):
return self.index < len(self.current_line)
def __str__(self):
"""Return recently read contents; current element marked with >>."""
# Format string for right-justified line numbers
n = len(self.lines)
msg = '{0:>' + str(math.floor(math.log10(n))+1) + "}: "
# Up to three previous lines and current line are included in output
s = ''
for i in range(max(0, n-4), n-1):
s += msg.format(i+1) + ' '.join(map(str, self.lines[i])) + '\n'
s += msg.format(n)
s += ' '.join(map(str, self.current_line[:self.index]))
s += ' >> '
s += ' '.join(map(str, self.current_line[self.index:]))
return s.strip()
# Try to import readline for interactive history
try:
import readline
except:
pass
class InputReader:
"""An InputReader is an iterable that prompts the user for input."""
def __init__(self, prompt):
self.prompt = prompt
def __iter__(self):
while True:
yield input(self.prompt)
self.prompt = ' ' * len(self.prompt)
class LineReader:
"""A LineReader is an iterable that prints lines after a prompt."""
def __init__(self, lines, prompt, comment=";"):
self.lines = lines
self.prompt = prompt
self.comment = comment
def __iter__(self):
while self.lines:
line = self.lines.pop(0).strip('\n')
if (self.prompt is not None and line != "" and
not line.lstrip().startswith(self.comment)):
print(self.prompt + line)
self.prompt = ' ' * len(self.prompt)
yield line
raise EOFError