-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathistream.py
419 lines (342 loc) · 11.6 KB
/
istream.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# istream.py
"""
input streams for Python
History:
16-Dec-2006: implemented PeekStream:peekStr()
10-Apr-2007: added IStream:grabToString()
2-May-2007: added IStream:grabToBefore()
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Class hierarchy;
IStream (ab)
IFile -- a readable file
PeekStream (ab)
ScanString -- a scannable string
FileWrapper
"""
import string
debug = False # debugging this module?
#---------------------------------------------------------------------
DIGITS = "0123456789"
LETTERSU = string.ascii_letters + "_"
IDENTCHARS = LETTERSU + string.digits
def cin(c, s):
""" is c in s? If c is empty, return False.
@param c [string] = a character or ''
@param s [string] = a string; c must match one char in s
@return [bool]
"""
if c=='': return False
return c in s
def isDigit(c):
return cin(c, DIGITS)
def isLetterU(c):
return cin(c, LETTERSU)
def isIdentChar(c):
return cin(c, IDENTCHARS)
#---------------------------------------------------------------------
class IStream:
#========================================================
# to be implemented by subclasses:
def get(self):
""" Get the next character in the stream
@return [string] length 1; or '' if EOF
"""
raise NotImplementedError
def eof(self):
""" are we at the end of this stream?
@return [bool]
"""
raise NotImplementedError
#========================================================
def getChar(self):
""" alias for get() """
return self.get()
def getLine(self):
""" Get the next line, including '\n' at end
@return [string]
"""
if debug:
print "*** in getLine() ***"
line = ""
while 1:
nextChar = self.get()
if debug:
print "*** in getLine() 2. nextChar=%r ***" % (nextChar,)
if nextChar == "": return line
if debug:
print "*** in getLine() 3. nextChar=%r ***" % (nextChar,)
line += nextChar
if nextChar == "\n": return line
def getLines(self):
""" Get all the lines
@return [list of string]
"""
lines = []
while 1:
line = self.getLine()
if line == "": return lines
lines += [line]
def getAll(self):
""" Get all the characters in the stream as a string
@return [string]
"""
lines = self.getLines()
return string.join(lines, "")
def getChars(self, n =-1):
""" Get (n) characters in the stream. If n<0, get the lot.
@return [string]
"""
#---------------------------------------------------------------------
"""
A wrapper round a file object
Instance variables
~~~~~~~~~~~~~~~~~~
atEnd [bool] are we at the end of the file?
f [file] the underlying file object
"""
class IFile(IStream):
def __init__(self, file= None, filename= None):
"""
Create a new IFile. Either file or filename must be set.
@param file [file] = a file object
@param filename = a filename or pathname
"""
if file:
self.f = file
elif filename:
pass
else:
msg = "Error in creating IFile; must include file or"\
" filename (file=%r, filename=%r)" % (file, filename)
raise Exception(msg)
self.atEnd = False
def get(self):
ch = self.f.read(1)
if len(ch)==0: self.atEnd = True
return ch
def eof(self):
return self.atEnd
#---------------------------------------------------------------------
class PeekStream(IStream):
#========================================================
# to be implemented by subclasses:
def peek(self, lookahead=0):
""" Returns a peek of one char. If lookahead==0, returns the
next to be written in the stream.
@param n [int]
@return [string] length 1; or "" if no more chars
at relevant position
"""
raise NotImplementedError
#========================================================
def eof(self):
return self.peekChar()==''
def peekChar(self, lookahead=0):
""" alias for peek() """
return self.peek(lookahead)
def yyypeekStr(self, n, lookahead=0):
""" peekStr(n) returns a peek of the next (n) chars; however
peekStr(n, f) returns a peek of the next (n) characters,
starting from an offset of (f) characters ahead from the
current position.
@param n [int]
@param lookahead [int] = where to start looking from
@return [string] length n or shorter if no more characters
"""
def isNext(self, matchStr):
""" Are the next chars are (matchStr)?
@param matchStr [string]
@return [bool]
"""
return self.peekStr(len(matchStr)) == matchStr
def isNextWord(self):
""" Is what follows a C-style identifier?
@return [bool]
"""
ch = self.peek()
return isLetterU(ch)
def grabWord(self):
""" grab a C-style identifier. Throw away characters until
we're at the start of one, then greedily grab all of it.
@return [string] = the identifier, or '' if couldn't get one
"""
while 1:
if self.peek()=='': return ''
if self.isNextWord(): break
self.get()
r = ''
r = self.get()
while isIdentChar(self.peek()):
r += self.get()
return r
def isNextInt(self):
""" Is what follows an integer? (ie optional '-'
followed by 1 or more [0-9]
@return [bool]
"""
c = self.peek()
if c=='': return False
if isDigit(c): return True
if c=="-" and isDigit(self.peek(1)): return True
return False
def grabInt(self, notFound=None):
""" Scan forward until an int is found, then return it.
If no int found, return (notFound)
@return [int]
"""
while not self.isNextInt():
c = self.get()
if c == '': return notFound
r = self.get()
while isDigit(self.peek()):
r += self.get()
return int(r)
def isNextSkip(self, matchStr):
""" Are the next chars are (matchStr)? If so, skip them
and return True.
@param matchStr [string]
@return [bool]
"""
lm = len(matchStr)
isNext = (self.peekStr(lm) == matchStr)
if isNext: self.getChars(lm)
return isNext
def skipPast(self, s):
""" Keep consuming characters until the most recently
consumed are the string (s)
@param s [string]
@return [bool] True if skipped any characters
"""
ls = len(s)
if ls==0: return False
while 1:
if self.isNextSkip(s) or self.eof(): return True
self.get()
def grabToString(self, s):
""" Keep reading characters until we either reach the end of
the stream, or the most-recently-read characters are the string (s).
If (s) is '', don't read any characters.
Return all the characters read, including the (s) at the end.
@param s [string]
@return [string] = all the characters that have been grabbed
"""
if s=="": return ""
charsRead = ""
lens = len(s)
while 1:
if self.eof(): return charsRead
charsRead += self.get()
if len(charsRead)>= lens and charsRead[-lens:]==s:
# we've just read (s), so quit
return charsRead
#//while
def grabToBefore(self, s):
""" Keep reading characters until we either reach the end of
the stream, or the next-to-be-read characters are the string (s).
Return the characters grabbed. If what's next in the stream is
(s), return an empty string.
@param s [string] = a string which must follow the characters
to be grabbed
@return [string] = the characters grabbed
"""
grabbed = ""
while 1:
if self.isNext(s): return grabbed
if self.eof(): return grabbed
grabbed += self.get()
def isNextSkip_emptyLine(self):
""" Are the next characters an empty line?
If so, skip them and return True. If not, return False.
An "empty line" means "\n\n".
@return [bool]
"""
return self.isNextSkip("\n\n")
def skipPastSet(self, chars):
""" Keep consuming characters until the next char to be read
isn't in the set (chars),
skip past it.
@param chars [string]
@return [bool] True if skipped any characters
"""
r = False
while 1:
ch = self.peek()
if ch=='': break
if ch not in chars: break
self.get()
r = True
return r
#---------------------------------------------------------------------
class ScanString(PeekStream):
def __init__(self, s):
""" create a scannable string
@param s [string]
"""
if debug and type(s)!=str:
print "ScanString:__init__(%r) ***NOT A STRING***" % (s,)
assert type(s)==str
self.s = s
self.at = 0
#========================================================
# inherited from superclasses:
def get(self):
ch = self.s[self.at:self.at+1]
self.at += 1
return ch
def getChars(self, n=-1):
if n<0:
r = self.s[self.at:]
self.at = len(self.s)
else:
ixto = self.at + n
r = self.s[self.at:ixto]
self.at += n
if self.at > len(self.s): self.at = len(self.s)
return r
def peek(self, lookahead=0):
ix = self.at + lookahead
result = self.s[ix:ix+1]
if debug:
print "ScanString:peek(%r) ix=%r result=%r"\
% (lookahead, ix, result)
return result
def peekStr(self, n, lookahead=0):
""" peekStr(n) returns a peek of the next (n) chars; however
peekStr(n, f) returns a peek of the next (n) characters,
starting from an offset of (f) characters ahead from the
current position.
@param n [int]
@param lookahead [int] = where to start looking from
@return [string] length n or shorter if no more characters
"""
ixto = lookahead + self.at + n
return self.s[lookahead + self.at : ixto]
#========================================================
#---------------------------------------------------------------------
"""
A wrapper around a file object (i.e. an object created with the Python
built-in file() function)
"""
class FileWrapper(PeekStream):
def __init__(self, file= None, filename= None):
"""
@param file [file] = a file object
@param filename = a filename or pathname
"""
if file:
self.f = file
elif filename:
pass
else:
msg = "Error in creating FileWrapper; must include file or"\
" filename (file=%r, filename=%r)" % (file, filename)
raise Exception(msg)
#========================================================
# inherited from superclasses
def get(self):
""" return the next character
return [string]
"""
#========================================================
#---------------------------------------------------------------------
#end