diff --git a/modules/base/pymod/table.py b/modules/base/pymod/table.py index bb68a5a57e95d402f362b2e732472cbf0e9511d4..28ed269b62979f84a15ba3f00d951ca325572e5c 100644 --- a/modules/base/pymod/table.py +++ b/modules/base/pymod/table.py @@ -67,6 +67,105 @@ def GuessColumnType(iterator): # return the last element available return possibilities.pop() +class QueryNode: + + def __init__(self,par): + self.operator=None + self.operand=None + self.children=list() + self.parent=par + + def AddChild(self): + if len(self.children)>=2: + raise RuntimeError('Cannot add more than two children') + self.children.append(QueryNode(self)) + return self.children[-1] + + def SetOperator(self,op): + if self.operand!=None: + raise RuntimeError('Cannot add an operator to a node, that is already marked as leave by an operand!') + self.operator=op + + def SetOperand(self,op): + if self.operator!=None: + raise RuntimeError('Setting an operand marks a node as leave, but there is already an operator set!') + self.operand=op + + def GetParent(self): + return self.parent + + def SetParent(self, par): + par.children.append(self) + self.parent=par + return self.parent + + def EvaluateAnd(self,lhs,rhs): + return lhs and rhs + + def EvaluateOr(self,lhs,rhs): + return lhs or rhs + + def EvaluateEqual(self,lhs,rhs): + return lhs==rhs + + def EvaluateNonEqual(self,lhs,rhs): + return lhs!=rhs + + def EvaluateLower(self,lhs,rhs): + return lhs<rhs + + def EvaluateGreater(self,lhs,rhs): + return lhs>rhs + + def EvaluateLowerEqual(self,lhs,rhs): + return lhs<=rhs + + def EvaluateGreaterEqual(self,lhs,rhs): + return lhs>=rhs + + def EvaluateOperator(self,op,lhs,rhs): + if op=='and': + return self.EvaluateAnd(lhs,rhs) + elif op=='or': + return self.EvaluateOr(lhs,rhs) + elif op=='=': + return self.EvaluateEqual(lhs,rhs) + elif op=='!=': + return self.EvaluateNonEqual(lhs,rhs) + elif op=='<': + return self.EvaluateLower(lhs,rhs) + elif op=='>': + return self.EvaluateGreater(lhs,rhs) + elif op=='<=': + return self.EvaluateLowerEqual(lhs,rhs) + elif op=='>=': + return self.EvaluateGreaterEqual(lhs,rhs) + else: + raise RuntimeError('Unknown operator: '+op) + + def EvaluateNode(self): + + #handle the case where node is a leave + if self.operand: + return self.operand + + #if there is a single logical expression, there is a root node with no + #operator... We have to evaluate its child + if len(self.children)==1: + return self.children[0].EvaluateNode() + + #something must have failed in building up the tree! + if self.operator==None: + raise RuntimeError('no operator set!') + if len(self.children)!=2: + raise RuntimeError('Node must have two children!') + + #recursively traverse binary tree + return self.EvaluateOperator(self.operator,self.children[0].EvaluateNode(),self.children[1].EvaluateNode()) + + + + class BinaryColExpr: def __init__(self, op, lhs, rhs): self.op=op @@ -819,6 +918,124 @@ Statistics for column %(col)s filt_tab.AddRow(row) return filt_tab + def Select(self, query): + + valid_operators=['and','or','=','!=','<','>','<=','>='] + + def ParseOperand(operand,row_num): + if operand=='true': + return True + if operand=='false': + return False + if operand in self.col_names: + col_index=self.GetColIndex(operand) + return self.rows[row_num][col_index] + try: + return float(operand) + except: + raise RuntimeError('Could not parse operand '+operand) + + def ParseExpression(expression): + + split_expression=list() + split_types=list() + actual_position=0 + eaten_stuff='' + + while True: + + if actual_position>=len(expression): + if len(eaten_stuff.strip())>0: + split_expression.append(eaten_stuff.strip()) + split_types.append('operand') + return split_expression,split_types + + token=expression[actual_position] + + if token=='(' or token=='[' or token=='{': + if len(eaten_stuff.strip())>0: + split_expression.append(eaten_stuff.strip()) + split_types.append('operand') + split_expression.append('(') + split_types.append('opening_bracket') + actual_position+=1 + eaten_stuff='' + continue + + if token==')' or token==']' or token=='}': + if len(eaten_stuff.strip())>0: + split_expression.append(eaten_stuff.strip()) + split_types.append('operand') + split_expression.append(')') + split_types.append('closing_bracket') + actual_position+=1 + eaten_stuff='' + continue + + found_operator=False + + for op in valid_operators: + if actual_position+len(op)>len(expression): + continue + if expression[actual_position:actual_position+len(op)]==op: + if len(eaten_stuff.strip())>0: + split_expression.append(eaten_stuff.strip()) + split_types.append('operand') + split_expression.append(op) + split_types.append('operator') + actual_position+=len(op) + eaten_stuff='' + found_operator=True + + if found_operator: + continue + + eaten_stuff+=token + actual_position+=1 + + selected_tab=Table(list(self.col_names), list(self.col_types)) + + split_expression, type_expression=ParseExpression(query) + + root=QueryNode(None) + actual_node=root + + leaves=list() + leave_operands=list() + + #build binary tree with operators as nodes and operands as leaves + for exp, typ in zip(split_expression,type_expression): + + if typ=='opening_bracket': + #add child and move down + actual_node=actual_node.AddChild() + elif typ=='operator': + #set operator, add child and move down + actual_node.SetOperator(exp) + actual_node=actual_node.AddChild() + elif typ=='operand': + #reached leave, go up again + leaves.append(actual_node) + leave_operands.append(exp) + actual_node=actual_node.GetParent() + elif typ=='closing_bracket': + if actual_node.GetParent()==None: + #move up with creating a new root + actual_node=actual_node.SetParent(QueryNode(None)) + root=actual_node + else: + #move up + actual_node=actual_node.GetParent() + + #evaluate tree for every row + for i, r in enumerate(self.rows): + for leave, operand in zip(leaves, leave_operands): + leave.SetOperand(ParseOperand(operand,i)) + if root.EvaluateNode(): + selected_tab.AddRow(r) + + return selected_tab + @staticmethod def _LoadOST(stream_or_filename): fieldname_pattern=re.compile(r'(?P<name>[^[]+)(\[(?P<type>\w+)\])?')