-
Notifications
You must be signed in to change notification settings - Fork 0
/
state.py
133 lines (115 loc) · 4.15 KB
/
state.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
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
# Symbol: {name}
# Symbols table : { {name} {type:0=terminal / 1=non-terminal} }
# Rule: {LHS} {RHS: sequence of symbol names} {index of ·} {0=not visited / 1=visited}
# state: {index} {Rules} {leaf}
# Transitions graph: {List: {index of Source State} {index of Distination State} {transition symbol}}
class State:
_n=0 # start index of states (made to change manually here)
_count=0+_n
graph=[]
def __init__(self):
self.rules = []
self._i = State._count
self.hasreduce=0 # The state has a rule to reduce
State._count = State._count+1
def add_rule(self, rule):
'''add new rule to the state'''
if rule not in self.rules:
self.rules.append(rule)
if rule._closure==-1:
self.hasreduce=1
def goto(self, distination_index, symbol):
'''
add a transition -with a symbol, between a state and its successor
'''
g = [self._i, distination_index, symbol]
if g not in State.graph:
State.graph.append(g)
def closure(self): # closure operation
for rule in self.rules:
if rule.visited:
continue
for r in rule.visit():
if r not in self.rules:
self.add_rule(r)
def __eq__(self, s):
"If self rules in s rules"
if not isinstance(s, State):
# don't attempt to compare against unrelated types
# https://stackoverflow.com/a/1227325
return NotImplemented
eq = True
if self.rules.__len__() > s.rules.__len__():
return False
for r in self.rules:
eq = eq and (r in s.rules)
return eq
def __str__(self):
s = []
max_len=1
for r in self.rules:
line=' ['+str(r)
s.append(line)
if len(line) > max_len: max_len=len(line)
# make all line with the same length
for i in range(len(s)):
pad = max_len-len(s[i])
s[i]=s[i]+' '*pad+']'
s.insert(0,''.join(['I',str(self._i),':',' '*(max_len-2)]))
return '\n'.join(s)
class Rule:
_n=0 # start index of augmented grammar
augmented = []
def __init__(self, lhs, rhs=[], dot_index=0):
self.lhs = lhs
if rhs == ['!εpslon']:
self.rhs=[]
else:
self.rhs = rhs
self._closure = dot_index
if self.dotatend():
self._closure = -1
self.visited = 0
def __str__(self):
rhs = list(self.rhs)
dot = self._closure
if dot == -1:
dot = len(rhs)
rhs.insert(dot, '•')
return self.lhs + ' → ' + ' '.join(rhs)
def __eq__(self, rule):
if not isinstance(rule, Rule):
# don't attempt to compare against unrelated types
# https://stackoverflow.com/a/1227325
return NotImplemented
return self.lhs == rule.lhs and self.rhs == rule.rhs and self._closure == rule._closure
def handle(self):
return self.rhs[self._closure]
def visit(self):
'''
Mark rule as visited and expand in their state
'''
self.visited = 1
# If dot is not at the end of rule and is a handle(non-terminal)
if self._closure != -1:
handle = self.rhs[self._closure]
if handle.startswith('`'):
return [r.copy() for r in Rule.augmented if r.lhs == handle]
return []
def dotatend(self):
'''
checks if we reachs the end if the rule
'''
if self._closure == len(self.rhs):
return True
return False
def movedot(self):
'''
move the . closure and return a new rule for a new state
'''
if self._closure == -1:
return None
return Rule(self.lhs, self.rhs, self._closure + 1)
def copy(self):
'''ignore colsure and visited attributes'''
return Rule(self.lhs,self.rhs)