def __transform(self, ast): root_type = type(ast) # will be AST class for AND or FOLLOWEDBY changed = False or_children = [] other_children = [] for child in ast.operands: if isinstance(child, OrObservationExpression): or_children.append(child.operands) else: other_children.append(child) if or_children: distributed_children = [ root_type([ _dupe_ast(sub_ast) for sub_ast in itertools.chain( other_children, prod_seq, ) ]) for prod_seq in itertools.product(*or_children) ] # Need to recursively continue to distribute AND/FOLLOWEDBY over OR # in any of our new sub-expressions which need it. distributed_children = [ self.transform(child)[0] for child in distributed_children ] result = OrObservationExpression(distributed_children) changed = True else: result = ast return result, changed
def _dupe_ast(ast): """ Create a duplicate of the given AST. The AST root must be an observation expression of some kind (AND/OR/qualified, etc). Note: the observation expression "leaves", i.e. simple square-bracket observation expressions are currently not duplicated. I don't think it's necessary as of this writing. But revisit this if/when necessary. Args: ast: The AST to duplicate Returns: The duplicate AST """ if isinstance(ast, AndObservationExpression): result = AndObservationExpression( [_dupe_ast(child) for child in ast.operands]) elif isinstance(ast, OrObservationExpression): result = OrObservationExpression( [_dupe_ast(child) for child in ast.operands]) elif isinstance(ast, FollowedByObservationExpression): result = FollowedByObservationExpression( [_dupe_ast(child) for child in ast.operands]) elif isinstance(ast, QualifiedObservationExpression): # Don't need to dupe the qualifier object at this point result = QualifiedObservationExpression( _dupe_ast(ast.observation_expression), ast.qualifier, ) elif isinstance(ast, ObservationExpression): result = ast else: raise TypeError("Can't duplicate " + type(ast).__name__) return result
def __transform(self, ast): # If no OR children, nothing to do if any( isinstance(child, OrObservationExpression) for child in ast.operands): # When we distribute FOLLOWEDBY over OR, it is important to # preserve the original FOLLOWEDBY order! We don't need to do that # for AND, but we do it anyway because it doesn't hurt, and we can # use the same code for both. iterables = [] for child in ast.operands: if isinstance(child, OrObservationExpression): iterables.append(child.operands) else: iterables.append((child, )) root_type = type(ast) # will be AST class for AND or FOLLOWEDBY distributed_children = [ root_type([ _dupe_ast(sub_ast) for sub_ast in itertools.chain(prod_seq, ) ]) for prod_seq in itertools.product(*iterables) ] # Need to recursively continue to distribute AND/FOLLOWEDBY over OR # in any of our new sub-expressions which need it. distributed_children = [ self.transform(child)[0] for child in distributed_children ] result = OrObservationExpression(distributed_children) changed = True else: result = ast changed = False return result, changed