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/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
+