Rev 166 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
#!/usr/bin/env python
# Copyright: Ira W. Snyder
# Start Date: 2005-11-18
# End Date:
# License: Public Domain
#
# Changelog Follows:
#
# 2005-11-18
# * Just getting the basics in place, since we haven't been given
# the whole description of the project yet.
#
# 2005-11-26
# * Got the whole class working, now to implement pretty-printing
#
# 2005-11-28
# * This has pretty-printing now. (The derivation looks correct)
# * Needs testing still, but for every case I can come up with, it works.
#
# Check for <Python-2.3 compatibility (boolean values)
try:
True, False
except NameError:
(True, False) = (1, 0)
import sys
# Default Symbols
SYM_E = "A" # "E"
SYM_T = "B" # "T"
SYM_P = "C" # "P"
SYM_F = "F"
SYM_N = "N"
SYM_V = "V"
SYM_D = "D"
SYM_EPRM = "E'"
SYM_TPRM = "T'"
SYM_PPRM = "P'"
SYM_EMPTY = ""
class RecursiveDescentParser:
def __init__(self):
self.__clear()
def __clear(self):
self.str = "" # the string of input to test
self.strpos = 0 # the current position in str
self.derivation = ""
def __replace_sym(self, to_repl, with_this):
"""Replace a string in the derivation with another string"""
pos = self.derivation.find(to_repl)
if pos == -1:
#print 'str: %s' % (self.derivation, )
#print 'to_repl: %s' % (to_repl, )
#print 'with_this: %s' % (with_this, )
raise ValueError
self.derivation = self.derivation[:pos] + str(with_this) + self.derivation[pos+len(to_repl):]
def __input_test_str(self):
self.str = raw_input("input str: ")
def main_menu(self):
done = False
while not done:
print 'Menu:'
print '========================================'
print '1. Test a string'
print '2. Quit'
s = raw_input('Choice >>> ')
if s == '1':
self.__clear()
self.__input_test_str()
self.__test_str()
elif s == '2':
done = True
else:
print 'Bad Selection'
def __print_deriv(self, with_arrow=True):
if with_arrow:
print "%s ->" % (self.derivation, ),
else:
print "%s" % (self.derivation, )
def __test_str(self):
self.derivation = SYM_E[:]
procE_ans = self.procE()
all_input_used = (len(self.str) == self.strpos)
ans = procE_ans and all_input_used
self.__print_deriv(with_arrow=False)
print 'Parsing: %s' % (ans and 'Completed' or 'Failed', )
if not ans:
print 'All input used: %s' % (all_input_used and 'True' or 'False', )
def procE(self):
self.__print_deriv()
# Catch the exception generated below if we don't have enough
# input to 'look ahead'
try:
self.__replace_sym(SYM_E, SYM_T + SYM_EPRM)
return self.procT() and self.procEprm()
except IndexError:
print 'Not enough input to continue, failed.'
return False
def procEprm(self):
self.__print_deriv()
# Check empty str
if self.strpos >= len(self.str):
self.__replace_sym(SYM_EPRM, SYM_EMPTY)
return True
# Check +
if self.str[self.strpos] == '+':
self.strpos += 1
self.__replace_sym(SYM_EPRM, '+' + SYM_T + SYM_EPRM)
return self.procT() and self.procEprm()
# Check -
if self.str[self.strpos] == '-':
self.strpos += 1
self.__replace_sym(SYM_EPRM, '-' + SYM_T + SYM_EPRM)
return self.procT() and self.procEprm()
# We accept empty str, so take it
self.__replace_sym(SYM_EPRM, SYM_EMPTY)
return True
def procT(self):
self.__print_deriv()
self.__replace_sym(SYM_T, SYM_P + SYM_TPRM)
return self.procP() and self.procTprm()
def procTprm(self):
self.__print_deriv()
# Check empty str
if self.strpos >= len(self.str):
self.__replace_sym(SYM_TPRM, SYM_EMPTY)
return True
# Check *
if self.str[self.strpos] == '*':
self.strpos += 1
self.__replace_sym(SYM_TPRM, '*' + SYM_P + SYM_TPRM)
return self.procP() and self.procTprm()
# we accept empty str, so take it
self.__replace_sym(SYM_TPRM, SYM_EMPTY)
return True
def procP(self):
self.__print_deriv()
self.__replace_sym(SYM_P, SYM_F + SYM_PPRM)
return self.procF() and self.procPprm()
def procPprm(self):
self.__print_deriv()
# Check empty str
if self.strpos >= len(self.str):
self.__replace_sym(SYM_PPRM, SYM_EMPTY)
return True
# check exp symbol
if self.str[self.strpos] == '^':
self.strpos += 1
self.__replace_sym(SYM_PPRM, '^' + SYM_F + SYM_PPRM)
return self.procF() and self.procPprm()
# we accept empty str, so take it
self.__replace_sym(SYM_PPRM, SYM_EMPTY)
return True
def procF(self):
self.__print_deriv()
# Check x
if self.str[self.strpos] == 'x':
self.strpos += 1
self.__replace_sym(SYM_F, 'x' + SYM_V)
return self.procV()
# Check (E)
if self.str[self.strpos] == '(':
self.strpos += 1
self.__replace_sym(SYM_F, '(' + SYM_E + ')')
if self.procE():
if self.str[self.strpos] == ')':
self.strpos += 1
return True
return False
return False
# Must be a number
self.__replace_sym(SYM_F, SYM_N)
return self.procN()
def procN(self):
self.__print_deriv()
nums = range(100) # get 0-99
nums.reverse() # make it 99-0
for n in nums:
if self.str[self.strpos:self.strpos+len(str(n))] == str(n):
self.strpos += len(str(n))
self.__replace_sym(SYM_N, str(n))
return True
return False
def procV(self):
self.__print_deriv()
if self.str[self.strpos] in '0123456789':
self.strpos += 1
self.__replace_sym(SYM_V, self.str[self.strpos-1])
return True
return False
if __name__ == '__main__':
rdp = RecursiveDescentParser()
rdp.main_menu()