-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpredictive.py
98 lines (86 loc) · 3.98 KB
/
predictive.py
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
class PredictiveParser:
def __init__(self, grammar):
self.grammar = grammar
self.first = {}
self.follow = {}
self.build_first()
self.build_follow()
def build_first(self):
for non_terminal in self.grammar:
self.first[non_terminal] = set()
self.calculate_first(non_terminal)
def calculate_first(self, non_terminal):
if non_terminal in self.first:
return self.first[non_terminal]
for production in self.grammar[non_terminal]:
for symbol in production:
if symbol.isupper():
self.first[non_terminal] |= self.calculate_first(symbol)
if 'ε' not in self.first[symbol]:
break
else:
self.first[non_terminal].add(symbol)
break
return self.first[non_terminal]
def build_follow(self):
start_symbol = list(self.grammar.keys())[0]
self.follow[start_symbol] = {'$'}
for non_terminal in self.grammar:
self.follow[non_terminal] = set()
while True:
updated = False
for non_terminal in self.grammar:
for production in self.grammar[non_terminal]:
for i, symbol in enumerate(production):
if symbol.isupper():
first_of_next = self.first[production[i+1]] if i < len(production) - 1 else {'$'}
if 'ε' in self.first[symbol]:
if i == len(production) - 1:
updated |= self.follow[non_terminal] != self.follow[non_terminal] | self.follow[symbol]
self.follow[non_terminal] |= self.follow[symbol]
else:
updated |= self.follow[non_terminal] != self.follow[non_terminal] | (first_of_next - {'ε'})
self.follow[non_terminal] |= first_of_next - {'ε'}
else:
updated |= self.follow[non_terminal] != self.follow[non_terminal] | first_of_next
self.follow[non_terminal] |= first_of_next
if not updated:
break
def parse(self, input_string):
stack = ['$']
input_string += '$'
output = []
current_symbol = input_string[0]
input_pointer = 1
while len(stack) > 0:
top_of_stack = stack.pop()
if top_of_stack == current_symbol:
if current_symbol == '$':
break
output.append(f"Matched {current_symbol}")
current_symbol = input_string[input_pointer]
input_pointer += 1
elif top_of_stack in self.grammar:
if current_symbol not in self.grammar[top_of_stack]:
return "Parsing Error: Unexpected symbol"
output.append(f"Expanded {top_of_stack} -> {' '.join(self.grammar[top_of_stack][current_symbol])}")
stack.extend(reversed(self.grammar[top_of_stack][current_symbol]))
else:
return "Parsing Error: Unexpected symbol"
return "\n".join(output)
def get_grammar_from_user():
grammar = {}
while True:
non_terminal = input("Enter non-terminal (or press Enter to finish): ").strip()
if not non_terminal:
break
productions = input(f"Enter productions for {non_terminal}: ").strip().split('|')
grammar[non_terminal] = [p.strip().split() for p in productions]
return grammar
def main():
user_grammar = get_grammar_from_user()
parser = PredictiveParser(user_grammar)
input_string = input("Enter input string: ")
print(parser.parse(input_string))
if __name__ == "__main__":
main()