usfmtc 0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- usfmtc/__init__.py +379 -0
- usfmtc/diagrams.py +247 -0
- usfmtc/extension.py +79 -0
- usfmtc/grammar.py +461 -0
- usfmtc/parser.py +300 -0
- usfmtc/railroad.py +1320 -0
- usfmtc/reference.py +302 -0
- usfmtc/rnc2rng/__init__.py +13 -0
- usfmtc/rnc2rng/parser.py +741 -0
- usfmtc/rnc2rng/rnctree.py +17 -0
- usfmtc/rnc2rng/serializer.py +255 -0
- usfmtc/sfmparser.py +345 -0
- usfmtc/usfmgenerate.py +156 -0
- usfmtc/usfmparser.py +590 -0
- usfmtc/usjproc.py +88 -0
- usfmtc/usx.rng +2182 -0
- usfmtc/usxgrammar.py +36 -0
- usfmtc/usxmodel.py +520 -0
- usfmtc/usxparser.py +568 -0
- usfmtc/xmlutils.py +139 -0
- usfmtc-0.2.dist-info/LICENSE.md +20 -0
- usfmtc-0.2.dist-info/METADATA +61 -0
- usfmtc-0.2.dist-info/RECORD +26 -0
- usfmtc-0.2.dist-info/WHEEL +5 -0
- usfmtc-0.2.dist-info/entry_points.txt +2 -0
- usfmtc-0.2.dist-info/top_level.txt +1 -0
usfmtc/parser.py
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
|
|
2
|
+
import logging, time, re
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
5
|
+
|
|
6
|
+
class GlobalState:
|
|
7
|
+
def __init__(self, timeout=1e7):
|
|
8
|
+
self.defstack = []
|
|
9
|
+
self.cstack = []
|
|
10
|
+
self.lasterror = None
|
|
11
|
+
self.time = time.time() + timeout
|
|
12
|
+
|
|
13
|
+
def __call__(self):
|
|
14
|
+
return self
|
|
15
|
+
|
|
16
|
+
def __getitem__(self, key):
|
|
17
|
+
raise NotImplementedError("Indexing not implemented")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class State:
|
|
21
|
+
def __init__(self, gs, pos=0):
|
|
22
|
+
self.gs = gs
|
|
23
|
+
self.pos = pos
|
|
24
|
+
|
|
25
|
+
def copy(self):
|
|
26
|
+
return self.__class__(self.gs, self.pos)
|
|
27
|
+
|
|
28
|
+
# Assumes gs is string like somewhat
|
|
29
|
+
def __call__(self):
|
|
30
|
+
return self.gs[self.pos:]
|
|
31
|
+
|
|
32
|
+
def extend(self, offset):
|
|
33
|
+
res = self.copy()
|
|
34
|
+
res.pos = self.pos + offset
|
|
35
|
+
return res
|
|
36
|
+
|
|
37
|
+
def atend(self):
|
|
38
|
+
return len(self.gs()) - self.pos == 0
|
|
39
|
+
|
|
40
|
+
# Definitely expects gs to 'be' a string
|
|
41
|
+
def getcontext(self):
|
|
42
|
+
s = self.gs()
|
|
43
|
+
end = s.find("\n", self.pos)
|
|
44
|
+
if end == 0:
|
|
45
|
+
end = s[self.pos + 1:].find("\n") + 1
|
|
46
|
+
tok = s[self.pos:end] if end >= 0 else s[self.pos:]
|
|
47
|
+
return tok
|
|
48
|
+
|
|
49
|
+
def debug(self, good, to, index, kwindex, rep, name, respos):
|
|
50
|
+
return '{}ing[{}/{}:{}.{}{}->{}] {} at {}->{} = "{}"...'.format("match" if good else "fail", self.defstack,
|
|
51
|
+
to, index, kwindex, "["+",".join(map(str, self.cstack))+"]", rep, name, self.pos, respos, self()[:10].replace("\n", "\\n"))
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def lasterror(self):
|
|
55
|
+
return self.gs.lasterror
|
|
56
|
+
|
|
57
|
+
@lasterror.setter
|
|
58
|
+
def lasterror(self, val):
|
|
59
|
+
self.gs.lasterror = val
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def cstack(self):
|
|
63
|
+
return self.gs.cstack
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def defstack(self):
|
|
67
|
+
return self.gs.defstack
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class PrintContext:
|
|
71
|
+
def __init__(self):
|
|
72
|
+
self.indent = 0
|
|
73
|
+
|
|
74
|
+
def do_indent(self, indent=2):
|
|
75
|
+
self.indent += indent
|
|
76
|
+
|
|
77
|
+
def unindent(self, indent=2):
|
|
78
|
+
self.indent = max(0, self.indent-indent)
|
|
79
|
+
|
|
80
|
+
def str(self, s, index=0):
|
|
81
|
+
return s.asstr(context=self)
|
|
82
|
+
|
|
83
|
+
class NoParseError(Exception):
|
|
84
|
+
def __init__(self, msg='', state=None):
|
|
85
|
+
self.msg = msg
|
|
86
|
+
self.state = state
|
|
87
|
+
|
|
88
|
+
def __str__(self):
|
|
89
|
+
return self.msg
|
|
90
|
+
|
|
91
|
+
class Parser:
|
|
92
|
+
debug = True #False
|
|
93
|
+
|
|
94
|
+
def __init__(self, p, **kw):
|
|
95
|
+
self.define(p)
|
|
96
|
+
self.properties = {}
|
|
97
|
+
p = kw.get('parent', None)
|
|
98
|
+
self.index = getattr(p, 'index') + 1 if p is not None else 0
|
|
99
|
+
self.to = kw.get('to', '')
|
|
100
|
+
if 'inref' in kw:
|
|
101
|
+
self.inref = kw['inref']
|
|
102
|
+
|
|
103
|
+
def __str__(self):
|
|
104
|
+
context = PrintContext()
|
|
105
|
+
return context.str(self)
|
|
106
|
+
|
|
107
|
+
def define(self, p):
|
|
108
|
+
f = getattr(p, 'run', p)
|
|
109
|
+
if self.debug:
|
|
110
|
+
def runfn(s, **kw):
|
|
111
|
+
try:
|
|
112
|
+
res = f(s)
|
|
113
|
+
rese = None
|
|
114
|
+
except NoParseError as e:
|
|
115
|
+
res = None
|
|
116
|
+
rese = e
|
|
117
|
+
logger.debug(s.debug(rese is None, self.to, self.index, kw.get('index', ''), repr(self), getattr(self, 'name', 'UNK'),
|
|
118
|
+
(res[1].pos if res is not None else "?")))
|
|
119
|
+
if time.time() > s.gs.time:
|
|
120
|
+
raise TimeoutError()
|
|
121
|
+
if rese is not None:
|
|
122
|
+
raise(rese)
|
|
123
|
+
return res
|
|
124
|
+
else:
|
|
125
|
+
def runfn(s, **kw):
|
|
126
|
+
if s.atend():
|
|
127
|
+
raise NoParseError("<EOF>", s)
|
|
128
|
+
return f(s)
|
|
129
|
+
setattr(self, 'run', runfn)
|
|
130
|
+
|
|
131
|
+
def run(self, s):
|
|
132
|
+
"""State -> (b, State)"""
|
|
133
|
+
raise NotImplementedError('you must define() a parser')
|
|
134
|
+
|
|
135
|
+
def parse(self, s):
|
|
136
|
+
"""State -> b"""
|
|
137
|
+
try:
|
|
138
|
+
(tree, finals) = self.run(s)
|
|
139
|
+
except NoParseError as e:
|
|
140
|
+
if not s.atend():
|
|
141
|
+
tok = e.state.getcontext()
|
|
142
|
+
raise NoParseError('%s: %s' % (e.msg, tok), e.state)
|
|
143
|
+
if not finals.atend():
|
|
144
|
+
if finals.lasterror is not None:
|
|
145
|
+
tok = finals.lasterror.state.getcontext()
|
|
146
|
+
raise NoParseError("{}: {}".format(finals.lasterror.msg, tok), finals.lasterror.state)
|
|
147
|
+
else:
|
|
148
|
+
tok = finals.getcontext()
|
|
149
|
+
raise NoParseError("Incomplete match: {}".format(tok), finals)
|
|
150
|
+
return tree
|
|
151
|
+
|
|
152
|
+
def asstr(self, **kw):
|
|
153
|
+
return ""
|
|
154
|
+
|
|
155
|
+
def __repr__(self):
|
|
156
|
+
return ""
|
|
157
|
+
|
|
158
|
+
def merge(self, other, mode=None):
|
|
159
|
+
return (self, other)
|
|
160
|
+
|
|
161
|
+
def empty(self):
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class Group(Parser):
|
|
166
|
+
def __init__(self, *a, name="", mode="&", parent=None, result=None, capture=None, **kw):
|
|
167
|
+
super().__init__(self, parent=parent, **kw)
|
|
168
|
+
self.children = list(a)
|
|
169
|
+
self.mode = mode
|
|
170
|
+
self.result = result
|
|
171
|
+
self.name = name + mode
|
|
172
|
+
self.capture = capture
|
|
173
|
+
self.propmap = {}
|
|
174
|
+
if parent is not None:
|
|
175
|
+
parent.append(self)
|
|
176
|
+
|
|
177
|
+
def __repr__(self):
|
|
178
|
+
return "Group[{}]{}".format(self.name, f"\u21D2{self.capture}" if self.capture else "")
|
|
179
|
+
|
|
180
|
+
def asstr(self, context=None):
|
|
181
|
+
ext = re.sub(r"[|&]", "", self.mode) + (f"\u21D2{self.capture}" if self.capture else "")
|
|
182
|
+
sep = self.mode if self.mode == "|" else " "
|
|
183
|
+
res = []
|
|
184
|
+
if context is not None and len(self.children) > 1:
|
|
185
|
+
context.do_indent()
|
|
186
|
+
for i, x in enumerate(self.children):
|
|
187
|
+
v = str(x) if context is None else context.str(x, index=i)
|
|
188
|
+
if v != "":
|
|
189
|
+
res.append(v)
|
|
190
|
+
indent = 0 if context is None else context.indent
|
|
191
|
+
if not len(self.children):
|
|
192
|
+
return ""
|
|
193
|
+
elif len(self.children) == 1:
|
|
194
|
+
return sep.join(res) + ext
|
|
195
|
+
else:
|
|
196
|
+
context.unindent()
|
|
197
|
+
return "( " + (sep+"\n"+(" "*indent)).join(res) + " )" + ext
|
|
198
|
+
|
|
199
|
+
def run(self, s):
|
|
200
|
+
done = False
|
|
201
|
+
i = 0
|
|
202
|
+
res = []
|
|
203
|
+
cuts = s
|
|
204
|
+
allfailed = True
|
|
205
|
+
if self.mode == "|+":
|
|
206
|
+
for c in self.children:
|
|
207
|
+
c.mc = False
|
|
208
|
+
if self.capture:
|
|
209
|
+
s.gs.init(self.capture, "")
|
|
210
|
+
s.cstack.append(0)
|
|
211
|
+
while self._loop(i):
|
|
212
|
+
subres = []
|
|
213
|
+
nohit = True
|
|
214
|
+
for j, c in enumerate(self.children):
|
|
215
|
+
if self.mode == "|+":
|
|
216
|
+
matched = c.mc
|
|
217
|
+
news = cuts
|
|
218
|
+
if (n := getattr(c, "inref", None)) is not None:
|
|
219
|
+
s.defstack.append(n)
|
|
220
|
+
s.cstack[-1] += 1
|
|
221
|
+
try:
|
|
222
|
+
(newv, news) = c.run(cuts, index=j)
|
|
223
|
+
except NoParseError as e:
|
|
224
|
+
if n is not None:
|
|
225
|
+
s.gs.defstack.pop()
|
|
226
|
+
if self.mode.startswith('|') and (j < len(self.children) - 1 or not allfailed):
|
|
227
|
+
continue
|
|
228
|
+
elif any(x in self.mode for x in '?*') or ('+' in self.mode and i > 0):
|
|
229
|
+
done = True
|
|
230
|
+
break
|
|
231
|
+
else:
|
|
232
|
+
s.cstack.pop()
|
|
233
|
+
s.lasterror = e
|
|
234
|
+
if self.capture:
|
|
235
|
+
s.gs.release(self.capture)
|
|
236
|
+
raise e
|
|
237
|
+
if n is not None:
|
|
238
|
+
s.defstack.pop()
|
|
239
|
+
if self.mode == "|+":
|
|
240
|
+
if cuts.pos != news.pos and matched and c.mc:
|
|
241
|
+
if self.capture:
|
|
242
|
+
s.gs.release(self.capture)
|
|
243
|
+
s.cstack.pop()
|
|
244
|
+
raise NoParseError(f'Interleave multiply matched {c} in {self}', s)
|
|
245
|
+
elif cuts.pos != news.pos: # real match
|
|
246
|
+
c.mc = True
|
|
247
|
+
else:
|
|
248
|
+
continue
|
|
249
|
+
allfailed = False
|
|
250
|
+
nohit = False
|
|
251
|
+
cuts = news
|
|
252
|
+
if newv is not None:
|
|
253
|
+
subres.append(newv)
|
|
254
|
+
if self.mode.startswith('|'):
|
|
255
|
+
break
|
|
256
|
+
else:
|
|
257
|
+
if nohit:
|
|
258
|
+
break
|
|
259
|
+
s = cuts
|
|
260
|
+
if nohit and done: # be greedy
|
|
261
|
+
break
|
|
262
|
+
if len(subres):
|
|
263
|
+
res.append(subres)
|
|
264
|
+
if self.mode in '?|':
|
|
265
|
+
break
|
|
266
|
+
i += 1
|
|
267
|
+
if self.capture:
|
|
268
|
+
s.gs.release(self.capture)
|
|
269
|
+
s.cstack.pop()
|
|
270
|
+
return (self._returnRes(res, s), s)
|
|
271
|
+
|
|
272
|
+
def append(self, parser):
|
|
273
|
+
if len(self.children):
|
|
274
|
+
lasts = self.children[-1].merge(parser, mode=self.mode)
|
|
275
|
+
self.children = self.children[:-1] + list(lasts)
|
|
276
|
+
else:
|
|
277
|
+
self.children.append(parser)
|
|
278
|
+
|
|
279
|
+
def _loop(self, i):
|
|
280
|
+
if '*' in self.mode or '+' in self.mode:
|
|
281
|
+
return True
|
|
282
|
+
if i > 0:
|
|
283
|
+
return False
|
|
284
|
+
return True
|
|
285
|
+
|
|
286
|
+
def _returnRes(self, res, s):
|
|
287
|
+
if isinstance(res, list) and not len(res):
|
|
288
|
+
return None
|
|
289
|
+
if not any(x in self.mode for x in '*+&'):
|
|
290
|
+
if '?' in self.mode:
|
|
291
|
+
res = res[0] if len(res) and not isinstance(res, str) else None
|
|
292
|
+
return res
|
|
293
|
+
|
|
294
|
+
def empty(self):
|
|
295
|
+
self.children = [x for x in self.children if not x.empty()]
|
|
296
|
+
return len(self.children) == 0
|
|
297
|
+
|
|
298
|
+
def Skip(**kw):
|
|
299
|
+
return Parser(lambda s:(None, s), **kw)
|
|
300
|
+
|