|
7 | 7 | from datastructure.pda import * |
8 | 8 | from exceptions import * |
9 | 9 | import os |
| 10 | +import re |
| 11 | + |
| 12 | + |
| 13 | +def _read_symbol_set(input: str) -> set[str]: |
| 14 | + if input.count("{") != 1 or input.count("}") != 1: |
| 15 | + raise InputSetInvalid("Invalid Set Input") |
| 16 | + try: |
| 17 | + input = input.split(sep="{")[1].split(sep="}")[0] |
| 18 | + except Exception as _: |
| 19 | + raise InputSetInvalid("Invalid Set Input") |
| 20 | + inputs: list[str] = list(filter(None, input.split(","))) |
| 21 | + for item in inputs: |
| 22 | + if " " in item.strip(): |
| 23 | + raise InputSetInvalid("Invalid Input with space in symbol name") |
| 24 | + return {item.strip() for item in inputs} |
| 25 | + |
| 26 | + |
| 27 | +def _cfg_input() -> CFG: |
| 28 | + print("please input your CFG") |
| 29 | + print("N = ", end="") |
| 30 | + nonterminals: set[nonterminal_t] = _read_symbol_set(input=input()) |
| 31 | + if not nonterminals: |
| 32 | + raise InputCFGInvalid("The non-terminal symbol set are empty") |
| 33 | + print("T = ", end="") |
| 34 | + terminals: set[terminal_t] = _read_symbol_set(input=input()) |
| 35 | + if nonterminals & terminals: |
| 36 | + raise InputCFGInvalid("The terminal and non-terminal symbols are repeated") |
| 37 | + |
| 38 | + print( |
| 39 | + "P: (input one empty line to end, use -> to separate the left and right sides)" |
| 40 | + ) |
| 41 | + |
| 42 | + productions: set[production_t] = set() |
| 43 | + while True: |
| 44 | + input_line: str = input() |
| 45 | + if input_line == "": |
| 46 | + break |
| 47 | + production: list[str] = input_line.split(sep="->") |
| 48 | + if len(production) != 2: |
| 49 | + raise InputCFGInvalid("Production is contains multiple '->'") |
| 50 | + left = tuple(filter(None, production[0].split(sep=" "))) |
| 51 | + for right_input in production[1].split(sep="|"): |
| 52 | + right = tuple(filter(None, right_input.split(sep=" "))) |
| 53 | + productions.add((left, right)) |
| 54 | + |
| 55 | + print("S: ", end="") |
| 56 | + start: str = input() |
| 57 | + if start not in nonterminals: |
| 58 | + raise InputCFGInvalid("S is not in N") |
| 59 | + cfg = CFG(N=nonterminals, T=terminals, P=productions, S=start) |
| 60 | + if not cfg.is_valid(): |
| 61 | + raise InputCFGInvalid("Input CFG is invalid") |
| 62 | + return cfg |
| 63 | + |
| 64 | + |
| 65 | +def _read_immediate_state( |
| 66 | + Q: set[state_t], T: set[character_t], Gamma: set[stack_symbol_t], input: str |
| 67 | +) -> tuple[state_t, character_t, stack_symbol_t]: |
| 68 | + if input.count("(") != 1 or input.count(")") != 1: |
| 69 | + raise InputSetInvalid("Invalid State Input") |
| 70 | + try: |
| 71 | + input = input.split(sep="(")[1].split(sep=")")[0] |
| 72 | + except Exception as _: |
| 73 | + raise InputSetInvalid("Invalid State Input") |
| 74 | + |
| 75 | + if input.count(",") != 2: |
| 76 | + raise InputSetInvalid("Invalid transition definition") |
| 77 | + inputs = input.split(",") |
| 78 | + immediate_state: tuple[state_t, character_t, stack_symbol_t] = ( |
| 79 | + inputs[0].strip(), |
| 80 | + inputs[1].strip(), |
| 81 | + inputs[2].strip(), |
| 82 | + ) |
| 83 | + if immediate_state[0] not in Q: |
| 84 | + raise InputSetInvalid("Invalid transition definition") |
| 85 | + if immediate_state[1] and immediate_state[1] not in T: |
| 86 | + raise InputSetInvalid("Invalid transition definition") |
| 87 | + if immediate_state[2] and immediate_state[2] not in Gamma: |
| 88 | + raise InputSetInvalid("Invalid transition definition") |
| 89 | + return immediate_state |
| 90 | + |
| 91 | + |
| 92 | +def _read_transition_set( |
| 93 | + Q: set[state_t], Gamma: set[stack_symbol_t], input: str |
| 94 | +) -> set[tuple[state_t, tuple[stack_symbol_t, ...]]]: |
| 95 | + if input.count("{") != 1 or input.count("}") != 1: |
| 96 | + raise InputSetInvalid("Invalid Set Input") |
| 97 | + try: |
| 98 | + input = input.split(sep="{")[1].split(sep="}")[0] |
| 99 | + except Exception as _: |
| 100 | + raise InputSetInvalid("Invalid Set Input") |
| 101 | + |
| 102 | + brackets = str() |
| 103 | + for character in input: |
| 104 | + if character == "(" or character == ")": |
| 105 | + brackets: str = brackets + character |
| 106 | + if ( |
| 107 | + len(brackets) % 2 == 1 |
| 108 | + or "((" in brackets |
| 109 | + or "))" in brackets |
| 110 | + or (brackets and brackets[0] == ")") |
| 111 | + or (brackets and brackets[-1] == "(") |
| 112 | + ): |
| 113 | + raise InputSetInvalid("Invalid brackets") |
| 114 | + |
| 115 | + pattern = r"\(([^)]+)\)" |
| 116 | + |
| 117 | + matches: list[str] = re.findall(pattern=pattern, string=input) |
| 118 | + |
| 119 | + result: set[tuple[state_t, tuple[stack_symbol_t, ...]]] = set() |
| 120 | + for transition in matches: |
| 121 | + content: list[str] = transition.split(sep=",") |
| 122 | + if len(content) != 2: |
| 123 | + raise InputSetInvalid("Invalid transition definition") |
| 124 | + state: state_t = content[0] |
| 125 | + stack_symbols = tuple(filter(None, content[1].split(sep=" "))) |
| 126 | + result.add((state, stack_symbols)) |
| 127 | + return result |
| 128 | + |
| 129 | + |
| 130 | +def _pda_input() -> PDA: |
| 131 | + print("please input your PDA") |
| 132 | + print("Q = ", end="") |
| 133 | + Q: set[state_t] = _read_symbol_set(input=input()) |
| 134 | + if not Q: |
| 135 | + raise InputPDAInvalid("The state set are empty") |
| 136 | + print("T = ", end="") |
| 137 | + T: set[character_t] = _read_symbol_set(input=input()) |
| 138 | + |
| 139 | + print("Gamma = ", end="") |
| 140 | + Gamma: set[stack_symbol_t] = _read_symbol_set(input=input()) |
| 141 | + |
| 142 | + print("delta: (input one empty line to end, use (?,?,?) -> {(?,?),...} format)") |
| 143 | + |
| 144 | + delta: dict[ |
| 145 | + tuple[state_t, character_t, stack_symbol_t], |
| 146 | + set[tuple[state_t, tuple[stack_symbol_t, ...]]], |
| 147 | + ] = {} |
| 148 | + while True: |
| 149 | + input_line: str = input() |
| 150 | + if input_line == "": |
| 151 | + break |
| 152 | + transitions: list[str] = input_line.split(sep="->") |
| 153 | + if len(transitions) != 2: |
| 154 | + raise InputPDAInvalid("Transition is contains multiple '->'") |
| 155 | + immediate_state: tuple[state_t, character_t, stack_symbol_t] = ( |
| 156 | + _read_immediate_state(Q=Q, T=T, Gamma=Gamma, input=transitions[0]) |
| 157 | + ) |
| 158 | + next_states: set[tuple[state_t, tuple[stack_symbol_t, ...]]] = ( |
| 159 | + _read_transition_set(Q=Q, Gamma=Gamma, input=transitions[1]) |
| 160 | + ) |
| 161 | + if immediate_state in delta.keys(): |
| 162 | + raise InputPDAInvalid("repeated transition definition") |
| 163 | + delta[immediate_state] = next_states |
| 164 | + |
| 165 | + print("q0 = ", end="") |
| 166 | + q0: state_t = input() |
| 167 | + if q0 not in Q: |
| 168 | + raise InputPDAInvalid("q0 is not in Q") |
| 169 | + print("Z0 = ", end="") |
| 170 | + Z0: stack_symbol_t = input() |
| 171 | + if Z0 not in Gamma: |
| 172 | + raise InputPDAInvalid("Z0 is not in Gamma") |
| 173 | + |
| 174 | + print("F = ", end="") |
| 175 | + F: set[state_t] = _read_symbol_set(input=input()) |
| 176 | + if not F.issubset(Q): |
| 177 | + raise InputPDAInvalid("F is not a subset of Q") |
| 178 | + |
| 179 | + pda = PDA(Q=Q, T=T, Gamma=Gamma, delta=delta, q0=q0, Z0=Z0, F=F) |
| 180 | + if not pda.is_valid(): |
| 181 | + raise InputPDAInvalid("Input PDA is invalid") |
| 182 | + |
| 183 | + return pda |
| 184 | + |
| 185 | + |
| 186 | +def _simplify_cfg(cfg: CFG) -> CFG: |
| 187 | + return eliminate_useless_symbol( |
| 188 | + cfg=eliminate_unit_production(cfg=eliminate_epsilon_production(cfg=cfg)) |
| 189 | + ) |
10 | 190 |
|
11 | 191 |
|
12 | 192 | def _solve() -> None: |
13 | | - pass |
| 193 | + print("please input an number to choose the functionality you want to use.") |
| 194 | + print("1. input a CFG to simplify") |
| 195 | + print("2. input a PDA to convert to CFG") |
| 196 | + print("(input any other thing to exit)") |
| 197 | + input_content: str = input() |
| 198 | + if input_content == "1": |
| 199 | + while True: |
| 200 | + try: |
| 201 | + cfg: CFG = _cfg_input() |
| 202 | + break |
| 203 | + except Exception as e: |
| 204 | + print(e) |
| 205 | + print("Please check you input format and retry!") |
| 206 | + try: |
| 207 | + cfg = _simplify_cfg(cfg=cfg) |
| 208 | + except Exception as e: |
| 209 | + print(e) |
| 210 | + print("#=======================") |
| 211 | + print("Here is the result.") |
| 212 | + print(cfg.to_string()) |
| 213 | + if input_content == "2": |
| 214 | + while True: |
| 215 | + try: |
| 216 | + pda: PDA = _pda_input() |
| 217 | + break |
| 218 | + except Exception as e: |
| 219 | + print(e) |
| 220 | + print("Please check you input format and retry!") |
| 221 | + if not pda.is_epda(): |
| 222 | + pda = transfer_fpda_to_epda(fpda=pda) |
| 223 | + cfg = transfer_epda_to_cfg(epda=pda) |
| 224 | + try: |
| 225 | + cfg = _simplify_cfg(cfg=cfg) |
| 226 | + print("#=======================") |
| 227 | + print("Here is the result.") |
| 228 | + print(cfg.to_string()) |
| 229 | + except Exception as e: |
| 230 | + print(e) |
| 231 | + return |
14 | 232 |
|
15 | 233 |
|
16 | 234 | if __name__ == "__main__": |
|
0 commit comments