The intended functions below are the formal descriptions of
the interface. They should match the informal descriptions
given in Section 4, “The Scan class: Managing progress through a
stream”.
Before the actual class interface definition, we define one
verification function that describes the behavior of
position arguments to methods such as .tab().
#================================================================
# Verification functions
#----------------------------------------------------------------
# effective-pos(p) ==
# if p is nonnegative ->
# p
# else ->
# (length of the current line) + 1 + p
#----------------------------------------------------------------
# - - - - - c l a s s S c a n
class Scan(object):
'''Stream scanning class.
Exports:
Scan(inFile, commentPrefix=None, callback=None):
[ (inFile is a string or file-like object) and
(commentPrefix is a string or None) and
(callback is a function or None) ->
if (fileName names a readable file) ->
return a new Scan object with (commentPrefix)
as the comment character (if given) and
using (callback) as the callback procedure
(if given) and positioned at the beginning
of the first line (if there is one)
else if (fileName is a readable file handle) ->
return a new Scan object with (commentPrefix)
as the comment character (if given) and
using (callback) as the callback procedure
(if given), reading from fileName at its
current position
else ->
raise IOError ]
.atEndFile: # Read-only
[ if self is positioned at the end of the stream ->
True
else -> False ]
.line: # Read-only
[ if self is not at the end of the stream ->
the current line, with line terminator and
comment (if any) removed
else -> (undefined) ]
.rawLine: # Read-only
[ if self is not at the end of the stream ->
the current line, with line terminator removed
else -> (undefined) ]
.lineNo: # Read-only
[ the line number of the current line in self,
counting from 1 ]
.pos: # Read-only
[ if self is not at the end of the stream ->
the position within the current line, counting
from 0
else -> (undefined) ]
.close(): [ self := self, closed ]
.atEndLine():
[ if self is at the end of the stream or the end of
the current line ->
return True
else -> return False ]
.nextLine():
[ if self is at the end of the stream ->
return False
else ->
self := self advanced to the beginning of the
next line ]
.error(*L):
[ L is a list of strings ->
if any message has been issued for the current line ->
Log() +:= an error message showing the current
line and position of self, with the concatenated
elements of L used as the message
else ->
Log() +:= (current raw line) + (an error
message showing the current line and position
of self, with the concatenated elements of L
used as the message ]
.syntax(*L):
[ like .error() but raises SyntaxError after
transmitting the error message ]
.warning(*L):
[ like .error() but issues a warning rather than an
error message ]
.msgKind(kind, *L):
[ (kind is a string) and
(L is a list of strings) ->
like .error(), but issues a message of kind (kind) ]
.message(*L):
[ like .error() but uses Log().message() ]
.write(*L):
[ like .error() but uses Log().write() ]
.move(n):
[ n is a nonnegative int ->
if there are at least n characters remaining on the
current line ->
self := self with the position advanced by n
else -> raise IndexError ]
.tab(p):
[ if effective-pos(p) is within the current line ->
self := self with the current position moved to
effective-pos(p)
else -> raise IndexError ]
.isPos(p):
[ if effective-pos(p) is the current position in the
current line ->
return True
else -> return False ]
.find(s):
[ s is a string ->
if any part of the remainder of the current line
matches s ->
return the position on the current line where the
first such match begins
else -> return None ]
.upToRe(r):
[ r is a regular expression in string or compiled form ->
if any part of the remainder of the current line
matches r ->
return the position on the current line where the
first such match begins
else -> return None ]
.deblankFile():
[ self := self advanced past any leading whitespace,
over any number of lines ]
.deblankLine():
[ self := self advanced past any leading whitespace,
but not past end of line ]
.match(s):
[ s is a string ->
if the current position starts with s ->
return the position just after the match
else -> return None ]
.matchArb(s):
[ s is a string ->
if the current position starts with s, case-insensitive ->
return the position just after the match
else -> return None ]
.tabMatch(s):
[ s is a string ->
if the current line starts with s ->
self := self advanced past the match
return s
else -> return None ]
.tabMatchArb(s):
[ s is a string ->
if the current line starts with s, case-insensitive ->
self := self advanced past the match
return s
else -> return None ]
.reMatch(r):
[ r is a regular expression as a string or compiled ->
if r matches at the current position on the current
line ->
return a MatchObject representing the match
else -> return None ]
.tabReMatch(r):
[ r is a regular expression as a string or compiled ->
if r matches at the current position on the current
line ->
self := self advanced past the matching part
return a MatchObject representing the match
else -> return None ]
.integer(maxLen=None):
[ maxLen is an int, defaulting to 2**31-1 ->
if the current line at the current position starts
with one or more digits, preceded by an optional
"+" or "-" ->
self := self advanced past all that
return all that as an int
else -> return None ]
.fixed():
[ if the current line at the current position starts
with one or more digits, optionally preceded by
"+" or "-", and containing at most one "." ->
self := self advanced past all that
return all that as a float
else -> return None ]
.flatInt(n):
[ n is a positive integer ->
if the current line at the current position starts
with an integer right-justified in a field of size n ->
self := self advanced by n
return that integer as type int
else -> return None ]
The above is the exported interface. Next come the internal state variables.
State/Invariants:
.fileName:
[ if the constructor was called with a string ->
that string
else -> None ]
.file:
[ if self is closed -> None
else ->
readable file handle for the input stream ]
.commentPrefix: [ as passed to the constructor ]
.callback: [ as passed to the constructor ]
.__echoed:
[ if any messages have been issued during the current
line ->
True
else -> False ]
'''