forked from aixp/pycoco
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathactions.py
More file actions
247 lines (202 loc) · 7.08 KB
/
Copy pathactions.py
File metadata and controls
247 lines (202 loc) · 7.08 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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
__copyright__ = """
Compiler Generator Coco/R,
Copyright (c) 1990, 2004 Hanspeter Moessenboeck, University of Linz
extended by M. Loeberbauer & A. Woess, Univ. of Linz
ported from Java to Python by Ronald Longo
improved and refactored by KOLANICH
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
As an exception, it is allowed to write an extension of Coco/R that is used as a plugin in non-free software.
If not otherwise stated, any source code generated by Coco/R (other than Coco/R itself) does not fall under the GNU General Public License.
""" # pylint: disable=duplicate-code
import copy
import sys
import typing
from enum import IntEnum
from CoCoRuntime.errors import Errors
from .CharClass import CharClass
from .nodes import Node
from .symbols import SymbolTokensKinds
class TransitionCode(IntEnum):
normalTrans = 0 # transition codes
contextTrans = 1
class Target:
"""set of states that are reached by an action"""
__slots__ = ("state", "next")
def __init__(self, s: "State") -> None:
assert isinstance(s, State), s
self.state = s # target state
self.next = None # Target instance
class Action:
"""action of finite automaton"""
__slots__ = ("sym", "tc", "target", "next", "dfa")
def __init__(self, sym: int, tc: TransitionCode, dfa: "DFA") -> None:
assert isinstance(sym, int)
assert isinstance(tc, int)
self.sym = sym # action symbol
self.tc = tc # transition code
self.target = None # Target # states reached from this action
self.next = None # Action
self.dfa = dfa
def AddTarget(self, t: Target) -> None:
"""add t to the action.targets"""
assert isinstance(t, Target)
last = None # Target
p = self.target # Target
while (p is not None) and (t.state.nr >= p.state.nr):
if t.state == p.state:
return
last = p
p = p.next
t.next = p
if p == self.target:
self.target = t
else:
last.next = t
def AddTargets(self, a: "Action") -> None:
"""add copy of a.targets to action.targets"""
assert isinstance(a, __class__) # pylint:disable=undefined-variable
p = a.target
while p is not None:
t = Target(p.state)
self.AddTarget(t)
p = p.next
if a.tc == TransitionCode.contextTrans:
self.tc = TransitionCode.contextTrans
def Symbols(self) -> typing.Set[typing.Union[int, str]]:
return {self.sym}
def ShiftWith(self, s: typing.Set[int]) -> None:
if len(s) == 1:
self.__class__ = CharAct
self.sym = min(s) # .First()
else:
c = self.dfa.charClassStorage.Find(s)
if c is None:
c = self.dfa.charClassStorage.spawnDummy(s) # class with dummy name
self.__class__ = CharClassAct
self.sym = c.n
def GetTargetStates(self, meltedListHead: "Melted", param: typing.List[None], errors: "Errors") -> bool:
assert isinstance(param, list) # Object[]
# compute the set of target states
targets = set() # BitSet
endOf = None # Symbol
ctx = False # boolean
t = self.target
while t is not None:
stateNr = t.state.nr # int
if stateNr <= self.dfa.lastSimState:
targets.add(stateNr)
else:
try:
targets |= Melted.Set(meltedListHead, stateNr)
except BaseException:
print(sys.exc_info()[1])
Errors.count += 1
if t.state.endOf is not None:
if (endOf is None) or (endOf == t.state.endOf):
endOf = t.state.endOf
else:
errors.storeError(-1, -1, "Tokens " + endOf.name + " and " + t.state.endOf.name + " cannot be distinguished")
if t.state.ctx:
ctx = True
t = t.next
param[0] = targets
param[1] = endOf
return ctx
class State:
"""state of finite automaton"""
__slots__ = ("nr", "firstAction", "endOf", "ctx", "next", "dfa")
def __init__(self, no: int, dfa: "DFA") -> None:
self.nr = no # state number
self.firstAction = None # Action, to first action of this state
self.endOf = None # Symbol, recognized token if state is final
self.ctx = False # true if state is reached via contextTrans
self.next = None # State
self.dfa = dfa
@property
def isTrivialFinal(self) -> bool:
return not self.ctx and not self.firstAction and self.endOf.tokenKind != SymbolTokensKinds.classLitToken
def AddAction(self, act: "Action") -> None:
assert isinstance(act, Action)
lasta = None # Action
a = self.firstAction # Action
while (a is not None) and (act.prio >= a.prio):
lasta = a
a = a.next
# collecting classes at the beginning gives better performance
act.next = a
if a == self.firstAction:
self.firstAction = act
else:
lasta.next = act
def DetachAction(self, act: "Action") -> None:
assert isinstance(act, Action)
lasta = None # Action
a = self.firstAction # Action
while (a is not None) and a != act:
lasta = a
a = a.next
if a is not None:
if a == self.firstAction:
self.firstAction = a.next
else:
lasta.next = a.next
def TheAction(self, ch: str, charClassStorage: "CharClassStorage") -> typing.Optional["Action"]:
assert isinstance(ch, str)
if isinstance(ch, str):
ch = ord(ch)
a = self.firstAction
while a is not None:
if isinstance(a, CharAct) and ch == a.sym:
return a
if isinstance(a, CharClassAct):
s = charClassStorage.Set(a.sym)
if ch in s:
return a
a = a.next
return None
def MeltWith(self, s: "State") -> None:
"""copy actions of s to state"""
assert isinstance(s, State)
action = s.firstAction
while action is not None:
a = action.__class__(action.sym, action.tc, self.dfa)
a.AddTargets(action)
self.AddAction(a)
action = action.next
class Melted: # info about melted states
__slots__ = ("set", "state", "next")
def __init__(self, nextMeltedListItem: "Melted", st: typing.Set[int], state: State) -> None:
"""Prepends state to the list, returnsnew list head"""
assert isinstance(st, set)
assert isinstance(state, State)
self.set = st # set of old states
self.state = state # new state
self.next = nextMeltedListItem # Melted instance
@staticmethod
def StateWithSet(firstMelted: typing.Optional["Melted"], s: typing.Set[int]) -> typing.Optional["Melted"]:
assert isinstance(s, set)
m = firstMelted
while m is not None:
if s == m.set: # s.equals( m.set ):
return m
m = m.next
return None
@staticmethod
def Set(firstMelted: "Melted", nr):
assert isinstance(nr, int)
m = firstMelted
while m is not None:
if m.state.nr == nr:
return m.set
m = m.next
raise RuntimeError("-- compiler error in Melted.Set()")
class CharClassAct(Action):
__slots__ = ()
prio = 1
def Symbols(self) -> typing.Union[typing.Set[int], typing.Set[typing.Union[int, str]]]:
return copy.copy(self.dfa.charClassStorage.Set(self.sym))
class CharAct(Action):
__slots__ = ()
prio = 2