omextra 0.0.0.dev494__py3-none-any.whl → 0.0.0.dev496__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.
- omextra/text/abnf/__init__.py +17 -4
- omextra/text/abnf/_dataclasses.py +438 -0
- omextra/text/abnf/base.py +49 -82
- omextra/text/abnf/core.py +5 -5
- omextra/text/abnf/docs/__init__.py +0 -0
- omextra/text/abnf/docs/rfc5234.txt +893 -0
- omextra/text/abnf/docs/rfc7405.txt +221 -0
- omextra/text/abnf/internal.py +32 -0
- omextra/text/abnf/meta.py +37 -36
- omextra/text/abnf/ops.py +276 -0
- omextra/text/abnf/parsing.py +212 -0
- omextra/text/abnf/utils.py +4 -9
- omextra/text/abnf/visitors.py +8 -8
- {omextra-0.0.0.dev494.dist-info → omextra-0.0.0.dev496.dist-info}/METADATA +2 -2
- {omextra-0.0.0.dev494.dist-info → omextra-0.0.0.dev496.dist-info}/RECORD +19 -13
- omextra/text/abnf/parsers.py +0 -343
- {omextra-0.0.0.dev494.dist-info → omextra-0.0.0.dev496.dist-info}/WHEEL +0 -0
- {omextra-0.0.0.dev494.dist-info → omextra-0.0.0.dev496.dist-info}/entry_points.txt +0 -0
- {omextra-0.0.0.dev494.dist-info → omextra-0.0.0.dev496.dist-info}/licenses/LICENSE +0 -0
- {omextra-0.0.0.dev494.dist-info → omextra-0.0.0.dev496.dist-info}/top_level.txt +0 -0
omextra/text/abnf/ops.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
from omlish import dataclasses as dc
|
|
5
|
+
from omlish import lang
|
|
6
|
+
|
|
7
|
+
from .base import LeafOp
|
|
8
|
+
from .base import Op
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Literal(LeafOp, lang.Abstract):
|
|
15
|
+
def _match_repr(self) -> str:
|
|
16
|
+
return repr(self)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@ta.final
|
|
20
|
+
class StringLiteral(Literal, lang.Final):
|
|
21
|
+
def __init__(self, value: str) -> None:
|
|
22
|
+
super().__init__()
|
|
23
|
+
|
|
24
|
+
self._value = check.non_empty_str(value)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def value(self) -> str:
|
|
28
|
+
return self._value
|
|
29
|
+
|
|
30
|
+
def __repr__(self) -> str:
|
|
31
|
+
return f'{self.__class__.__name__}@{id(self):x}({self._value!r})'
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@ta.final
|
|
35
|
+
class CaseInsensitiveStringLiteral(Literal, lang.Final):
|
|
36
|
+
def __init__(self, value: str) -> None:
|
|
37
|
+
super().__init__()
|
|
38
|
+
|
|
39
|
+
self._value = check.non_empty_str(value).casefold()
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def value(self) -> str:
|
|
43
|
+
return self._value
|
|
44
|
+
|
|
45
|
+
def __repr__(self) -> str:
|
|
46
|
+
return f'{self.__class__.__name__}@{id(self):x}({self._value!r})'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@ta.final
|
|
50
|
+
class RangeLiteral(Literal, lang.Final):
|
|
51
|
+
@dc.dataclass(frozen=True)
|
|
52
|
+
class Range:
|
|
53
|
+
lo: str
|
|
54
|
+
hi: str
|
|
55
|
+
|
|
56
|
+
def __post_init__(self) -> None:
|
|
57
|
+
check.non_empty_str(self.lo)
|
|
58
|
+
check.non_empty_str(self.hi)
|
|
59
|
+
check.state(self.hi >= self.lo)
|
|
60
|
+
|
|
61
|
+
def __init__(self, value: Range) -> None:
|
|
62
|
+
super().__init__()
|
|
63
|
+
|
|
64
|
+
self._value = check.isinstance(value, RangeLiteral.Range)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def value(self) -> Range:
|
|
68
|
+
return self._value
|
|
69
|
+
|
|
70
|
+
def __repr__(self) -> str:
|
|
71
|
+
return f'{self.__class__.__name__}@{id(self):x}({self._value!r})'
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@ta.overload
|
|
75
|
+
def literal(s: str, *, case_sensitive: bool = False) -> StringLiteral:
|
|
76
|
+
...
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@ta.overload
|
|
80
|
+
def literal(lo: str, hi: str) -> RangeLiteral:
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def literal(*args, case_sensitive=None):
|
|
85
|
+
if not args:
|
|
86
|
+
raise TypeError
|
|
87
|
+
elif len(args) == 1:
|
|
88
|
+
s = check.isinstance(check.single(args), str)
|
|
89
|
+
if case_sensitive:
|
|
90
|
+
return StringLiteral(s)
|
|
91
|
+
else:
|
|
92
|
+
return CaseInsensitiveStringLiteral(s)
|
|
93
|
+
elif len(args) == 2:
|
|
94
|
+
check.none(case_sensitive)
|
|
95
|
+
return RangeLiteral(RangeLiteral.Range(*map(check.of_isinstance(str), args)))
|
|
96
|
+
else:
|
|
97
|
+
raise TypeError(args)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
##
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@ta.final
|
|
104
|
+
class Concat(Op, lang.Final):
|
|
105
|
+
def __init__(self, *children: Op) -> None:
|
|
106
|
+
super().__init__()
|
|
107
|
+
|
|
108
|
+
for c in check.not_empty(children):
|
|
109
|
+
check.isinstance(c, Op)
|
|
110
|
+
self._children = children
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def children(self) -> ta.Sequence[Op]:
|
|
114
|
+
return self._children
|
|
115
|
+
|
|
116
|
+
def __repr__(self) -> str:
|
|
117
|
+
return f'{self.__class__.__name__}@{id(self):x}({", ".join(map(repr, self._children))})'
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
concat = Concat
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
##
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@ta.final
|
|
127
|
+
class Repeat(Op, lang.Final):
|
|
128
|
+
@dc.dataclass(frozen=True)
|
|
129
|
+
class Times:
|
|
130
|
+
min: int = 0
|
|
131
|
+
max: int | None = None
|
|
132
|
+
|
|
133
|
+
def __post_init__(self) -> None:
|
|
134
|
+
if self.max is not None:
|
|
135
|
+
check.state(self.max >= self.min)
|
|
136
|
+
|
|
137
|
+
def __repr__(self) -> str:
|
|
138
|
+
if not self.min and self.max is None:
|
|
139
|
+
s = '*'
|
|
140
|
+
elif not self.min and self.max == 1:
|
|
141
|
+
s = '?'
|
|
142
|
+
elif self.min and self.max is None:
|
|
143
|
+
s = f'{self.min}+'
|
|
144
|
+
else:
|
|
145
|
+
s = f'{self.min}-{self.max!r}'
|
|
146
|
+
return f'{self.__class__.__name__}({s})'
|
|
147
|
+
|
|
148
|
+
def __init__(self, times: Times, child: Op) -> None:
|
|
149
|
+
super().__init__()
|
|
150
|
+
|
|
151
|
+
self._times = check.isinstance(times, Repeat.Times)
|
|
152
|
+
self._child = check.isinstance(child, Op)
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def times(self) -> Times:
|
|
156
|
+
return self._times
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def child(self) -> Op:
|
|
160
|
+
return self._child
|
|
161
|
+
|
|
162
|
+
def __repr__(self) -> str:
|
|
163
|
+
return f'{self.__class__.__name__}@{id(self):x}({self._times}, {self._child!r})'
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@ta.overload
|
|
167
|
+
def repeat(child: Op) -> Repeat: # noqa
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@ta.overload
|
|
172
|
+
def repeat(times: Repeat.Times, child: Op) -> Repeat: # noqa
|
|
173
|
+
...
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@ta.overload
|
|
177
|
+
def repeat(min: int, child: Op) -> Repeat: # noqa
|
|
178
|
+
...
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@ta.overload
|
|
182
|
+
def repeat(min: int, max: int | None, child: Op) -> Repeat: # noqa
|
|
183
|
+
...
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def repeat(*args):
|
|
187
|
+
min: int # noqa
|
|
188
|
+
max: int | None # noqa
|
|
189
|
+
|
|
190
|
+
if len(args) < 2:
|
|
191
|
+
[child] = args
|
|
192
|
+
min, max = 0, None # noqa
|
|
193
|
+
|
|
194
|
+
elif len(args) > 2:
|
|
195
|
+
min, max, child = args # noqa
|
|
196
|
+
|
|
197
|
+
else:
|
|
198
|
+
ti, child = args # noqa
|
|
199
|
+
|
|
200
|
+
if isinstance(ti, Repeat.Times):
|
|
201
|
+
min, max = ti.min, ti.max # noqa
|
|
202
|
+
|
|
203
|
+
else:
|
|
204
|
+
min, max = ti, None # noqa
|
|
205
|
+
|
|
206
|
+
return Repeat(
|
|
207
|
+
Repeat.Times(
|
|
208
|
+
check.isinstance(min, int),
|
|
209
|
+
check.isinstance(max, (int, None)),
|
|
210
|
+
),
|
|
211
|
+
check.isinstance(child, Op),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
ZERO_OR_ONE_TIMES = Repeat.Times(0, 1)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def option(child: Op) -> Repeat:
|
|
219
|
+
return Repeat(ZERO_OR_ONE_TIMES, check.isinstance(child, Op))
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
##
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@ta.final
|
|
226
|
+
class Either(Op, lang.Final):
|
|
227
|
+
def __init__(self, *children: Op, first_match: bool = False) -> None:
|
|
228
|
+
super().__init__()
|
|
229
|
+
|
|
230
|
+
for c in check.not_empty(children):
|
|
231
|
+
check.isinstance(c, Op)
|
|
232
|
+
self._children = children
|
|
233
|
+
self._first_match = first_match
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def children(self) -> ta.Sequence[Op]:
|
|
237
|
+
return self._children
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def first_match(self) -> bool:
|
|
241
|
+
return self._first_match
|
|
242
|
+
|
|
243
|
+
def __repr__(self) -> str:
|
|
244
|
+
return (
|
|
245
|
+
f'{self.__class__.__name__}@{id(self):x}('
|
|
246
|
+
f'{", ".join(map(repr, self._children))}'
|
|
247
|
+
f'{", first_match=True" if self._first_match else ""})'
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
either = Either
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
##
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@ta.final
|
|
258
|
+
class RuleRef(Op, lang.Final):
|
|
259
|
+
def __init__(self, name: str) -> None:
|
|
260
|
+
super().__init__()
|
|
261
|
+
|
|
262
|
+
self._name = check.non_empty_str(name)
|
|
263
|
+
self._name_f = name.casefold()
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def name(self) -> str:
|
|
267
|
+
return self._name
|
|
268
|
+
|
|
269
|
+
def __repr__(self) -> str:
|
|
270
|
+
return f'{self.__class__.__name__}@{id(self):x}({self._name!r})'
|
|
271
|
+
|
|
272
|
+
def _match_repr(self) -> str:
|
|
273
|
+
return repr(self)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
rule = RuleRef
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
|
|
5
|
+
from .base import Grammar
|
|
6
|
+
from .base import Match
|
|
7
|
+
from .base import Op
|
|
8
|
+
from .internal import Regex
|
|
9
|
+
from .ops import CaseInsensitiveStringLiteral
|
|
10
|
+
from .ops import Concat
|
|
11
|
+
from .ops import Either
|
|
12
|
+
from .ops import RangeLiteral
|
|
13
|
+
from .ops import Repeat
|
|
14
|
+
from .ops import RuleRef
|
|
15
|
+
from .ops import StringLiteral
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _Parser:
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
grammar: Grammar,
|
|
25
|
+
source: str,
|
|
26
|
+
) -> None:
|
|
27
|
+
super().__init__()
|
|
28
|
+
|
|
29
|
+
self._grammar = grammar
|
|
30
|
+
self._source = source
|
|
31
|
+
|
|
32
|
+
self._dispatch: dict[type[Op], ta.Any] = {
|
|
33
|
+
StringLiteral: self._iter_parse_string_literal,
|
|
34
|
+
CaseInsensitiveStringLiteral: self._iter_parse_case_insensitive_string_literal,
|
|
35
|
+
RangeLiteral: self._iter_parse_range_literal,
|
|
36
|
+
Concat: self._iter_parse_concat,
|
|
37
|
+
Repeat: self._iter_parse_repeat,
|
|
38
|
+
Either: self._iter_parse_either,
|
|
39
|
+
RuleRef: self._iter_parse_rule_ref,
|
|
40
|
+
Regex: self._iter_parse_regex,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
def _iter_parse_string_literal(self, op: StringLiteral, start: int) -> ta.Iterator[Match]:
|
|
44
|
+
if start < len(self._source): # noqa
|
|
45
|
+
source = self._source[start : start + len(op._value)] # noqa
|
|
46
|
+
if source == op._value: # noqa
|
|
47
|
+
yield Match(op, start, start + len(source), ())
|
|
48
|
+
|
|
49
|
+
def _iter_parse_case_insensitive_string_literal(self, op: CaseInsensitiveStringLiteral, start: int) -> ta.Iterator[Match]: # noqa
|
|
50
|
+
if start < len(self._source): # noqa
|
|
51
|
+
source = self._source[start : start + len(op._value)].casefold() # noqa
|
|
52
|
+
if source == op._value: # noqa
|
|
53
|
+
yield Match(op, start, start + len(source), ())
|
|
54
|
+
|
|
55
|
+
def _iter_parse_range_literal(self, op: RangeLiteral, start: int) -> ta.Iterator[Match]:
|
|
56
|
+
try:
|
|
57
|
+
source = self._source[start] # noqa
|
|
58
|
+
except IndexError:
|
|
59
|
+
return
|
|
60
|
+
# ranges are always case-sensitive
|
|
61
|
+
if (value := op._value).lo <= source <= value.hi: # noqa
|
|
62
|
+
yield Match(op, start, start + 1, ())
|
|
63
|
+
|
|
64
|
+
def _iter_parse_concat(self, op: Concat, start: int) -> ta.Iterator[Match]:
|
|
65
|
+
i = 0
|
|
66
|
+
match_tups: list[tuple[Match, ...]] = [()]
|
|
67
|
+
for cp in op._children: # noqa
|
|
68
|
+
next_match_tups: list[tuple[Match, ...]] = []
|
|
69
|
+
for mt in match_tups:
|
|
70
|
+
for cm in self.iter_parse(cp, mt[-1].end if mt else start):
|
|
71
|
+
next_match_tups.append((*mt, cm))
|
|
72
|
+
i += 1
|
|
73
|
+
if not next_match_tups:
|
|
74
|
+
return
|
|
75
|
+
match_tups = next_match_tups
|
|
76
|
+
if not i:
|
|
77
|
+
return
|
|
78
|
+
for mt in sorted(match_tups, key=len, reverse=True):
|
|
79
|
+
yield Match(op, start, mt[-1].end if mt else start, mt)
|
|
80
|
+
|
|
81
|
+
def _iter_parse_repeat(self, op: Repeat, start: int) -> ta.Iterator[Match]:
|
|
82
|
+
match_tup_set: set[tuple[Match, ...]] = set()
|
|
83
|
+
last_match_tup_set: set[tuple[Match, ...]] = {()}
|
|
84
|
+
i = 0
|
|
85
|
+
while True:
|
|
86
|
+
if op._times.max is not None and i == op._times.max: # noqa
|
|
87
|
+
break
|
|
88
|
+
next_match_tup_set: set[tuple[Match, ...]] = set()
|
|
89
|
+
for mt in last_match_tup_set:
|
|
90
|
+
for cm in self.iter_parse(op._child, mt[-1].end if mt else start): # noqa
|
|
91
|
+
next_match_tup_set.add((*mt, cm))
|
|
92
|
+
if not next_match_tup_set or next_match_tup_set < match_tup_set:
|
|
93
|
+
break
|
|
94
|
+
i += 1
|
|
95
|
+
match_tup_set |= next_match_tup_set
|
|
96
|
+
last_match_tup_set = next_match_tup_set
|
|
97
|
+
if i < op._times.min: # noqa
|
|
98
|
+
return
|
|
99
|
+
for mt in sorted(match_tup_set or [()], key=len, reverse=True):
|
|
100
|
+
yield Match(op, start, mt[-1].end if mt else start, mt) # noqa
|
|
101
|
+
|
|
102
|
+
def _iter_parse_either(self, op: Either, start: int) -> ta.Iterator[Match]:
|
|
103
|
+
for cp in op._children: # noqa
|
|
104
|
+
found = False
|
|
105
|
+
for cm in self.iter_parse(cp, start):
|
|
106
|
+
found = True
|
|
107
|
+
yield Match(op, start, cm.end, (cm,))
|
|
108
|
+
if found and op._first_match: # noqa
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
def _iter_parse_rule_ref(self, op: RuleRef, start: int) -> ta.Iterator[Match]:
|
|
112
|
+
cp = self._grammar._rules_by_name_f[op._name_f].op # noqa
|
|
113
|
+
for cm in self.iter_parse(cp, start):
|
|
114
|
+
yield Match(op, cm.start, cm.end, (cm,))
|
|
115
|
+
|
|
116
|
+
def _iter_parse_regex(self, op: Regex, start: int) -> ta.Iterator[Match]:
|
|
117
|
+
raise NotImplementedError
|
|
118
|
+
|
|
119
|
+
def iter_parse(self, op: Op, start: int) -> ta.Iterator[Match]:
|
|
120
|
+
return self._dispatch[op.__class__](op, start)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
##
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class _DebugParser(_Parser):
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
grammar: Grammar,
|
|
130
|
+
source: str,
|
|
131
|
+
level: int = 1,
|
|
132
|
+
*,
|
|
133
|
+
write: ta.Callable[[str], None] | None = None,
|
|
134
|
+
) -> None:
|
|
135
|
+
super().__init__(grammar, source)
|
|
136
|
+
|
|
137
|
+
self._level = level
|
|
138
|
+
if write is None:
|
|
139
|
+
write = print
|
|
140
|
+
self._write = write
|
|
141
|
+
|
|
142
|
+
self._op_strs: dict[Op, str] = {}
|
|
143
|
+
|
|
144
|
+
def _op_str(self, op: Op) -> str:
|
|
145
|
+
try:
|
|
146
|
+
return self._op_strs[op]
|
|
147
|
+
except KeyError:
|
|
148
|
+
pass
|
|
149
|
+
ps = self._op_strs[op] = str(op)
|
|
150
|
+
return ps
|
|
151
|
+
|
|
152
|
+
_depth: int = 0
|
|
153
|
+
|
|
154
|
+
def iter_parse(self, op: Op, start: int) -> ta.Iterator[Match]:
|
|
155
|
+
if self._level < 2 and not isinstance(op, RuleRef):
|
|
156
|
+
yield from super().iter_parse(op, start)
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
ws = f'{" " * self._depth} '
|
|
160
|
+
|
|
161
|
+
if self._level < 2:
|
|
162
|
+
ps = check.isinstance(op, RuleRef).name
|
|
163
|
+
else:
|
|
164
|
+
ps = self._op_str(op)
|
|
165
|
+
body = f'{start}:{self._source[start]!r} {ps}'
|
|
166
|
+
|
|
167
|
+
if self._level > 2:
|
|
168
|
+
self._write(f'{ws}+ {body}')
|
|
169
|
+
else:
|
|
170
|
+
self._write(f'{ws}{body}')
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
self._depth += 1
|
|
174
|
+
|
|
175
|
+
for m in super().iter_parse(op, start): # noqa
|
|
176
|
+
if self._level > 3:
|
|
177
|
+
self._write(f'{ws}! {m.start}-{m.end}:{self._source[m.start:m.end]!r}')
|
|
178
|
+
|
|
179
|
+
yield m
|
|
180
|
+
|
|
181
|
+
finally:
|
|
182
|
+
self._depth -= 1
|
|
183
|
+
|
|
184
|
+
if self._level > 3:
|
|
185
|
+
self._write(f'{ws}- {body}')
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
##
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _iter_parse(
|
|
192
|
+
grammar: Grammar,
|
|
193
|
+
source: str,
|
|
194
|
+
op: Op,
|
|
195
|
+
start: int,
|
|
196
|
+
*,
|
|
197
|
+
debug: int = 0,
|
|
198
|
+
) -> ta.Iterator[Match]:
|
|
199
|
+
parser: _Parser
|
|
200
|
+
if debug:
|
|
201
|
+
parser = _DebugParser(
|
|
202
|
+
grammar,
|
|
203
|
+
source,
|
|
204
|
+
level=debug,
|
|
205
|
+
)
|
|
206
|
+
else:
|
|
207
|
+
parser = _Parser(
|
|
208
|
+
grammar,
|
|
209
|
+
source,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
return parser.iter_parse(op, start)
|
omextra/text/abnf/utils.py
CHANGED
|
@@ -6,8 +6,7 @@ from omlish import check
|
|
|
6
6
|
|
|
7
7
|
from .base import Grammar
|
|
8
8
|
from .base import Match
|
|
9
|
-
from .
|
|
10
|
-
from .parsers import RuleRef
|
|
9
|
+
from .ops import RuleRef
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
##
|
|
@@ -18,7 +17,7 @@ def strip_insignificant_match_rules(m: Match, g: Grammar) -> Match:
|
|
|
18
17
|
return c.flat_map_children(
|
|
19
18
|
lambda x: (
|
|
20
19
|
(rec(x),) if not (
|
|
21
|
-
isinstance((xp := x.
|
|
20
|
+
isinstance((xp := x.op), RuleRef) and
|
|
22
21
|
check.not_none(g.rule(xp.name)).insignificant
|
|
23
22
|
) else ()
|
|
24
23
|
),
|
|
@@ -28,7 +27,7 @@ def strip_insignificant_match_rules(m: Match, g: Grammar) -> Match:
|
|
|
28
27
|
|
|
29
28
|
def only_match_rules(m: Match) -> Match:
|
|
30
29
|
def rec(c: Match) -> ta.Iterable[Match]:
|
|
31
|
-
if isinstance(c.
|
|
30
|
+
if isinstance(c.op, RuleRef):
|
|
32
31
|
return (c.flat_map_children(rec),)
|
|
33
32
|
else:
|
|
34
33
|
return itertools.chain.from_iterable(map(rec, c.children))
|
|
@@ -44,7 +43,6 @@ def parse_rules(
|
|
|
44
43
|
root: str | None = None,
|
|
45
44
|
*,
|
|
46
45
|
start: int = 0,
|
|
47
|
-
complete: bool = False,
|
|
48
46
|
**kwargs: ta.Any,
|
|
49
47
|
) -> Match | None:
|
|
50
48
|
if (match := grammar.parse(
|
|
@@ -55,9 +53,6 @@ def parse_rules(
|
|
|
55
53
|
)) is None:
|
|
56
54
|
return None
|
|
57
55
|
|
|
58
|
-
if complete and (match.start, match.end) != (start, len(source)):
|
|
59
|
-
raise AbnfIncompleteParseError
|
|
60
|
-
|
|
61
56
|
match = only_match_rules(match)
|
|
62
57
|
match = strip_insignificant_match_rules(match, grammar)
|
|
63
58
|
|
|
@@ -67,7 +62,7 @@ def parse_rules(
|
|
|
67
62
|
##
|
|
68
63
|
|
|
69
64
|
|
|
70
|
-
def
|
|
65
|
+
def fix_ws(s: str) -> str:
|
|
71
66
|
return (
|
|
72
67
|
textwrap.dedent(s)
|
|
73
68
|
.rstrip()
|
omextra/text/abnf/visitors.py
CHANGED
|
@@ -6,8 +6,8 @@ from omlish import dispatch
|
|
|
6
6
|
from omlish import lang
|
|
7
7
|
|
|
8
8
|
from .base import Match
|
|
9
|
-
from .base import
|
|
10
|
-
from .
|
|
9
|
+
from .base import Op
|
|
10
|
+
from .ops import RuleRef
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
T = ta.TypeVar('T')
|
|
@@ -16,21 +16,21 @@ T = ta.TypeVar('T')
|
|
|
16
16
|
##
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class
|
|
19
|
+
class OpMatchVisitor(lang.Abstract, ta.Generic[T]):
|
|
20
20
|
@dispatch.method()
|
|
21
|
-
def
|
|
22
|
-
raise TypeError(
|
|
21
|
+
def visit_op(self, o: Op, m: Match) -> T:
|
|
22
|
+
raise TypeError(o)
|
|
23
23
|
|
|
24
24
|
#
|
|
25
25
|
|
|
26
26
|
def visit_match(self, m: Match) -> T:
|
|
27
|
-
return self.
|
|
27
|
+
return self.visit_op(m.op, m)
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
##
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
class
|
|
33
|
+
class RuleMatchVisitor(lang.Abstract, ta.Generic[T]):
|
|
34
34
|
_registry = col.AttrRegistry[ta.Callable, str]()
|
|
35
35
|
|
|
36
36
|
@classmethod
|
|
@@ -52,4 +52,4 @@ class RuleVisitor(lang.Abstract, ta.Generic[T]):
|
|
|
52
52
|
#
|
|
53
53
|
|
|
54
54
|
def visit_match(self, m: Match) -> T:
|
|
55
|
-
return self.visit_rule(check.isinstance(m.
|
|
55
|
+
return self.visit_rule(check.isinstance(m.op, RuleRef).name, m)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omextra
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev496
|
|
4
4
|
Summary: omextra
|
|
5
5
|
Author: wrmsr
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
14
14
|
Requires-Python: >=3.13
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: omlish==0.0.0.
|
|
17
|
+
Requires-Dist: omlish==0.0.0.dev496
|
|
18
18
|
Dynamic: license-file
|
|
19
19
|
|
|
20
20
|
# Overview
|
|
@@ -65,14 +65,20 @@ omextra/sql/parsing/_antlr/MinisqlVisitor.py,sha256=UPs2qA2VlNRP_LMwF0DpMxMir9tI
|
|
|
65
65
|
omextra/sql/parsing/_antlr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
66
|
omextra/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
67
|
omextra/text/abnf/LICENSE,sha256=UJzrhSePw9TVm0VfkVmx9NRHkPS1n6jx2UgajTb14ts,1054
|
|
68
|
-
omextra/text/abnf/__init__.py,sha256=
|
|
69
|
-
omextra/text/abnf/
|
|
70
|
-
omextra/text/abnf/
|
|
68
|
+
omextra/text/abnf/__init__.py,sha256=nDwr8A5pU8HajHvmAyT8Kr_d_nAUx3tO-ioZXifHob4,1298
|
|
69
|
+
omextra/text/abnf/_dataclasses.py,sha256=YBZeWEvEkYZ8LqJo0MrV5kpKaUWoyLAP7hCT4ziOEh4,18787
|
|
70
|
+
omextra/text/abnf/base.py,sha256=fhZwnsghININSgE7O5NWHBK196LKf_g_xyXwugvR_Vo,7134
|
|
71
|
+
omextra/text/abnf/core.py,sha256=H5U5d-QOI66YSacrLXEcvMQipkabvP8EWo6d9QaxWoU,2283
|
|
71
72
|
omextra/text/abnf/errors.py,sha256=uj1oQvRVRlWkMG8mpmcRUg25kICeIJx5EWAVij3pmXc,142
|
|
72
|
-
omextra/text/abnf/
|
|
73
|
-
omextra/text/abnf/
|
|
74
|
-
omextra/text/abnf/
|
|
75
|
-
omextra/text/abnf/
|
|
73
|
+
omextra/text/abnf/internal.py,sha256=PzHIzEEv3H5oLI_hwSmkuOwwzh_BZpvXBjGKYqmPvzs,493
|
|
74
|
+
omextra/text/abnf/meta.py,sha256=IvWdyo8739fCmBwVLzz7VrkScc5DM9uVOKUpEb6-lfM,14504
|
|
75
|
+
omextra/text/abnf/ops.py,sha256=LHF1_NfczSypGb3XhNJIT0zWkIx57lzoZSgSELxUE2E,6063
|
|
76
|
+
omextra/text/abnf/parsing.py,sha256=lDMBPCC-3Rs9XwnW_18E7NMjTSz_gz896u21mv15rvs,6676
|
|
77
|
+
omextra/text/abnf/utils.py,sha256=cpPTGeiVFf1AN4K3KgodsOcarEhFxdEcgxtpRx9xB84,1460
|
|
78
|
+
omextra/text/abnf/visitors.py,sha256=S7ZJ2TmQR_RHJ70xWWt3w6UGRD20uE7Nd5UqFYU593k,1266
|
|
79
|
+
omextra/text/abnf/docs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
|
+
omextra/text/abnf/docs/rfc5234.txt,sha256=I478qv82TBjP29ZpQT-D6_pb3rHOU6wiwN2ZMGiXLLU,26352
|
|
81
|
+
omextra/text/abnf/docs/rfc7405.txt,sha256=Bwt1VISu7eBvlPox9a0iuVD_uCF39teXrEMUQAuuvqU,6661
|
|
76
82
|
omextra/text/antlr/__init__.py,sha256=88bMl_28cfSKslgOkMGYXqALgsHz3KC4LFvAVtzj7k8,89
|
|
77
83
|
omextra/text/antlr/delimit.py,sha256=s0Jlu6oOamtuyWv_V98P0YC7HmgFJBF7GHVsDfMYTdI,3476
|
|
78
84
|
omextra/text/antlr/dot.py,sha256=37ZBYpum-n6Xg9UGj1Wc0wyuR3gL_uX63KwrSzyWNmk,967
|
|
@@ -146,9 +152,9 @@ omextra/text/antlr/cli/__main__.py,sha256=ckYkj0drxabBVwWYewH2SS36TTeAxllZtS4xEl
|
|
|
146
152
|
omextra/text/antlr/cli/cli.py,sha256=LW8pJNmySBOV3g8QxquPjUgxFv7YblzEyi555hHH3_M,1234
|
|
147
153
|
omextra/text/antlr/cli/consts.py,sha256=HUYJP9j4RfeuuQv6HFd2oFMS0piWJ9Sq1tbeAs4OlBc,290
|
|
148
154
|
omextra/text/antlr/cli/gen.py,sha256=HYleVptrpynwcl6GspX6O9_oMRSxwdYAQGuUFfDYse8,5236
|
|
149
|
-
omextra-0.0.0.
|
|
150
|
-
omextra-0.0.0.
|
|
151
|
-
omextra-0.0.0.
|
|
152
|
-
omextra-0.0.0.
|
|
153
|
-
omextra-0.0.0.
|
|
154
|
-
omextra-0.0.0.
|
|
155
|
+
omextra-0.0.0.dev496.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
|
156
|
+
omextra-0.0.0.dev496.dist-info/METADATA,sha256=6sf1j4HqLxz1VaR6VxOC4dVNrH97Z0YP0YZZNVgudXc,1409
|
|
157
|
+
omextra-0.0.0.dev496.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
158
|
+
omextra-0.0.0.dev496.dist-info/entry_points.txt,sha256=-MFAMks5HgZ60Ore0Wl5lKVKk8z4kf1Ls3WY9E_OlCU,37
|
|
159
|
+
omextra-0.0.0.dev496.dist-info/top_level.txt,sha256=o1nCNRejLMcayDngLuWMWwaeOucz33BXbpuoVvvzjPc,8
|
|
160
|
+
omextra-0.0.0.dev496.dist-info/RECORD,,
|