import re from typing import List, Tuple from statemachine import State, StateMachine from statemachine.exceptions import TransitionNotAllowed class FSM(StateMachine): def __init__(self): super().__init__() self._stack = [] def process(self, node: dict): phase = node["_phase"] if phase == "enter": self.enter(node) name = re.sub(r"(? List[Tuple[str, str]]: return self._correlations class MarkerFSM(FSM): init = State(initial=True) stmt_end = State("End", final=True) receive_marker_column = State("Receiving Marker Column") receive_assignment_column = State("Receiving Assignment Column") discard_column = State("Discarding Column") binary_operation = State("Binary Operation") assignment = State("Assignment") count_assignment_marker = State("Count Assignment Marker") count_marker = State("Count Marker") leave_stmt = init.to(stmt_end, cond="outer_stmt") enter_binary_operation_expr = init.to(binary_operation, unless="logic_op") leave_binary_operation_expr = count_marker.to(init, unless="logic_op") enter_param_marker_expr = ( count_marker.to.itself() | count_assignment_marker.to.itself() ) enter_assignment = init.to(assignment) leave_assignment = count_assignment_marker.to(init) enter_column_name = ( init.to(discard_column) | binary_operation.to(receive_marker_column) | assignment.to(receive_assignment_column) ) leave_column_name = ( discard_column.to(init) | receive_marker_column.to(count_marker) | receive_assignment_column.to(count_assignment_marker) ) def __init__(self): super().__init__() self._marker_columns = [] self._assignment_columns = [] self._marker_counts = [] def assignments(self) -> List[str]: assert self.current_state.final column_names = [] for i in range(len(self._assignment_columns)): count = self._marker_counts[i] for _ in range(count): column_names.append(self._assignment_columns[i]) return column_names def markers(self) -> List[str]: assert self.current_state.final column_names = [] for i in range(len(self._marker_columns)): count = self._marker_counts[i + len(self._assignment_columns)] for _ in range(count): column_names.append(self._marker_columns[i]) return column_names @receive_marker_column.enter def save_marker_column_name(self, node: dict): if node["table"]: self._marker_columns.append(node["table"] + "." + node["name"]) else: self._marker_columns.append(node["name"]) self._marker_counts.append(0) @receive_assignment_column.enter def save_assignment_column_name(self, node: dict): if node["table"]: self._assignment_columns.append(node["table"] + "." + node["name"]) else: self._assignment_columns.append(node["name"]) self._marker_counts.append(0) @enter_param_marker_expr.on def inc_count_marker(self): self._marker_counts[-1] += 1 def logic_op(self, node: dict): if "op" in node: return node["op"] in ["and", "or"] else: return self._stack[-1]["op"] in ["and", "or"] def outer_stmt(self, node: dict): return len(self._stack) == 1