diff --git a/tools/python/jwutils/algo/Makefile b/tools/python/jwutils/algo/Makefile new file mode 100644 index 0000000..b77eb55 --- /dev/null +++ b/tools/python/jwutils/algo/Makefile @@ -0,0 +1,4 @@ +TOPDIR = ../../../.. + +include $(TOPDIR)/make/proj.mk +include $(MODDIR)/make/py-mod.mk diff --git a/tools/python/jwutils/algo/ShuntingYard.py b/tools/python/jwutils/algo/ShuntingYard.py new file mode 100644 index 0000000..615cefb --- /dev/null +++ b/tools/python/jwutils/algo/ShuntingYard.py @@ -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 +