Skip to content
This repository was archived by the owner on Aug 29, 2024. It is now read-only.

Commit 2e1b8f8

Browse files
committed
feat: implement command-line interface for input and output
- Developed a command-line interface (CLI) to handle user input and output. - Ensured the CLI supports various input formats and provides clear output. - Improved user interaction by adding helpful prompts and error messages.
1 parent 84efa84 commit 2e1b8f8

File tree

1 file changed

+219
-1
lines changed

1 file changed

+219
-1
lines changed

src/main.py

Lines changed: 219 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,228 @@
77
from datastructure.pda import *
88
from exceptions import *
99
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+
)
10190

11191

12192
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
14232

15233

16234
if __name__ == "__main__":

0 commit comments

Comments
 (0)