-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmain.py
More file actions
203 lines (180 loc) · 6.15 KB
/
main.py
File metadata and controls
203 lines (180 loc) · 6.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# Author: Max Base
# Date: 2020/06/17
# Web: maxbase.org
# Repo: https://github.com/BaseMax/CFG2CNF
import sys
print("Enter your grammers with `S -> ab` similar style, Also you can split grammers by | and even write grammers in diffrent lines\nand finaly type *:")
moves={}
rules={}
# alphas=list(map(chr, range(97, 123)))
alphas=['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
while True:
line=input()
if line == "*":
break
parts=line.split("->")
if len(parts) != 2:
print(f"Error parsing line: {line}. Format should be 'A -> B'")
continue
parts[0]=parts[0].strip()
parts[1]=parts[1].strip()
parts[1]=parts[1].split("|")
# print(parts)
if not parts[0] in rules:
rules[parts[0]]=[]
for part in parts[1]:
part=part.strip()
# print(part)
rules[parts[0]].append(part)
def new_name_for_grammer(array):
names=list(array)
# print(names)
for alpha in alphas:
if alpha not in names:
return alpha
print("Error: cannot find a new and fresh name for new Grammer statement!")
sys.exit(-1)
def delta_add_other_case_for_his(array, key):
index = 0
count = len(array[key])
if count == 0:
return array[key]
while index < count:
if key in array[key][index]:
new_prod = array[key][index].replace(key, "")
if new_prod and new_prod not in array[key]:
array[key].append(new_prod)
index+=1
return array[key]
def delta_add_other_case_for_others(array, target):
for key in list(array):
if key != target:
index = 0
count = len(array[key])
while index < count:
if target in array[key][index]:
array[key].append(array[key][index].replace(target, ""))
index+=1
return array
def move_terminals_to_new_grammers(array):
# print("Result until this step:", array)
for key in list(array):
# print("\n\n= checking new key:", key)
index = 0
count = len(array[key])
# print("Count of", key,"grammer is",count)
while index < count:
value=array[key][index]
length=len(value)
# print("\n---- parsing", value)
# print("Value as", array[key])
i=0
while i < length:
# print("* Value as", array[key])
if str(value[i]).islower():
if value[i] not in list(moves):
name=new_name_for_grammer(array)
moves[value[i]]=name
# print("Create", name,"statement for",value[i],"terminal char!",)
array[name]=[]
array[name].append(value[i])
else:
name=moves[value[i]]
# print("found", value[i], "in", array[key][index], "will rename to", name)
array[key][index]=array[key][index].replace(value[i], name)
# print(array[key])
# print(value[i])
i+=1
index+=1
return array
def replace_grammer_statement_names(array, key, char):
# If the character is a non-terminal (already defined)
if char in array:
return array
# If it's not in moves values, it's a new non-terminal we need to handle
if char not in list(moves.values()):
print(f"Warning: Found non-terminal {char} in {key} statement that isn't from a terminal conversion")
# Let's not exit since this is valid for existing non-terminals
return array
def formatter_grammers(array):
for key in list(array):
index = 0
count = len(array[key])
while index < count:
# if count == 1:
# elif count >= 2:
length=len(array[key][index])
if length == 1:
if not array[key][index][0].islower():
array=replace_grammer_statement_names(array, key, array[key][index])
elif length > 2:
# print("Found a non-standard grammer:", array[key][index])
values=array[key][index][1:]
name=new_name_for_grammer(array)
# print("Create", name,"statement for", values)
array[name]=[]
array[name].append(values)
array[key][index]=array[key][index].replace(values, name)
index+=1
return array
# Remove epsilon productions
def remove_epsilon_productions(array):
has_epsilon = {}
# Find all non-terminals that derive epsilon
for key in list(array):
if "$" in array[key]:
has_epsilon[key] = True
array[key].remove("$")
# Add new productions
for key in list(array):
new_productions = []
for production in array[key]:
for nullable in has_epsilon:
if nullable in production:
# Add new production without the nullable symbol
new_prod = production.replace(nullable, "", 1)
if new_prod and new_prod not in array[key] and new_prod not in new_productions:
new_productions.append(new_prod)
array[key].extend(new_productions)
return array
# Remove unit productions (A -> B)
def remove_unit_productions(array):
changed = True
while changed:
changed = False
for key in list(array):
unit_prods = []
for i, prod in enumerate(array[key]):
if len(prod) == 1 and prod.isupper():
unit_prods.append((i, prod))
# Process unit productions
for i, unit in sorted(unit_prods, reverse=True):
array[key].pop(i)
if unit in array:
for replacement in array[unit]:
if replacement not in array[key]:
array[key].append(replacement)
changed = True
return array
# Apply the CFG to CNF conversion
def convert_to_cnf(array):
# Step 1: Remove epsilon productions
array = remove_epsilon_productions(array)
# Step 2: Remove unit productions
array = remove_unit_productions(array)
# Step 3: Move terminals to new grammars
array = move_terminals_to_new_grammers(array)
# Step 4: Format grammers (split productions with more than 2 non-terminals)
array = formatter_grammers(array)
return array
# Print the grammar in a nice format
def print_grammar(array):
print("\nResulting CNF Grammar:")
for key in sorted(array.keys()):
productions = " | ".join(array[key]) if array[key] else "$"
print(f"{key} -> {productions}")
# Process the input grammar
print("\nProcessing grammar...")
rules = convert_to_cnf(rules)
print_grammar(rules)
print("\nConversion completed successfully.")