mirror of
ssh://git.janware.com/srv/git/janware/proj/jw-python
synced 2026-01-15 01:52:56 +01:00
Re-implement ShuntingYard.infix_to_postfix()
The previous implementation had no concept of associativity. Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
81ca793a6e
commit
1259b16837
1 changed files with 118 additions and 29 deletions
|
|
@ -1,7 +1,9 @@
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import re
|
import re
|
||||||
|
|
||||||
Operator = namedtuple("Operator", [ "func", "nargs" , "prec" ])
|
Operator = namedtuple("Operator", [ "func", "nargs" , "prec", "assoc" ]) # export
|
||||||
|
L, R = 'Left Right'.split()
|
||||||
|
ARG, KEYW, LPAREN, RPAREN = 'arg kw ( )'.split()
|
||||||
|
|
||||||
class Stack:
|
class Stack:
|
||||||
|
|
||||||
|
|
@ -25,10 +27,22 @@ class Stack:
|
||||||
|
|
||||||
class ShuntingYard(object): # export
|
class ShuntingYard(object): # export
|
||||||
|
|
||||||
def __init__(self, operators):
|
def __init__(self, operators = None):
|
||||||
|
self.debug = True
|
||||||
self.__ops = {}
|
self.__ops = {}
|
||||||
for k, v in operators.iteritems():
|
if operators is not None:
|
||||||
self.add_operator(k, v.func, v.nargs, v.prec)
|
for k, v in operators.iteritems():
|
||||||
|
self.add_operator(k, v.func, v.nargs, v.prec, v.assoc)
|
||||||
|
|
||||||
|
def token_string():
|
||||||
|
for k, v in self.__ops:
|
||||||
|
buf = ", " + k
|
||||||
|
if v.nargs > 1:
|
||||||
|
buf += "xxx"
|
||||||
|
r = r + buf
|
||||||
|
if len(r):
|
||||||
|
return r[2:]
|
||||||
|
return r
|
||||||
|
|
||||||
def tokenize(self, spec):
|
def tokenize(self, spec):
|
||||||
|
|
||||||
|
|
@ -46,25 +60,96 @@ class ShuntingYard(object): # export
|
||||||
|
|
||||||
tokens, remainder = scanner.scan(spec)
|
tokens, remainder = scanner.scan(spec)
|
||||||
if len(remainder)>0:
|
if len(remainder)>0:
|
||||||
raise Expression("Failed to tokenize " + spec + ", remaining bit is ", remainder)
|
raise Exception("Failed to tokenize " + spec + ", remaining bit is ", remainder)
|
||||||
|
|
||||||
print tokens
|
#print tokens
|
||||||
|
return tokens
|
||||||
r = []
|
r = []
|
||||||
for e in tokens:
|
for e in tokens:
|
||||||
r.append(e[1])
|
r.append(e[1])
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def add_operator(self, name, func, nargs, precedence):
|
def add_operator(self, name, func, nargs, precedence, assoc):
|
||||||
self.__ops[name] = Operator(func, nargs, precedence)
|
self.__ops[name] = Operator(func, nargs, precedence, assoc)
|
||||||
|
|
||||||
def infix_to_postfix(self, infix):
|
def infix_to_postfix(self, infix):
|
||||||
|
tokenized = self.tokenize(infix)
|
||||||
|
print "tokenized = ", tokenized
|
||||||
|
outq, stack = [], []
|
||||||
|
table = ['TOKEN,ACTION,RPN OUTPUT,OP STACK,NOTES'.split(',')]
|
||||||
|
for toktype, token in tokenized:
|
||||||
|
print "Checking token", token
|
||||||
|
note = action = ''
|
||||||
|
if toktype == ARG:
|
||||||
|
action = 'Add arg to output'
|
||||||
|
outq.append(token)
|
||||||
|
table.append( (token, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
|
||||||
|
elif toktype == KEYW:
|
||||||
|
val = self.__ops[token]
|
||||||
|
t1, (f1, n1, p1, a1) = token, val
|
||||||
|
v = t1
|
||||||
|
note = 'Pop ops from stack to output'
|
||||||
|
while stack:
|
||||||
|
t2, (f2, n2, p2, a2) = stack[-1]
|
||||||
|
if (a1 == L and p1 <= p2) or (a1 == R and p1 < p2):
|
||||||
|
if t1 != RPAREN:
|
||||||
|
if t2 != LPAREN:
|
||||||
|
stack.pop()
|
||||||
|
action = '(Pop op)'
|
||||||
|
outq.append(t2)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if t2 != LPAREN:
|
||||||
|
stack.pop()
|
||||||
|
action = '(Pop op)'
|
||||||
|
outq.append(t2)
|
||||||
|
else:
|
||||||
|
stack.pop()
|
||||||
|
action = '(Pop & discard "(")'
|
||||||
|
table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
|
||||||
|
break
|
||||||
|
table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
|
||||||
|
v = note = ''
|
||||||
|
else:
|
||||||
|
note = ''
|
||||||
|
break
|
||||||
|
note = ''
|
||||||
|
note = ''
|
||||||
|
if t1 != RPAREN:
|
||||||
|
stack.append((token, val))
|
||||||
|
action = 'Push op token to stack'
|
||||||
|
else:
|
||||||
|
action = 'Discard ")"'
|
||||||
|
table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
|
||||||
|
note = 'Drain stack to output'
|
||||||
|
while stack:
|
||||||
|
v = ''
|
||||||
|
t2, (f2, n2, p2, a2) = stack[-1]
|
||||||
|
action = '(Pop op)'
|
||||||
|
stack.pop()
|
||||||
|
outq.append(t2)
|
||||||
|
table.append( (v, action, ' '.join(outq), ' '.join(s[0] for s in stack), note) )
|
||||||
|
v = note = ''
|
||||||
|
if self.debug:
|
||||||
|
maxcolwidths = [len(max(x, key=len)) for x in zip(*table)]
|
||||||
|
row = table[0]
|
||||||
|
print( ' '.join('{cell:^{width}}'.format(width=width, cell=cell) for (width, cell) in zip(maxcolwidths, row)))
|
||||||
|
for row in table[1:]:
|
||||||
|
print( ' '.join('{cell:<{width}}'.format(width=width, cell=cell) for (width, cell) in zip(maxcolwidths, row)))
|
||||||
|
return table[-1][2].split()
|
||||||
|
|
||||||
|
def infix_to_postfix_orig(self, infix):
|
||||||
|
|
||||||
s = Stack()
|
s = Stack()
|
||||||
r = []
|
r = []
|
||||||
tokens = self.tokenize(infix)
|
tokens = self.tokenize(infix)
|
||||||
|
|
||||||
for token in tokens:
|
for tokinfo in tokens:
|
||||||
|
|
||||||
|
print tokinfo
|
||||||
|
toktype, token = tokinfo[0], tokinfo[1]
|
||||||
|
|
||||||
print "Checking token ", token
|
print "Checking token ", token
|
||||||
|
|
||||||
|
|
@ -105,16 +190,18 @@ class ShuntingYard(object): # export
|
||||||
|
|
||||||
for token in postfixexpr:
|
for token in postfixexpr:
|
||||||
|
|
||||||
|
print "Checking token %s" % (token)
|
||||||
if token not in self.__ops.keys():
|
if token not in self.__ops.keys():
|
||||||
vals.push(token)
|
vals.push(token)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
op = self.__ops[token]
|
op = self.__ops[token]
|
||||||
args = []
|
args = []
|
||||||
#print "Adding %d arguments" % (op.nargs)
|
print "Adding %d arguments" % (op.nargs)
|
||||||
for i in range(0, op.nargs):
|
for i in range(0, op.nargs):
|
||||||
#print "Adding argument %d" % (i)
|
print "Adding argument %d" % (i)
|
||||||
args.append(vals.pop())
|
args.append(vals.pop())
|
||||||
|
#print "running %s(%s)" % (token, ', '.join(reversed(args)))
|
||||||
val = op.func(*reversed(args))
|
val = op.func(*reversed(args))
|
||||||
print "%s(%s) = %s" % (token, ', '.join(reversed(args)), val)
|
print "%s(%s) = %s" % (token, ', '.join(reversed(args)), val)
|
||||||
vals.push(val)
|
vals.push(val)
|
||||||
|
|
@ -134,8 +221,8 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
class Calculator(ShuntingYard):
|
class Calculator(ShuntingYard):
|
||||||
|
|
||||||
def tokenize(self, string):
|
#def tokenize(self, string):
|
||||||
return string.split()
|
# return string.split()
|
||||||
|
|
||||||
def f_mult(self, a, b):
|
def f_mult(self, a, b):
|
||||||
return str(atof(a) * atof(b));
|
return str(atof(a) * atof(b));
|
||||||
|
|
@ -152,16 +239,18 @@ if __name__ == '__main__':
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Op = Operator
|
Op = Operator
|
||||||
operators = {
|
operators = {
|
||||||
'/': Op(self.f_div, 2, 3),
|
'^': Op(None, 2, 4, R),
|
||||||
'*': Op(self.f_mult, 2, 3),
|
'*': Op(self.f_mult, 2, 3, L),
|
||||||
'+': Op(self.f_add, 2, 2),
|
'/': Op(self.f_div, 2, 3, L),
|
||||||
'-': Op(self.f_sub, 2, 2),
|
'+': Op(self.f_add, 2, 2, L),
|
||||||
'(': Op(None, 2, 1),
|
'-': Op(self.f_sub, 2, 2, L),
|
||||||
')': Op(None, 2, 1)
|
'(': Op(None, 0, 9, L),
|
||||||
|
')': Op(None, 0, 0, L),
|
||||||
}
|
}
|
||||||
super(Calculator, self).__init__(operators)
|
super(Calculator, self).__init__(operators)
|
||||||
|
|
||||||
print Calculator().eval("( 2 * 3 + 4 * 5 ) / ( 5 - 3 )")
|
rr = Calculator().eval("( 2 * 3 + 4 * 5 ) / ( 5 - 3 )")
|
||||||
|
print "Result =", rr
|
||||||
|
|
||||||
# ------------- testbed match object
|
# ------------- testbed match object
|
||||||
|
|
||||||
|
|
@ -182,7 +271,7 @@ if __name__ == '__main__':
|
||||||
def f_is_not(self, a):
|
def f_is_not(self, a):
|
||||||
if a == "True":
|
if a == "True":
|
||||||
return "False"
|
return "False"
|
||||||
return "False"
|
return "True"
|
||||||
|
|
||||||
def f_and(self, a_, b_):
|
def f_and(self, a_, b_):
|
||||||
a = a_ == "True"
|
a = a_ == "True"
|
||||||
|
|
@ -194,14 +283,14 @@ if __name__ == '__main__':
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
Op = Operator
|
Op = Operator
|
||||||
operators = {
|
operators = {
|
||||||
'(': Op(None, 2, 1),
|
'(': Op(None, 2, 9, L),
|
||||||
')': Op(None, 2, 1),
|
')': Op(None, 2, 0, L),
|
||||||
'name=': Op(self.f_is_name, 1, 3),
|
'name=': Op(self.f_is_name, 1, 3, R),
|
||||||
'and': Op(self.f_and, 2, 3),
|
'and': Op(self.f_and, 2, 3, L),
|
||||||
'label~=': Op(self.f_matches_label, 1, 3),
|
'label~=': Op(self.f_matches_label, 1, 3, R),
|
||||||
'False': Op(None, 0, 3),
|
'False': Op(None, 0, 3, L),
|
||||||
'True': Op(None, 0, 3),
|
'True': Op(None, 0, 3, L),
|
||||||
'not': Op(self.f_is_not, 1, 3),
|
'not': Op(self.f_is_not, 1, 3, R),
|
||||||
}
|
}
|
||||||
super(Matcher, self).__init__(operators)
|
super(Matcher, self).__init__(operators)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue