omextra 0.0.0.dev496__py3-none-any.whl → 0.0.0.dev498__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.
@@ -5,20 +5,45 @@ Originally based on library by Charles Yeomans (see LICENSE file):
5
5
 
6
6
  https://github.com/declaresub/abnf/tree/561ced67c0a8afc869ad0de5b39dbe4f6e71b0d8/src/abnf
7
7
 
8
- It has however been nearly entirely rewritten.
8
+ It has however been entirely rewritten.
9
9
 
10
10
  ====
11
11
 
12
12
  TODO:
13
- - cache lol
14
- - get greedier
15
- - match-powered optimizer
16
- - greedily compile regexes
13
+ - opto
17
14
  - error reporting
18
- - codegen
15
+ - codegen?
16
+ - | as either(first_match=True)
17
+ - optional auto SPACE channel for ALL-UPCASE-RULE-NAMES
19
18
  - fix_ws problem
20
19
  - auto? no, need to keep lines / offsets accurate for errors
21
20
  - relax CRLF rule by default?
21
+ - grammar transform? helper kwarg?
22
+ - kwarg to mark uppercase rules insignificant
23
+ - ebnf mode?
24
+ - peg / lalr engines?
25
+ - must always keep true abnf mode
26
+ - optionally separate lexing step
27
+
28
+ ====
29
+
30
+ | Feature | EBNF | ABNF |
31
+ | ------------------------- | -------------- | ---------------- |
32
+ | Rule terminator | `;` | none |
33
+ | Alternation | `|` | `/` |
34
+ | Optional | `[a]` or `a?` | `[a]` |
35
+ | Zero or more | `a*` | `*a` |
36
+ | One or more | `a+` | `1*a` |
37
+ | Bounded repetition | `1..5 a` | `1*5a` |
38
+ | Character ranges | sometimes: | `%xNN-NN` |
39
+ | | `"0" .. "9"` | |
40
+ | Literal chars | `'a'` or `"a"` | `"a"` only |
41
+ | Case-insensitive literals | no | `%i"..."` |
42
+ | Comments | `(* *)` or | `;` |
43
+ | | `/* */` or | |
44
+ | | `-- ` | |
45
+ | Rule names | case-sensitive | case-insensitive |
46
+
22
47
  """
23
48
  from omlish import dataclasses as _dc # noqa
24
49
 
@@ -33,16 +58,7 @@ _dc.init_package(
33
58
 
34
59
 
35
60
  from .base import ( # noqa
36
- Match,
37
- longest_match,
38
-
39
61
  Op,
40
-
41
- Rule,
42
- Grammar,
43
-
44
- iter_parse,
45
- parse,
46
62
  )
47
63
 
48
64
  from .core import ( # noqa
@@ -54,6 +70,21 @@ from .errors import ( # noqa
54
70
  AbnfGrammarParseError,
55
71
  )
56
72
 
73
+ from .grammars import ( # noqa
74
+ Channel,
75
+ Rule,
76
+ RulesCollection,
77
+
78
+ Grammar,
79
+ )
80
+
81
+ from .matches import ( # noqa
82
+ Match,
83
+
84
+ longest_match,
85
+ filter_matches,
86
+ )
87
+
57
88
  from .meta import ( # noqa
58
89
  META_GRAMMAR_RULES,
59
90
  META_GRAMMAR,
@@ -82,11 +113,13 @@ from .ops import ( # noqa
82
113
  rule,
83
114
  )
84
115
 
116
+ from .parsing import ( # noqa
117
+ iter_parse,
118
+ parse,
119
+ )
120
+
85
121
  from .utils import ( # noqa
86
- strip_insignificant_match_rules,
87
122
  only_match_rules,
88
123
 
89
- parse_rules,
90
-
91
124
  fix_ws,
92
125
  )
@@ -39,6 +39,8 @@ def _register(**kwargs):
39
39
  cls_names=(
40
40
  ('omextra.text.abnf', 'MetaGrammarRuleMatchVisitor.QuotedString'),
41
41
  ('omextra.text.abnf', 'MetaGrammarRuleMatchVisitor.RuleName'),
42
+ ('omextra.text.abnf', '_CaseInsensitiveStringLiteralRegexItem'),
43
+ ('omextra.text.abnf', '_StringLiteralRegexItem'),
42
44
  ),
43
45
  )
44
46
  def _process_dataclass__30a5dd74853303d917aae5f67d4e7189615d1440():
@@ -436,3 +438,247 @@ def _process_dataclass__3dce49e08774c707abbc7a6513e35ccdc43001c9():
436
438
  setattr(__dataclass__cls, '__init__', __init__)
437
439
 
438
440
  return _process_dataclass
441
+
442
+
443
+ @_register(
444
+ plan_repr=(
445
+ "Plans(tup=(CopyPlan(fields=()), EqPlan(fields=()), FrozenPlan(fields=(), allow_dynamic_dunder_attrs=False), Ha"
446
+ "shPlan(action='add', fields=(), cache=False), InitPlan(fields=(), self_param='self', std_params=(), kw_only_pa"
447
+ "rams=(), frozen=True, slots=False, post_init_params=None, init_fns=(), validate_fns=()), ReprPlan(fields=(), i"
448
+ "d=False, terse=False, default_fn=None)))"
449
+ ),
450
+ plan_repr_sha1='e1f7edfe11f2b721d6a656c46e698fedc95461bb',
451
+ op_ref_idents=(),
452
+ cls_names=(
453
+ ('omextra.text.abnf', '_RegexItem'),
454
+ ),
455
+ )
456
+ def _process_dataclass__e1f7edfe11f2b721d6a656c46e698fedc95461bb():
457
+ def _process_dataclass(
458
+ *,
459
+ __dataclass__cls,
460
+ __dataclass__FieldFnValidationError, # noqa
461
+ __dataclass__FieldTypeValidationError, # noqa
462
+ __dataclass__FnValidationError, # noqa
463
+ __dataclass__FrozenInstanceError=dataclasses.FrozenInstanceError, # noqa
464
+ __dataclass__FunctionType=types.FunctionType, # noqa
465
+ __dataclass__HAS_DEFAULT_FACTORY=dataclasses._HAS_DEFAULT_FACTORY, # noqa
466
+ __dataclass__MISSING=dataclasses.MISSING, # noqa
467
+ __dataclass__None=None, # noqa
468
+ __dataclass__TypeError=TypeError, # noqa
469
+ __dataclass___recursive_repr=reprlib.recursive_repr, # noqa
470
+ __dataclass__isinstance=isinstance, # noqa
471
+ __dataclass__object_setattr=object.__setattr__, # noqa
472
+ __dataclass__property=property, # noqa
473
+ ):
474
+ def __copy__(self):
475
+ if self.__class__ is not __dataclass__cls:
476
+ raise TypeError(self)
477
+ return __dataclass__cls() # noqa
478
+
479
+ __copy__.__qualname__ = f"{__dataclass__cls.__qualname__}.__copy__"
480
+ if '__copy__' in __dataclass__cls.__dict__:
481
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __copy__ in class {__dataclass__cls.__name__}")
482
+ setattr(__dataclass__cls, '__copy__', __copy__)
483
+
484
+ def __eq__(self, other):
485
+ if self is other:
486
+ return True
487
+ if self.__class__ is not other.__class__:
488
+ return NotImplemented
489
+ return True
490
+
491
+ __eq__.__qualname__ = f"{__dataclass__cls.__qualname__}.__eq__"
492
+ if '__eq__' in __dataclass__cls.__dict__:
493
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __eq__ in class {__dataclass__cls.__name__}")
494
+ setattr(__dataclass__cls, '__eq__', __eq__)
495
+
496
+ def __setattr__(self, name, value):
497
+ if (
498
+ type(self) is __dataclass__cls
499
+ ):
500
+ raise __dataclass__FrozenInstanceError(f"cannot assign to field {name!r}")
501
+ super(__dataclass__cls, self).__setattr__(name, value)
502
+
503
+ __setattr__.__qualname__ = f"{__dataclass__cls.__qualname__}.__setattr__"
504
+ if '__setattr__' in __dataclass__cls.__dict__:
505
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __setattr__ in class {__dataclass__cls.__name__}")
506
+ setattr(__dataclass__cls, '__setattr__', __setattr__)
507
+
508
+ def __delattr__(self, name):
509
+ if (
510
+ type(self) is __dataclass__cls
511
+ ):
512
+ raise __dataclass__FrozenInstanceError(f"cannot delete field {name!r}")
513
+ super(__dataclass__cls, self).__delattr__(name)
514
+
515
+ __delattr__.__qualname__ = f"{__dataclass__cls.__qualname__}.__delattr__"
516
+ if '__delattr__' in __dataclass__cls.__dict__:
517
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __delattr__ in class {__dataclass__cls.__name__}")
518
+ setattr(__dataclass__cls, '__delattr__', __delattr__)
519
+
520
+ def __hash__(self):
521
+ return hash(())
522
+
523
+ __hash__.__qualname__ = f"{__dataclass__cls.__qualname__}.__hash__"
524
+ setattr(__dataclass__cls, '__hash__', __hash__)
525
+
526
+ def __init__(
527
+ self,
528
+ ) -> __dataclass__None:
529
+ pass
530
+
531
+ __init__.__qualname__ = f"{__dataclass__cls.__qualname__}.__init__"
532
+ if '__init__' in __dataclass__cls.__dict__:
533
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __init__ in class {__dataclass__cls.__name__}")
534
+ setattr(__dataclass__cls, '__init__', __init__)
535
+
536
+ @__dataclass___recursive_repr()
537
+ def __repr__(self):
538
+ parts = []
539
+ return (
540
+ f"{self.__class__.__qualname__}("
541
+ f"{', '.join(parts)}"
542
+ f")"
543
+ )
544
+
545
+ __repr__.__qualname__ = f"{__dataclass__cls.__qualname__}.__repr__"
546
+ if '__repr__' in __dataclass__cls.__dict__:
547
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __repr__ in class {__dataclass__cls.__name__}")
548
+ setattr(__dataclass__cls, '__repr__', __repr__)
549
+
550
+ return _process_dataclass
551
+
552
+
553
+ @_register(
554
+ plan_repr=(
555
+ "Plans(tup=(CopyPlan(fields=('ps',)), EqPlan(fields=('ps',)), FrozenPlan(fields=('ps',), allow_dynamic_dunder_a"
556
+ "ttrs=False), HashPlan(action='add', fields=('ps',), cache=False), InitPlan(fields=(InitPlan.Field(name='ps', a"
557
+ "nnotation=OpRef(name='init.fields.0.annotation'), default=None, default_factory=None, init=True, override=Fals"
558
+ "e, field_type=FieldType.INSTANCE, coerce=None, validate=None, check_type=None),), self_param='self', std_param"
559
+ "s=('ps',), kw_only_params=(), frozen=True, slots=False, post_init_params=None, init_fns=(), validate_fns=()), "
560
+ "ReprPlan(fields=(ReprPlan.Field(name='ps', kw_only=False, fn=None),), id=False, terse=False, default_fn=None))"
561
+ ")"
562
+ ),
563
+ plan_repr_sha1='f7dc3147180d5d4dc248e9c0d94aa0f1e503c4c7',
564
+ op_ref_idents=(
565
+ '__dataclass__init__fields__0__annotation',
566
+ ),
567
+ cls_names=(
568
+ ('omextra.text.abnf', '_RegexRegexItem'),
569
+ ),
570
+ )
571
+ def _process_dataclass__f7dc3147180d5d4dc248e9c0d94aa0f1e503c4c7():
572
+ def _process_dataclass(
573
+ *,
574
+ __dataclass__cls,
575
+ __dataclass__init__fields__0__annotation,
576
+ __dataclass__FieldFnValidationError, # noqa
577
+ __dataclass__FieldTypeValidationError, # noqa
578
+ __dataclass__FnValidationError, # noqa
579
+ __dataclass__FrozenInstanceError=dataclasses.FrozenInstanceError, # noqa
580
+ __dataclass__FunctionType=types.FunctionType, # noqa
581
+ __dataclass__HAS_DEFAULT_FACTORY=dataclasses._HAS_DEFAULT_FACTORY, # noqa
582
+ __dataclass__MISSING=dataclasses.MISSING, # noqa
583
+ __dataclass__None=None, # noqa
584
+ __dataclass__TypeError=TypeError, # noqa
585
+ __dataclass___recursive_repr=reprlib.recursive_repr, # noqa
586
+ __dataclass__isinstance=isinstance, # noqa
587
+ __dataclass__object_setattr=object.__setattr__, # noqa
588
+ __dataclass__property=property, # noqa
589
+ ):
590
+ def __copy__(self):
591
+ if self.__class__ is not __dataclass__cls:
592
+ raise TypeError(self)
593
+ return __dataclass__cls( # noqa
594
+ ps=self.ps,
595
+ )
596
+
597
+ __copy__.__qualname__ = f"{__dataclass__cls.__qualname__}.__copy__"
598
+ if '__copy__' in __dataclass__cls.__dict__:
599
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __copy__ in class {__dataclass__cls.__name__}")
600
+ setattr(__dataclass__cls, '__copy__', __copy__)
601
+
602
+ def __eq__(self, other):
603
+ if self is other:
604
+ return True
605
+ if self.__class__ is not other.__class__:
606
+ return NotImplemented
607
+ return (
608
+ self.ps == other.ps
609
+ )
610
+
611
+ __eq__.__qualname__ = f"{__dataclass__cls.__qualname__}.__eq__"
612
+ if '__eq__' in __dataclass__cls.__dict__:
613
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __eq__ in class {__dataclass__cls.__name__}")
614
+ setattr(__dataclass__cls, '__eq__', __eq__)
615
+
616
+ __dataclass___setattr_frozen_fields = {
617
+ 'ps',
618
+ }
619
+
620
+ def __setattr__(self, name, value):
621
+ if (
622
+ type(self) is __dataclass__cls
623
+ or name in __dataclass___setattr_frozen_fields
624
+ ):
625
+ raise __dataclass__FrozenInstanceError(f"cannot assign to field {name!r}")
626
+ super(__dataclass__cls, self).__setattr__(name, value)
627
+
628
+ __setattr__.__qualname__ = f"{__dataclass__cls.__qualname__}.__setattr__"
629
+ if '__setattr__' in __dataclass__cls.__dict__:
630
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __setattr__ in class {__dataclass__cls.__name__}")
631
+ setattr(__dataclass__cls, '__setattr__', __setattr__)
632
+
633
+ __dataclass___delattr_frozen_fields = {
634
+ 'ps',
635
+ }
636
+
637
+ def __delattr__(self, name):
638
+ if (
639
+ type(self) is __dataclass__cls
640
+ or name in __dataclass___delattr_frozen_fields
641
+ ):
642
+ raise __dataclass__FrozenInstanceError(f"cannot delete field {name!r}")
643
+ super(__dataclass__cls, self).__delattr__(name)
644
+
645
+ __delattr__.__qualname__ = f"{__dataclass__cls.__qualname__}.__delattr__"
646
+ if '__delattr__' in __dataclass__cls.__dict__:
647
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __delattr__ in class {__dataclass__cls.__name__}")
648
+ setattr(__dataclass__cls, '__delattr__', __delattr__)
649
+
650
+ def __hash__(self):
651
+ return hash((
652
+ self.ps,
653
+ ))
654
+
655
+ __hash__.__qualname__ = f"{__dataclass__cls.__qualname__}.__hash__"
656
+ setattr(__dataclass__cls, '__hash__', __hash__)
657
+
658
+ def __init__(
659
+ self,
660
+ ps: __dataclass__init__fields__0__annotation,
661
+ ) -> __dataclass__None:
662
+ __dataclass__object_setattr(self, 'ps', ps)
663
+
664
+ __init__.__qualname__ = f"{__dataclass__cls.__qualname__}.__init__"
665
+ if '__init__' in __dataclass__cls.__dict__:
666
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __init__ in class {__dataclass__cls.__name__}")
667
+ setattr(__dataclass__cls, '__init__', __init__)
668
+
669
+ @__dataclass___recursive_repr()
670
+ def __repr__(self):
671
+ parts = []
672
+ parts.append(f"ps={self.ps!r}")
673
+ return (
674
+ f"{self.__class__.__qualname__}("
675
+ f"{', '.join(parts)}"
676
+ f")"
677
+ )
678
+
679
+ __repr__.__qualname__ = f"{__dataclass__cls.__qualname__}.__repr__"
680
+ if '__repr__' in __dataclass__cls.__dict__:
681
+ raise __dataclass__TypeError(f"Cannot overwrite attribute __repr__ in class {__dataclass__cls.__name__}")
682
+ setattr(__dataclass__cls, '__repr__', __repr__)
683
+
684
+ return _process_dataclass
omextra/text/abnf/base.py CHANGED
@@ -1,105 +1,11 @@
1
- import io
2
- import itertools
1
+ import abc
3
2
  import typing as ta
4
3
 
5
4
  from omlish import check
6
5
  from omlish import lang
7
6
 
8
- from .errors import AbnfError
9
- from .errors import AbnfIncompleteParseError
10
7
 
11
-
12
- with lang.auto_proxy_import(globals()):
13
- from . import ops
14
- from . import parsing
15
-
16
-
17
- ##
18
-
19
-
20
- @ta.final
21
- class Match(ta.NamedTuple):
22
- op: 'Op'
23
- start: int
24
- end: int
25
- children: tuple['Match', ...]
26
-
27
- @property
28
- def length(self) -> int:
29
- return self.end - self.start
30
-
31
- #
32
-
33
- def __repr__(self) -> str:
34
- return (
35
- f'{self.__class__.__name__}('
36
- f'{self.op._match_repr()}, ' # noqa
37
- f'{self.start}, {self.end}'
38
- f'{f", {self.children!r}" if self.children else ""})'
39
- )
40
-
41
- def render_to(
42
- self,
43
- write: ta.Callable[[str], ta.Any],
44
- *,
45
- indent: int | None = None,
46
- _depth: int = 0,
47
- ) -> None:
48
- ix: str | None = (' ' * (indent * _depth)) if indent is not None else None
49
- if ix:
50
- write(ix)
51
- o = self.op
52
- if isinstance(o, (ops.StringLiteral, ops.CaseInsensitiveStringLiteral)):
53
- write(f'literal<{self.start}-{self.end}>({o.value!r})')
54
- elif isinstance(o, ops.RangeLiteral):
55
- write(f'literal<{self.start}-{self.end}>({o.value.lo!r}-{o.value.hi!r})')
56
- else:
57
- write(f'{o.__class__.__name__.lower()}<{self.start}-{self.end}>')
58
- if isinstance(o, ops.RuleRef):
59
- write(f':{o.name}')
60
- if self.children:
61
- write('(')
62
- if ix is not None:
63
- write('\n')
64
- for i, c in enumerate(self.children):
65
- if i and ix is None:
66
- write(', ')
67
- c.render_to(write, indent=indent, _depth=_depth + 1)
68
- if ix is not None:
69
- write(',\n')
70
- if ix:
71
- write(ix)
72
- write(')')
73
-
74
- def render(
75
- self,
76
- *,
77
- indent: int | None = None,
78
- ) -> str:
79
- sb = io.StringIO()
80
- self.render_to(sb.write, indent=indent)
81
- return sb.getvalue()
82
-
83
- def __str__(self) -> str:
84
- return self.render()
85
-
86
- #
87
-
88
- def map_children(self, fn: ta.Callable[['Match'], 'Match']) -> 'Match':
89
- return self._replace(children=tuple(map(fn, self.children)))
90
-
91
- def flat_map_children(self, fn: ta.Callable[['Match'], ta.Iterable['Match']]) -> 'Match':
92
- return self._replace(children=tuple(itertools.chain.from_iterable(map(fn, self.children))))
93
-
94
-
95
- def longest_match(ms: ta.Iterable[Match]) -> Match | None:
96
- bm: Match | None = None
97
- bl = 0
98
- for m in ms:
99
- l = m.length
100
- if bm is None or l > bl:
101
- bm, bl = m, l
102
- return bm
8
+ OpTuple: ta.TypeAlias = tuple['Op', ...]
103
9
 
104
10
 
105
11
  ##
@@ -110,171 +16,29 @@ class Op(lang.Abstract, lang.PackageSealed):
110
16
  return f'{self.__class__.__name__}@{id(self):x}'
111
17
 
112
18
 
113
- class LeafOp(Op, lang.Abstract):
114
- pass
115
-
116
-
117
- ##
118
-
119
-
120
- class Rule(lang.Final):
121
- def __init__(
122
- self,
123
- name: str,
124
- op: Op,
125
- *,
126
- insignificant: bool = False,
127
- ) -> None:
128
- super().__init__()
129
-
130
- self._name = check.non_empty_str(name)
131
- self._name_f = name.casefold()
132
- self._op = check.isinstance(op, Op)
133
- self._insignificant = insignificant
19
+ class CompositeOp(Op, lang.Abstract):
20
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
21
+ super().__init_subclass__(**kwargs)
134
22
 
135
- def __repr__(self) -> str:
136
- return f'{self.__class__.__name__}({self._name!r})'
137
-
138
- @property
139
- def name(self) -> str:
140
- return self._name
141
-
142
- @property
143
- def name_f(self) -> str:
144
- return self._name_f
145
-
146
- @property
147
- def op(self) -> Op:
148
- return self._op
149
-
150
- @property
151
- def insignificant(self) -> bool:
152
- return self._insignificant
153
-
154
-
155
- class Grammar(lang.Final):
156
- def __init__(
157
- self,
158
- *rules: Rule,
159
- root: Rule | str | None = None,
160
- ) -> None:
161
- super().__init__()
162
-
163
- rules_set: set[Rule] = set()
164
- rules_by_name: dict[str, Rule] = {}
165
- rules_by_name_f: dict[str, Rule] = {}
166
- rules_by_op: dict[Op, Rule] = {}
167
- for gr in rules:
168
- check.not_in(gr, rules_set)
169
- check.not_in(gr._name, rules_by_name) # noqa
170
- check.not_in(gr._name_f, rules_by_name_f) # noqa
171
- check.not_in(gr._op, rules_by_op) # noqa
172
- rules_set.add(gr)
173
- rules_by_name[gr._name] = gr # noqa
174
- rules_by_name_f[gr._name_f] = gr # noqa
175
- rules_by_op[gr._op] = gr # noqa
176
- self._rules = rules_set
177
- self._rules_by_name: ta.Mapping[str, Rule] = rules_by_name
178
- self._rules_by_name_f: ta.Mapping[str, Rule] = rules_by_name_f
179
- self._rules_by_op: ta.Mapping[Op, Rule] = rules_by_op
180
-
181
- if isinstance(root, str):
182
- root = rules_by_name_f[root.casefold()]
183
- self._root = root
184
-
185
- @property
186
- def root(self) -> Rule | None:
187
- return self._root
188
-
189
- def rule(self, name: str) -> Rule | None:
190
- return self._rules_by_name_f.get(name.casefold())
191
-
192
- def iter_parse(
193
- self,
194
- source: str,
195
- root: Rule | str | None = None,
196
- *,
197
- start: int = 0,
198
- debug: int = 0,
199
- ) -> ta.Iterator[Match]:
200
- if root is None:
201
- if (root := self._root) is None:
202
- raise AbnfError('No root or default root specified')
23
+ try:
24
+ leaf_op_cls = LeafOp
25
+ except NameError:
26
+ pass
203
27
  else:
204
- if isinstance(root, str):
205
- root = self._rules_by_name_f[root.casefold()]
206
- else:
207
- root = check.in_(check.isinstance(root, Rule), self._rules)
208
-
209
- return parsing._iter_parse( # noqa
210
- self,
211
- source,
212
- root._op, # noqa
213
- start,
214
- debug=debug,
215
- )
216
-
217
- def parse(
218
- self,
219
- source: str,
220
- root: str | None = None,
221
- *,
222
- start: int = 0,
223
- complete: bool = False,
224
- debug: int = 0,
225
- ) -> Match | None:
226
- if (match := longest_match(self.iter_parse(
227
- source,
228
- root,
229
- start=start,
230
- debug=debug,
231
- ))) is None:
232
- return None
233
-
234
- if complete and (match.start, match.end) != (start, len(source)):
235
- raise AbnfIncompleteParseError
236
-
237
- return match
238
-
239
-
240
- ##
28
+ check.not_issubclass(cls, leaf_op_cls)
241
29
 
30
+ @property
31
+ @abc.abstractmethod
32
+ def children(self) -> OpTuple:
33
+ raise NotImplementedError
242
34
 
243
- def iter_parse(
244
- obj: Grammar | Rule | Op,
245
- src: str,
246
- *,
247
- root: str | None = None,
248
- start: int = 0,
249
- ) -> ta.Iterator[Match]:
250
- if isinstance(obj, Grammar):
251
- gram = obj
252
- elif isinstance(obj, Rule):
253
- check.none(root)
254
- gram = Grammar(obj, root=obj)
255
- elif isinstance(obj, Op):
256
- check.none(root)
257
- gram = Grammar(Rule('root', obj), root='root')
258
- else:
259
- raise TypeError(obj)
35
+ @abc.abstractmethod
36
+ def replace_children(self, *children: Op) -> Op:
37
+ raise NotImplementedError
260
38
 
261
- return gram.iter_parse(
262
- src,
263
- root,
264
- start=start,
265
- )
266
39
 
40
+ class LeafOp(Op, lang.Abstract):
41
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
42
+ super().__init_subclass__(**kwargs)
267
43
 
268
- def parse(
269
- obj: Grammar | Rule | Op,
270
- src: str,
271
- *,
272
- root: str | None = None,
273
- start: int = 0,
274
- ) -> Match | None:
275
- return longest_match(iter_parse(
276
- obj,
277
- src,
278
- root=root,
279
- start=start,
280
- ))
44
+ check.not_issubclass(cls, CompositeOp)