mirror of
ssh://git.janware.com/srv/git/janware/proj/jw-python
synced 2026-01-15 09:53:32 +01:00
Add algo.ShuntingYard
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
6f1fcdc693
commit
81ca793a6e
2 changed files with 216 additions and 0 deletions
4
tools/python/jwutils/algo/Makefile
Normal file
4
tools/python/jwutils/algo/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
TOPDIR = ../../../..
|
||||
|
||||
include $(TOPDIR)/make/proj.mk
|
||||
include $(MODDIR)/make/py-mod.mk
|
||||
212
tools/python/jwutils/algo/ShuntingYard.py
Normal file
212
tools/python/jwutils/algo/ShuntingYard.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
from collections import namedtuple
|
||||
import re
|
||||
|
||||
Operator = namedtuple("Operator", [ "func", "nargs" , "prec" ])
|
||||
|
||||
class Stack:
|
||||
|
||||
def __init__(self, itemlist=[]):
|
||||
self.items = itemlist
|
||||
|
||||
def isEmpty(self):
|
||||
if self.items == []:
|
||||
return True
|
||||
return False
|
||||
|
||||
def peek(self):
|
||||
return self.items[-1:][0]
|
||||
|
||||
def pop(self):
|
||||
return self.items.pop()
|
||||
|
||||
def push(self, item):
|
||||
self.items.append(item)
|
||||
return 0
|
||||
|
||||
class ShuntingYard(object): # export
|
||||
|
||||
def __init__(self, operators):
|
||||
self.__ops = {}
|
||||
for k, v in operators.iteritems():
|
||||
self.add_operator(k, v.func, v.nargs, v.prec)
|
||||
|
||||
def tokenize(self, spec):
|
||||
|
||||
regex = ""
|
||||
for k in self.__ops.keys():
|
||||
regex = regex + "|" + re.escape(k)
|
||||
|
||||
regex = regex[1:]
|
||||
|
||||
scanner = re.Scanner([
|
||||
(regex, lambda scanner,token:("kw", token)),
|
||||
(r"\w+", lambda scanner,token:("arg", token)),
|
||||
(r"\s+", None), # None == skip token.
|
||||
])
|
||||
|
||||
tokens, remainder = scanner.scan(spec)
|
||||
if len(remainder)>0:
|
||||
raise Expression("Failed to tokenize " + spec + ", remaining bit is ", remainder)
|
||||
|
||||
print tokens
|
||||
r = []
|
||||
for e in tokens:
|
||||
r.append(e[1])
|
||||
|
||||
return r
|
||||
|
||||
def add_operator(self, name, func, nargs, precedence):
|
||||
self.__ops[name] = Operator(func, nargs, precedence)
|
||||
|
||||
def infix_to_postfix(self, infix):
|
||||
|
||||
s = Stack()
|
||||
r = []
|
||||
tokens = self.tokenize(infix)
|
||||
|
||||
for token in tokens:
|
||||
|
||||
print "Checking token ", token
|
||||
|
||||
if token not in self.__ops.keys():
|
||||
r.append(token)
|
||||
continue
|
||||
|
||||
if token == '(':
|
||||
s.push(token)
|
||||
continue
|
||||
|
||||
if token == ')':
|
||||
topToken = s.pop()
|
||||
while topToken != '(':
|
||||
r.append(topToken)
|
||||
topToken = s.pop()
|
||||
continue
|
||||
|
||||
while (not s.isEmpty()) and (self.__ops[s.peek()].prec >= self.__ops[token].prec):
|
||||
#print token
|
||||
r.append(s.pop())
|
||||
#print r
|
||||
|
||||
s.push(token)
|
||||
print (s.peek())
|
||||
|
||||
while not s.isEmpty():
|
||||
opToken = s.pop()
|
||||
r.append(opToken)
|
||||
#print r
|
||||
|
||||
return r
|
||||
#return " ".join(r)
|
||||
|
||||
def eval_postfix(self, postfixexpr):
|
||||
|
||||
vals = Stack()
|
||||
|
||||
for token in postfixexpr:
|
||||
|
||||
if token not in self.__ops.keys():
|
||||
vals.push(token)
|
||||
continue
|
||||
|
||||
op = self.__ops[token]
|
||||
args = []
|
||||
#print "Adding %d arguments" % (op.nargs)
|
||||
for i in range(0, op.nargs):
|
||||
#print "Adding argument %d" % (i)
|
||||
args.append(vals.pop())
|
||||
val = op.func(*reversed(args))
|
||||
print "%s(%s) = %s" % (token, ', '.join(reversed(args)), val)
|
||||
vals.push(val)
|
||||
|
||||
return vals.pop()
|
||||
|
||||
def eval(self, infix):
|
||||
postfix = self.infix_to_postfix(infix)
|
||||
print infix, "-->", postfix
|
||||
return self.eval_postfix(postfix)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# ------------- testbed calculator
|
||||
|
||||
from string import atof
|
||||
|
||||
class Calculator(ShuntingYard):
|
||||
|
||||
def tokenize(self, string):
|
||||
return string.split()
|
||||
|
||||
def f_mult(self, a, b):
|
||||
return str(atof(a) * atof(b));
|
||||
|
||||
def f_div(self, a, b):
|
||||
return str(atof(a) / atof(b));
|
||||
|
||||
def f_add(self, a, b):
|
||||
return str(atof(a) + atof(b));
|
||||
|
||||
def f_sub(self, a, b):
|
||||
return str(atof(a) - atof(b));
|
||||
|
||||
def __init__(self):
|
||||
Op = Operator
|
||||
operators = {
|
||||
'/': Op(self.f_div, 2, 3),
|
||||
'*': Op(self.f_mult, 2, 3),
|
||||
'+': Op(self.f_add, 2, 2),
|
||||
'-': Op(self.f_sub, 2, 2),
|
||||
'(': Op(None, 2, 1),
|
||||
')': Op(None, 2, 1)
|
||||
}
|
||||
super(Calculator, self).__init__(operators)
|
||||
|
||||
print Calculator().eval("( 2 * 3 + 4 * 5 ) / ( 5 - 3 )")
|
||||
|
||||
# ------------- testbed match object
|
||||
|
||||
Object = namedtuple("Object", [ "Name", "Label" ])
|
||||
|
||||
class Matcher(ShuntingYard):
|
||||
|
||||
def f_is_name(self, a):
|
||||
if obj.Name == a:
|
||||
return "True"
|
||||
return "False"
|
||||
|
||||
def f_matches_label(self, a):
|
||||
if re.match(a, obj.Label):
|
||||
return "True"
|
||||
return "False"
|
||||
|
||||
def f_is_not(self, a):
|
||||
if a == "True":
|
||||
return "False"
|
||||
return "False"
|
||||
|
||||
def f_and(self, a_, b_):
|
||||
a = a_ == "True"
|
||||
b = b_ == "True"
|
||||
if a and b:
|
||||
return "True"
|
||||
return "False"
|
||||
|
||||
def __init__(self, obj):
|
||||
Op = Operator
|
||||
operators = {
|
||||
'(': Op(None, 2, 1),
|
||||
')': Op(None, 2, 1),
|
||||
'name=': Op(self.f_is_name, 1, 3),
|
||||
'and': Op(self.f_and, 2, 3),
|
||||
'label~=': Op(self.f_matches_label, 1, 3),
|
||||
'False': Op(None, 0, 3),
|
||||
'True': Op(None, 0, 3),
|
||||
'not': Op(self.f_is_not, 1, 3),
|
||||
}
|
||||
super(Matcher, self).__init__(operators)
|
||||
|
||||
obj = Object("hans", "wurst")
|
||||
|
||||
r = Matcher(obj).eval("name=hans and (not label~=worst)")
|
||||
print "Result =", r
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue