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