ungetch in Python

B

Bill Trenker

Scott said:
I'm writing a small parser for a minilanguage in Python,

Hi Scott,

You might want to have a look at "pyparsing -- an object-oriented approach to text processing in Python".

http://pyparsing.sourceforge.net

The Python source code is only 34K, or just under 1000 lines of code, so it may be small enough to include with your project and save you the trouble of writing yet another parser.

Regards,
Bill
 
S

Scott Fenton

Hello and merry chrismas/new years/<insert winter holiday> everyone,

I'm writing a small parser for a minilanguage in Python,
and I was wondering --- is there any equiv. of C's ungetch
or Scheme's peek-char in python? That is, is there a way to
look at a character without taking it off of the input stream
or is there a way to put it back on the input stream afterwards?
I googled around and didn't come up with anything that didn't
involve curses, which I don't want to use. Any help?

TIA
-Scott
 
A

anton muhin

Scott said:
Hello and merry chrismas/new years/<insert winter holiday> everyone,

I'm writing a small parser for a minilanguage in Python,
and I was wondering --- is there any equiv. of C's ungetch
or Scheme's peek-char in python? That is, is there a way to
look at a character without taking it off of the input stream
or is there a way to put it back on the input stream afterwards?
I googled around and didn't come up with anything that didn't
involve curses, which I don't want to use. Any help?

TIA
-Scott

Merry christmas & Happy New Year!

First of all I'd rather suggest to use one of parsers for Python---there
are plenty of them and they are really nice.

To your question: I don't remember this kind of function is Standard
Lib, but chances are it is there. But still you can emulate it with your
own iterator class.

Here comes an example:

class UngetIter(object):
def __init__(self, it):
self.it_ = iter(it)
self.hasUnget_ = False
self.last_ = None

def __iter__(self):
return self

def next(self):
if self.hasUnget_:
self.hasUnget_ = False
return self.last_

return self.it_.next()

def unget(self, o):
assert not self.hasUnget_, 'Only one unget allowed!'
self.hasUnget_ = True
self.last_ = o

i = UngetIter('abc')
for n, e in enumerate(i):
if n % 2 == 1:
print 'unget', e
i.unget(e)
else:
print e

Warning: almost not tested.

If you want to unget more than one char, you can just use a list of
objects to unget.

regards,
anton.
 
E

Erik Max Francis

Scott said:
I'm writing a small parser for a minilanguage in Python,
and I was wondering --- is there any equiv. of C's ungetch
or Scheme's peek-char in python? That is, is there a way to
look at a character without taking it off of the input stream
or is there a way to put it back on the input stream afterwards?
I googled around and didn't come up with anything that didn't
involve curses, which I don't want to use. Any help?

Not internally but it's awfully easy to write a wrapper around a file
class which does this.
 
S

Stephen Ferg

I've written a set of classes that wrap a file or a list. The heart of
the matter (at least for what you're interested in doing) is the
"current()" method, which allows you to specify an offset, which
allows you to lookahead or backward.


#-------------------------------------------------------------
#
# class: InputList
#
#-------------------------------------------------------------
class InputList:
"""This class holds a list of strings.
The list supports file-like operations.
Subclasses can have the __init__() method over-ridden,
in order to be loaded from files.
"""
def __init__(self, aList):
"""the aList argument should be a list of strings.
"""
self.items = aList
self.reset()
return

def __len__(self):
return len(self.items)

def reset(self):
# removed and converted to a method --- self.last_index() =
len(self.items) - 1
self.index = -1
return

def last_index(self):
return len(self.items) - 1

def getnext(self):
self.index = self.index + 1
return self.current()

def get(self):
return self.getnext()


def current(self, lookahead_count = 0,
value_to_be_returned_if_past_end=""):
"""return the item at the current position, unless we are past the
last item,
in which case return value_to_be_returned_if_past_end.
"""
lookahead_index = self.index + lookahead_count
if lookahead_index > self.last_index(): return
value_to_be_returned_if_past_end
else: return self.items[lookahead_index]


def eof(self,lookahead_count = 0):
"""look ahead lookahead_count items, and see if that position is
past
the end of the file. Return 1 if it is, otherwise return 0.
"""
lookahead_index = self.index + lookahead_count
if lookahead_index > self.last_index(): return 1
else: return 0

def more(self):
"""The opposite of eof. A convenience feature.
"""
if self.eof() == 0: return 1
return 0


def getIndex(self):
return self.index


def current_count(self):
return self.index + 1

def sortInsensitive(self):
"""A case-insensitive sort, in place.
Uppercase A sorts before lower-case A.
"""
temp = []
for item in self.items:
temp.append((item.upper(), item))
temp.sort()

self.items = []
for item in temp:
self.items.append(item[1])

del temp
return None


def size(self):
return len(self.items)

def remove(self, comment_indicator = None):
""" Remove lines from the list
if comment_indicator = None, remove all blank lines.
Otherwise, remove all lines that have comment_indicator in column 1.
"""
indexes = range(len(self.items))
indexes.reverse()
if comment_indicator != None: cLength = len(comment_indicator)
for i in indexes:
if comment_indicator == None:
if self.items.strip() == "": del self.items
else:
if self.items.strip()[:cLength] == comment_indicator: del
self.items


def remove_trailing(self, comment_indicator = None):
""" Remove lines from the list
Like remove(), but works from the trailing end of the list, and
works
only as long as it finds a line that can be removed.

if comment_indicator = None, remove blank lines.
Otherwise, remove lines that have comment_indicator in column 1.
"""
indexes = range(len(self.items))
indexes.reverse()
if comment_indicator != None: cLength = len(comment_indicator)
for i in indexes:
if comment_indicator == None:
if self.items.strip() == "": del self.items
else: break
else:
if self.items.strip()[:cLength] == comment_indicator: del
self.items
else: break

def remove_leading(self, comment_indicator = None):
""" Remove lines from the list
Like remove(), but works from the front end of the list, and works
only as long as it finds a line that can be removed.

if comment_indicator = None, remove blank lines.
Otherwise, remove lines that have comment_indicator in column 1.
"""
indexes = range(len(self.items))
indexes_to_remove = []

if comment_indicator != None: cLength = len(comment_indicator)
for i in indexes:
if comment_indicator == None:
if self.items.strip() == "": indexes_to_remove.append(i)
else: break
else:
if self.items.strip()[:cLength] == comment_indicator:
indexes_to_remove.append(i)
else: break

if len(indexes_to_remove) > 0:
indexes_to_remove.reverse()
for i in indexes_to_remove:
del self.items


def close(self):
return

def strip(self):
# strip trailing whitespace (including newlines) from each line
i = 0
while i < len(self.items):
self.items = string.rstrip(self.items)
i = i + 1

#-------------------------------------------------------------
# end of class: InputList
#-------------------------------------------------------------


#-------------------------------------------------------------
#class: InputFileOrStream
#-------------------------------------------------------------
class InputFileOrStream(InputList):

def delete(self):
if f_exists(self.myFilename): f_delete(self.myFilename)


def filename(self):
return self.myFilename


#-------------------------------------------------------------
# end of class: InputFileOrStream
#-------------------------------------------------------------



#-------------------------------------------------------------
#class: InputFile
#-------------------------------------------------------------
class InputFile(InputFileOrStream):
"""Read a file as a list of lines.
"""
"""This class is an abstract class for InputFile and Input Stream.

_______ How to use it: code example ___________
import fpx
infile = fpx.InputFile(infile_name)
#or infile = fpx.InputStream(infile_name)

infile.getnext()
while not infile.eof():
print infile.current_count(),infile.current()
infile.getnext()
print "Processed", infile.total_count(), "lines in file",
infile.filename()
"""

def __init__(self, name):
"""the name argument should be a text string containing
the name of the file to be read in.
"""

# open the file and read it into a list, and close it
self.myFilename = os.path.normcase(name)
file = open(self.myFilename, "r")
self.items = file.readlines()
file.close()

self.strip()
self.reset()


def linein(self):
"""A method for REXX compatibility.
"""
return self.getnext()

def getline(self):
return self.getnext()


#-------------------------------------------------------------
# end of class: InputFile
#-------------------------------------------------------------




#-------------------------------------------------------------
# class: InputStream
#-------------------------------------------------------------
class InputStream(InputFileOrStream):
"""Read a file as a list of characters.
"""

def __init__(self, name):
"""The name argument should be a text string containing
the name of the file to be read in.
"""

# open the file and read it into a list, and close it
self.myFilename = os.path.normcase(name)
file = open(self.myFilename, "r")
self.items = file.read()
file.close()
self.myLineIndex = -1
self.myCharIndexInLine = -1

self.reset()
return


def charin(self):
"""Method for REXX compatibility.
"""
return self.getchar()


def getchar(self):
if self.myLineIndex == -1:
self.myLineIndex = 0
self.myCharIndexInLine = 0
else:
if self.current() == "\n":
self.myLineIndex += 1
self.myCharIndexInLine = 0
else:
self.myCharIndexInLine += 1

return self.getnext()


#-------------------------------------------------------------
# end of class: InputStream
#-------------------------------------------------------------
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,173
Messages
2,570,939
Members
47,484
Latest member
JackRichard

Latest Threads

Top