117 lines
3.1 KiB
Python
117 lines
3.1 KiB
Python
import operator
|
|
|
|
class AstNode(object):
|
|
def __init__( self, opr, left, right ):
|
|
self.opr = opr
|
|
self.l = left
|
|
self.r = right
|
|
|
|
def eval(self):
|
|
return self.opr(self.l.eval(), self.r.eval())
|
|
|
|
class LeafNode(object):
|
|
def __init__( self, valStrg ):
|
|
self.v = int(valStrg)
|
|
|
|
def eval(self):
|
|
return self.v
|
|
|
|
class Yaccer(object):
|
|
def __init__(self):
|
|
self.operstak = []
|
|
self.nodestak =[]
|
|
self.__dict__.update(self.state1)
|
|
|
|
def v1( self, valStrg ):
|
|
# Value String
|
|
self.nodestak.append( LeafNode(valStrg))
|
|
self.__dict__.update(self.state2)
|
|
#print 'push', valStrg
|
|
|
|
def o2( self, operchar ):
|
|
# Operator character or open paren in state1
|
|
def openParen(a,b):
|
|
return 0 # function should not be called
|
|
|
|
opDict= { '+': ( operator.add, 2, 2 ),
|
|
'-': (operator.sub, 2, 2 ),
|
|
'*': (operator.mul, 3, 3 ),
|
|
'/': (operator.div, 3, 3 ),
|
|
'^': ( pow, 4, 5 ), # right associative exponentiation for grins
|
|
'(': ( openParen, 0, 8 )
|
|
}
|
|
operPrecidence = opDict[operchar][2]
|
|
self.redeuce(operPrecidence)
|
|
|
|
self.operstak.append(opDict[operchar])
|
|
self.__dict__.update(self.state1)
|
|
# print 'pushop', operchar
|
|
|
|
def syntaxErr(self, char ):
|
|
# Open Parenthesis
|
|
print 'parse error - near operator "%s"' %char
|
|
|
|
def pc2( self,operchar ):
|
|
# Close Parenthesis
|
|
# reduce node until matching open paren found
|
|
self.redeuce( 1 )
|
|
if len(self.operstak)>0:
|
|
self.operstak.pop() # pop off open parenthesis
|
|
else:
|
|
print 'Error - no open parenthesis matches close parens.'
|
|
self.__dict__.update(self.state2)
|
|
|
|
def end(self):
|
|
self.redeuce(0)
|
|
return self.nodestak.pop()
|
|
|
|
def redeuce(self, precidence):
|
|
while len(self.operstak)>0:
|
|
tailOper = self.operstak[-1]
|
|
if tailOper[1] < precidence: break
|
|
|
|
tailOper = self.operstak.pop()
|
|
vrgt = self.nodestak.pop()
|
|
vlft= self.nodestak.pop()
|
|
self.nodestak.append( AstNode(tailOper[0], vlft, vrgt))
|
|
# print 'reduce'
|
|
|
|
state1 = { 'v': v1, 'o':syntaxErr, 'po':o2, 'pc':syntaxErr }
|
|
state2 = { 'v': syntaxErr, 'o':o2, 'po':syntaxErr, 'pc':pc2 }
|
|
|
|
|
|
def Lex( exprssn, p ):
|
|
bgn = None
|
|
cp = -1
|
|
for c in exprssn:
|
|
cp += 1
|
|
if c in '+-/*^()': # throw in exponentiation (^)for grins
|
|
if bgn is not None:
|
|
p.v(p, exprssn[bgn:cp])
|
|
bgn = None
|
|
if c=='(': p.po(p, c)
|
|
elif c==')':p.pc(p, c)
|
|
else: p.o(p, c)
|
|
elif c in ' \t':
|
|
if bgn is not None:
|
|
p.v(p, exprssn[bgn:cp])
|
|
bgn = None
|
|
elif c in '0123456789':
|
|
if bgn is None:
|
|
bgn = cp
|
|
else:
|
|
print 'Invalid character in expression'
|
|
if bgn is not None:
|
|
p.v(p, exprssn[bgn:cp])
|
|
bgn = None
|
|
|
|
if bgn is not None:
|
|
p.v(p, exprssn[bgn:cp+1])
|
|
bgn = None
|
|
return p.end()
|
|
|
|
|
|
expr = raw_input("Expression:")
|
|
astTree = Lex( expr, Yaccer())
|
|
print expr, '=',astTree.eval()
|