syncraft 0.1.21__tar.gz → 0.1.22__tar.gz

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.
Files changed (27) hide show
  1. {syncraft-0.1.21 → syncraft-0.1.22}/PKG-INFO +1 -1
  2. {syncraft-0.1.21 → syncraft-0.1.22}/pyproject.toml +1 -1
  3. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/algebra.py +19 -191
  4. syncraft-0.1.22/syncraft/ast.py +345 -0
  5. syncraft-0.1.22/syncraft/constraint.py +189 -0
  6. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/diagnostic.py +5 -5
  7. syncraft-0.1.22/syncraft/finder.py +79 -0
  8. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/generator.py +12 -4
  9. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/parser.py +6 -3
  10. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/syntax.py +35 -41
  11. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/PKG-INFO +1 -1
  12. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/SOURCES.txt +1 -1
  13. {syncraft-0.1.21 → syncraft-0.1.22}/tests/test_bimap.py +29 -143
  14. {syncraft-0.1.21 → syncraft-0.1.22}/tests/test_parse.py +2 -1
  15. syncraft-0.1.21/syncraft/ast.py +0 -137
  16. syncraft-0.1.21/syncraft/cmd.py +0 -61
  17. syncraft-0.1.21/syncraft/finder.py +0 -149
  18. {syncraft-0.1.21 → syncraft-0.1.22}/LICENSE +0 -0
  19. {syncraft-0.1.21 → syncraft-0.1.22}/README.md +0 -0
  20. {syncraft-0.1.21 → syncraft-0.1.22}/setup.cfg +0 -0
  21. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/__init__.py +0 -0
  22. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/py.typed +0 -0
  23. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft/sqlite3.py +0 -0
  24. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/dependency_links.txt +0 -0
  25. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/requires.txt +0 -0
  26. {syncraft-0.1.21 → syncraft-0.1.22}/syncraft.egg-info/top_level.txt +0 -0
  27. {syncraft-0.1.21 → syncraft-0.1.22}/tests/test_until.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.21
3
+ Version: 0.1.22
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "syncraft"
3
- version = "0.1.21"
3
+ version = "0.1.22"
4
4
  description = "Parser combinator library"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -5,200 +5,15 @@ from typing import (
5
5
  )
6
6
 
7
7
  import traceback
8
- from dataclasses import dataclass, replace
8
+ from dataclasses import dataclass, replace, asdict
9
9
  from weakref import WeakKeyDictionary
10
10
  from abc import ABC
11
- from enum import Enum
12
- from functools import reduce
13
-
14
-
15
- A = TypeVar('A')
16
- B = TypeVar('B')
17
- C = TypeVar('C')
18
- S = TypeVar('S')
19
-
20
-
21
- @dataclass(frozen=True)
22
- class Bimap(Generic[S, A, B]):
23
- forward: Callable[[S, A], Tuple[S, B]]
24
- inverse: Callable[[S, B], Tuple[S, A]]
25
- def __rshift__(self, other: Bimap[S, B, C]) -> Bimap[S, A, C]:
26
- def fwd(s: S, a: A) -> Tuple[S, C]:
27
- s1, b = self.forward(s, a)
28
- return other.forward(s1, b)
29
- def inv(s: S, c: C) -> Tuple[S, A]:
30
- s1, b = other.inverse(s, c)
31
- return self.inverse(s1, b)
32
- return Bimap(
33
- forward=fwd,
34
- inverse=inv
35
- )
36
- @staticmethod
37
- def identity()->Bimap[S, A, A]:
38
- return Bimap(
39
- forward=lambda s, x: (s, x),
40
- inverse=lambda s, y: (s, y)
41
- )
42
-
43
- @staticmethod
44
- def when(condition: Callable[..., bool],
45
- then: Bimap[S, A, B],
46
- otherwise: Optional[Bimap[S, A, B]] = None) -> Callable[..., Bimap[S, A, B]]:
47
- def _when(*args:Any, **kwargs:Any) -> Bimap[S, A, B]:
48
- return then if condition(*args, **kwargs) else (otherwise or Bimap.identity())
49
- return _when
11
+ from syncraft.ast import ThenKind, ThenResult, ManyResult, OrResult, S
50
12
 
13
+ A = TypeVar('A') # Result type
14
+ B = TypeVar('B') # Mapped result type
15
+
51
16
 
52
-
53
- class StructuralResult:
54
- def bimap(self,
55
- before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
56
- after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
57
- ) -> Bimap[Any, Any, Any]:
58
- return Bimap.identity()
59
-
60
- @dataclass(frozen=True)
61
- class NamedResult(Generic[A], StructuralResult):
62
- name: str
63
- value: A
64
- def bimap(self,
65
- before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
66
- after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
67
- ) -> Bimap[Any, NamedResult[A], NamedResult[Any]]:
68
- inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
69
- def fwd(s: S, a: NamedResult[A])-> Tuple[S, NamedResult[Any]]:
70
- assert a == self, f"Expected {self}, got {a}"
71
- inner_s, inner_v = inner_b.forward(s, a.value)
72
- return (inner_s, replace(a, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, inner_v)
73
-
74
- def inv(s: S, a: NamedResult[Any]) -> Tuple[S, NamedResult[A]]:
75
- assert isinstance(a, NamedResult), f"Expected NamedResult, got {type(a)}"
76
- inner_s, inner_v = inner_b.inverse(s, a.value)
77
- return (inner_s, replace(self, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, replace(self, value=inner_v.value))
78
- ret: Bimap[Any, Any, Any] = Bimap(
79
- forward=fwd,
80
- inverse=inv
81
- )
82
- return before(self) >> ret >> after(self)
83
- @dataclass(eq=True, frozen=True)
84
- class ManyResult(Generic[A], StructuralResult):
85
- value: Tuple[A, ...]
86
- def bimap(self,
87
- before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
88
- after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
89
- ) -> Bimap[Any, ManyResult[A], List[A]]:
90
- # We don't allow zero length ManyResult e.g. many(at_least >= 1) at the syntax level
91
- # so inner_b has at least 1 element
92
- assert len(self.value) > 0, "ManyResult must have at least one element"
93
- inner_b = [v.bimap(before, after) if isinstance(v, StructuralResult) else before(v) >> after(v) for v in self.value]
94
- def fwd(s: Any, a: ManyResult[A]) -> Tuple[Any, List[A]]:
95
- assert a == self, f"Expected {self}, got {a}"
96
- return s, [inner_b[i].forward(s, v)[1] for i, v in enumerate(a.value)]
97
-
98
- def inv(s: Any, a: List[A]) -> Tuple[Any, ManyResult[A]]:
99
- assert isinstance(a, list), f"Expected list, got {type(a)}"
100
- ret = [inner_b[i].inverse(s, v)[1] for i, v in enumerate(a)]
101
- if len(ret) <= len(inner_b):
102
- return s, ManyResult(value=tuple(ret))
103
- else:
104
- extra = [inner_b[-1].inverse(s, v)[1] for v in a[len(inner_b):]]
105
- return s, ManyResult(value=tuple(ret + extra))
106
-
107
- ret = Bimap(
108
- forward=fwd,
109
- inverse=inv
110
- )
111
- return before(self) >> ret >> after(self)
112
-
113
- @dataclass(eq=True, frozen=True)
114
- class OrResult(Generic[A], StructuralResult):
115
- value: A
116
- def bimap(self,
117
- before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
118
- after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
119
- ) -> Bimap[Any, OrResult[A], Any]:
120
- inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
121
- def fwd(s: Any, a: OrResult[A]) -> Tuple[Any, Any]:
122
- assert a == self, f"Expected {self}, got {a}"
123
- return inner_b.forward(s, a.value)
124
-
125
- def inv(s: Any, a: Any) -> Tuple[Any, OrResult[A]]:
126
- inner_s, inner_v = inner_b.inverse(s, a)
127
- return inner_s, OrResult(value=inner_v)
128
-
129
- ret = Bimap(
130
- forward=fwd,
131
- inverse=inv
132
- )
133
- return before(self) >> ret >> after(self)
134
- class ThenKind(Enum):
135
- BOTH = '+'
136
- LEFT = '//'
137
- RIGHT = '>>'
138
-
139
- @dataclass(eq=True, frozen=True)
140
- class ThenResult(Generic[A, B], StructuralResult):
141
- kind: ThenKind
142
- left: A
143
- right: B
144
- def arity(self)->int:
145
- if self.kind == ThenKind.LEFT:
146
- return self.left.arity() if isinstance(self.left, ThenResult) else 1
147
- elif self.kind == ThenKind.RIGHT:
148
- return self.right.arity() if isinstance(self.right, ThenResult) else 1
149
- elif self.kind == ThenKind.BOTH:
150
- left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
151
- right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
152
- return left_arity + right_arity
153
- else:
154
- return 1
155
-
156
- def bimap(self,
157
- before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
158
- after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
159
- ) -> Bimap[Any, ThenResult[A, B], Tuple[Any, ...] | Any]:
160
- kind = self.kind
161
- lb = self.left.bimap(before, after) if isinstance(self.left, StructuralResult) else before(self.left) >> after(self.left)
162
- rb = self.right.bimap(before, after) if isinstance(self.right, StructuralResult) else before(self.right) >> after(self.right)
163
- left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
164
- right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
165
- def fwd(s : S, a : ThenResult[A, B]) -> Tuple[S, Tuple[Any, ...] | Any]:
166
- assert a == self, f"Expected {self}, got {a}"
167
- match kind:
168
- case ThenKind.LEFT:
169
- return lb.forward(s, a.left)
170
- case ThenKind.RIGHT:
171
- return rb.forward(s, a.right)
172
- case ThenKind.BOTH:
173
- s1, left_v = lb.forward(s, a.left)
174
- s2, right_v = rb.forward(s1, a.right)
175
- left_v = (left_v,) if not isinstance(a.left, ThenResult) else left_v
176
- right_v = (right_v,) if not isinstance(a.right, ThenResult) else right_v
177
- return s2, left_v + right_v
178
-
179
- def inv(s: S, b: Tuple[Any, ...] | Any) -> Tuple[S, ThenResult[A, B]]:
180
- match kind:
181
- case ThenKind.LEFT:
182
- s1, lv = lb.inverse(s, b)
183
- return s1, replace(self, left=lv)
184
- case ThenKind.RIGHT:
185
- s1, rv = rb.inverse(s, b)
186
- return s1, replace(self, right=rv)
187
- case ThenKind.BOTH:
188
- lraw = b[:left_size]
189
- rraw = b[left_size:left_size + right_size]
190
- lraw = lraw[0] if left_size == 1 else lraw
191
- rraw = rraw[0] if right_size == 1 else rraw
192
- s1, lv = lb.inverse(s, lraw)
193
- s2, rv = rb.inverse(s1, rraw)
194
- return s2, replace(self, left=lv, right=rv)
195
-
196
- ret: Bimap[Any, Any, Any] = Bimap(
197
- forward=fwd,
198
- inverse=inv
199
- )
200
- return before(self) >> ret >> after(self)
201
-
202
17
  InProgress = object() # Marker for in-progress state, used to prevent re-entrance in recursive calls
203
18
  L = TypeVar('L') # Left type for combined results
204
19
  R = TypeVar('R') # Right type for combined results
@@ -242,7 +57,14 @@ class Error:
242
57
  state=state,
243
58
  previous=self
244
59
  )
245
-
60
+ def to_list(self)->List[Dict[str, Any]]:
61
+ lst = []
62
+ current: Optional[Error] = self
63
+ while current is not None:
64
+ d = asdict(current)
65
+ lst.append({k:v for k,v in d.items() if v is not None and k != 'previous'})
66
+ current = current.previous
67
+ return lst
246
68
 
247
69
 
248
70
  @dataclass(frozen=True)
@@ -423,6 +245,12 @@ class Algebra(ABC, Generic[A, S]):
423
245
  return cast(Either[Any, Tuple[B, S]], parsed)
424
246
  return self.__class__(map_run, name=self.name) # type: ignore
425
247
 
248
+ def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]])->Algebra[B, S]:
249
+ def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
250
+ parsed = self.run(input, use_cache)
251
+ return f(parsed)
252
+ return self.__class__(map_all_run, name=self.name) # type: ignore
253
+
426
254
  def map_error(self, f: Callable[[Optional[Any]], Any]) -> Algebra[A, S]:
427
255
  def map_error_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
428
256
  parsed = self.run(input, use_cache)
@@ -0,0 +1,345 @@
1
+
2
+
3
+ from __future__ import annotations
4
+ import re
5
+ from typing import (
6
+ Optional, Any, TypeVar, Tuple, runtime_checkable,
7
+ Dict, Generic, Callable, Union, cast, List, Protocol, Type
8
+ )
9
+
10
+
11
+ from dataclasses import dataclass, replace, is_dataclass, asdict
12
+ from enum import Enum
13
+ from functools import cached_property
14
+ from syncraft.constraint import Binding, Variable, Bindable
15
+
16
+
17
+
18
+ A = TypeVar('A')
19
+ B = TypeVar('B')
20
+ C = TypeVar('C')
21
+ S = TypeVar('S', bound=Bindable)
22
+
23
+ @dataclass(frozen=True)
24
+ class Reducer(Generic[A, S]):
25
+ run_f: Callable[[A, S], S]
26
+ def __call__(self, a: A, s: S) -> S:
27
+ return self.run_f(a, s)
28
+
29
+ def map(self, f: Callable[[B], A]) -> Reducer[B, S]:
30
+ def map_run(b: B, s: S) -> S:
31
+ return self(f(b), s)
32
+ return Reducer(map_run)
33
+
34
+
35
+
36
+ @dataclass(frozen=True)
37
+ class Bimap(Generic[A, B]):
38
+ run_f: Callable[[A], Tuple[B, Callable[[B], A]]]
39
+ def __call__(self, a: A) -> Tuple[B, Callable[[B], A]]:
40
+ return self.run_f(a)
41
+ def __rshift__(self, other: Bimap[B, C]) -> Bimap[A, C]:
42
+ def then_run(a: A) -> Tuple[C, Callable[[C], A]]:
43
+ b, inv1 = self(a)
44
+ c, inv2 = other(b)
45
+ def inv(c2: C) -> A:
46
+ return inv1(inv2(c2))
47
+ return c, inv
48
+ return Bimap(then_run)
49
+ @staticmethod
50
+ def const(a: B)->Bimap[B, B]:
51
+ return Bimap(lambda _: (a, lambda b: b))
52
+
53
+ @staticmethod
54
+ def identity()->Bimap[Any, Any]:
55
+ return Bimap(lambda a: (a, lambda b: b))
56
+
57
+ @staticmethod
58
+ def when(cond: Callable[[A], bool],
59
+ then: Bimap[A, B],
60
+ otherwise: Optional[Bimap[A, C]] = None) -> Bimap[A, A | B | C]:
61
+ def when_run(a:A) -> Tuple[A | B | C, Callable[[A | B | C], A]]:
62
+ bimap = then if cond(a) else (otherwise if otherwise is not None else Bimap.identity())
63
+ abc, inv = bimap(a)
64
+ def inv_f(b: Any) -> A:
65
+ return inv(b)
66
+ return abc, inv_f
67
+ return Bimap(when_run)
68
+
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class Biarrow(Generic[S, A, B]):
73
+ forward: Callable[[S, A], Tuple[S, B]]
74
+ inverse: Callable[[S, B], Tuple[S, A]]
75
+ def __rshift__(self, other: Biarrow[S, B, C]) -> Biarrow[S, A, C]:
76
+ def fwd(s: S, a: A) -> Tuple[S, C]:
77
+ s1, b = self.forward(s, a)
78
+ return other.forward(s1, b)
79
+ def inv(s: S, c: C) -> Tuple[S, A]:
80
+ s1, b = other.inverse(s, c)
81
+ return self.inverse(s1, b)
82
+ return Biarrow(
83
+ forward=fwd,
84
+ inverse=inv
85
+ )
86
+ @staticmethod
87
+ def identity()->Biarrow[S, A, A]:
88
+ return Biarrow(
89
+ forward=lambda s, x: (s, x),
90
+ inverse=lambda s, y: (s, y)
91
+ )
92
+
93
+ @staticmethod
94
+ def when(condition: Callable[..., bool],
95
+ then: Biarrow[S, A, B],
96
+ otherwise: Optional[Biarrow[S, A, B]] = None) -> Callable[..., Biarrow[S, A, B]]:
97
+ def _when(*args:Any, **kwargs:Any) -> Biarrow[S, A, B]:
98
+ return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
99
+ return _when
100
+
101
+
102
+
103
+ class StructuralResult:
104
+
105
+ def bimap(self, f: Bimap[Any, Any]=Bimap.identity())->Tuple[Any, Callable[[Any], Any]]:
106
+ return f(self)
107
+
108
+
109
+ @dataclass(frozen=True)
110
+ class MarkedResult(Generic[A], StructuralResult):
111
+ name: str
112
+ value: A
113
+ def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[MarkedResult[B], Callable[[MarkedResult[B]], MarkedResult[A]]]:
114
+ b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
115
+ return MarkedResult(name=self.name, value=b), lambda b: replace(self, value=inv(b.value))
116
+
117
+
118
+ @dataclass(eq=True, frozen=True)
119
+ class ManyResult(Generic[A], StructuralResult):
120
+ value: Tuple[A, ...]
121
+ def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[List[B], Callable[[List[B]], ManyResult[A]]]:
122
+ assert self.value
123
+ forward = [v.bimap(f) if isinstance(v, StructuralResult) else f(v) for v in self.value]
124
+ def invf(b: List[B]) -> ManyResult[A]:
125
+ assert len(b) <= len(forward)
126
+ return replace(self, value=tuple([forward[i][1](bb) for i, bb in enumerate(b)]))
127
+ return [b for b, _ in forward], invf
128
+
129
+
130
+
131
+ @dataclass(eq=True, frozen=True)
132
+ class OrResult(Generic[A], StructuralResult):
133
+ value: A
134
+ def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[B, Callable[[B], OrResult[A]]]:
135
+ b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
136
+ return b, lambda b: replace(self, value=inv(b))
137
+
138
+ class ThenKind(Enum):
139
+ BOTH = '+'
140
+ LEFT = '//'
141
+ RIGHT = '>>'
142
+
143
+ FlatThen = Tuple[Any, ...]
144
+ MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
145
+
146
+ @dataclass(eq=True, frozen=True)
147
+ class ThenResult(Generic[A, B], StructuralResult):
148
+ kind: ThenKind
149
+ left: A
150
+ right: B
151
+ @staticmethod
152
+ def collect_marked(a: FlatThen, f: Optional[Callable[..., Any]] = None)->Tuple[MarkedThen, Callable[[MarkedThen], FlatThen]]:
153
+ index: List[str | int] = []
154
+ named_count = 0
155
+ for i, v in enumerate(a):
156
+ if isinstance(v, MarkedResult):
157
+ index.append(v.name)
158
+ named_count += 1
159
+ else:
160
+ index.append(i - named_count)
161
+ named = {v.name: v.value for v in a if isinstance(v, MarkedResult)}
162
+ unnamed = [v for v in a if not isinstance(v, MarkedResult)]
163
+ if f is None:
164
+ ret = (named, tuple(unnamed))
165
+ else:
166
+ ret = (f(**named), tuple(unnamed))
167
+ def invf(b: MarkedThen) -> Tuple[Any, ...]:
168
+ named_value, unnamed_value = b
169
+ assert isinstance(named_value, dict) or is_dataclass(named_value), f"Expected dict or dataclass for named values, got {type(named_value)}"
170
+ if is_dataclass(named_value):
171
+ named_dict = named | asdict(cast(Any, named_value))
172
+ else:
173
+ named_dict = named | named_value
174
+ ret = []
175
+ for x in index:
176
+ if isinstance(x, str):
177
+ assert x in named_dict, f"Missing named value: {x}"
178
+ ret.append(named_dict[x])
179
+ else:
180
+ assert 0 <= x < len(unnamed_value), f"Missing unnamed value at index: {x}"
181
+ ret.append(unnamed_value[x])
182
+ return tuple(ret)
183
+ return ret, invf
184
+
185
+ def bimap(self, f: Bimap[Any, Any]=Bimap.identity()) -> Tuple[MarkedThen, Callable[[MarkedThen], ThenResult[A, B]]]:
186
+ match self.kind:
187
+ case ThenKind.LEFT:
188
+ lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
189
+ return lb, lambda b: replace(self, left=linv(b))
190
+ case ThenKind.RIGHT:
191
+ rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
192
+ return rb, lambda b: replace(self, right=rinv(b))
193
+ case ThenKind.BOTH:
194
+ lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
195
+ rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
196
+ left_v = (lb,) if not isinstance(self.left, ThenResult) else lb
197
+ right_v = (rb,) if not isinstance(self.right, ThenResult) else rb
198
+ def invf(b: Tuple[Any, ...]) -> ThenResult[A, B]:
199
+ left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
200
+ right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
201
+ lraw = b[:left_size]
202
+ rraw = b[left_size:left_size + right_size]
203
+ lraw = lraw[0] if left_size == 1 else lraw
204
+ rraw = rraw[0] if right_size == 1 else rraw
205
+ la = linv(lraw)
206
+ ra = rinv(rraw)
207
+ return replace(self, left=la, right=ra)
208
+ return left_v + right_v, invf
209
+ # data, func = ThenResult.collect_marked(left_v + right_v)
210
+ # return data, lambda d: invf(func(d))
211
+
212
+
213
+ def arity(self)->int:
214
+ if self.kind == ThenKind.LEFT:
215
+ return self.left.arity() if isinstance(self.left, ThenResult) else 1
216
+ elif self.kind == ThenKind.RIGHT:
217
+ return self.right.arity() if isinstance(self.right, ThenResult) else 1
218
+ elif self.kind == ThenKind.BOTH:
219
+ left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
220
+ right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
221
+ return left_arity + right_arity
222
+ else:
223
+ return 1
224
+
225
+ @runtime_checkable
226
+ class TokenProtocol(Protocol):
227
+ @property
228
+ def token_type(self) -> Enum: ...
229
+ @property
230
+ def text(self) -> str: ...
231
+
232
+
233
+ @dataclass(frozen=True)
234
+ class Token:
235
+ token_type: Enum
236
+ text: str
237
+ def __str__(self) -> str:
238
+ return f"{self.token_type.name}({self.text})"
239
+
240
+ def __repr__(self) -> str:
241
+ return self.__str__()
242
+
243
+
244
+
245
+ @dataclass(frozen=True)
246
+ class TokenSpec:
247
+ token_type: Optional[Enum] = None
248
+ text: Optional[str] = None
249
+ case_sensitive: bool = False
250
+ regex: Optional[re.Pattern[str]] = None
251
+
252
+ def is_valid(self, token: TokenProtocol) -> bool:
253
+ type_match = self.token_type is None or token.token_type == self.token_type
254
+ value_match = self.text is None or (token.text.strip() == self.text.strip() if self.case_sensitive else
255
+ token.text.strip().upper() == self.text.strip().upper())
256
+ value_match = value_match or (self.regex is not None and self.regex.fullmatch(token.text) is not None)
257
+ return type_match and value_match
258
+
259
+
260
+
261
+
262
+ T = TypeVar('T', bound=TokenProtocol)
263
+
264
+
265
+ ParseResult = Union[
266
+ ThenResult['ParseResult[T]', 'ParseResult[T]'],
267
+ MarkedResult['ParseResult[T]'],
268
+ ManyResult['ParseResult[T]'],
269
+ OrResult['ParseResult[T]'],
270
+ T,
271
+ ]
272
+
273
+
274
+
275
+ @dataclass(frozen=True)
276
+ class AST(Generic[T]):
277
+ focus: ParseResult[T]
278
+ pruned: bool = False
279
+ parent: Optional[AST[T]] = None
280
+
281
+ def bimap(self)->Tuple[Any, Callable[[Any], AST[T]]]:
282
+ if isinstance(self.focus, StructuralResult):
283
+ data, invf = self.focus.bimap()
284
+ return data, lambda x: replace(self, focus=invf(x))
285
+ else:
286
+ return self.focus, lambda x: replace(self, focus=x)
287
+
288
+ def wrapper(self)-> Callable[[Any], Any]:
289
+ if isinstance(self.focus, MarkedResult):
290
+ focus = cast(MarkedResult[Any], self.focus)
291
+ return lambda x: MarkedResult(name = focus.name, value = x)
292
+ else:
293
+ return lambda x: x
294
+
295
+ def is_named(self) -> bool:
296
+ return isinstance(self.focus, MarkedResult)
297
+
298
+ def left(self) -> Optional[AST[T]]:
299
+ match self.focus:
300
+ case ThenResult(left=left, kind=kind):
301
+ return replace(self, focus=left, parent=self, pruned = self.pruned or kind == ThenKind.RIGHT)
302
+ case _:
303
+ raise TypeError(f"Invalid focus type({self.focus}) for left traversal")
304
+
305
+ def right(self) -> Optional[AST[T]]:
306
+ match self.focus:
307
+ case ThenResult(right=right, kind=kind):
308
+ return replace(self, focus=right, parent=self, pruned = self.pruned or kind == ThenKind.LEFT)
309
+ case _:
310
+ raise TypeError(f"Invalid focus type({self.focus}) for right traversal")
311
+
312
+
313
+ def down(self, index: int) -> Optional[AST[T]]:
314
+ match self.focus:
315
+ case ManyResult(value=children):
316
+ if 0 <= index < len(children):
317
+ return replace(self, focus=children[index], parent=self, pruned=self.pruned)
318
+ else:
319
+ raise IndexError(f"Index {index} out of bounds for ManyResult with {len(children)} children")
320
+ case OrResult(value=value):
321
+ if index == 0:
322
+ return replace(self, focus=value, parent=self, pruned=self.pruned)
323
+ else:
324
+ raise IndexError(f"Index {index} out of bounds for OrResult")
325
+ case MarkedResult(value=value):
326
+ return replace(self, focus=value, parent=self, pruned=self.pruned)
327
+ case _:
328
+ raise TypeError(f"Invalid focus type({self.focus}) for down traversal")
329
+
330
+ def how_many(self)->int:
331
+ focus = self.focus.value if isinstance(self.focus, MarkedResult) else self.focus
332
+ match focus:
333
+ case ManyResult(value=children):
334
+ return len(children)
335
+ case _:
336
+ raise TypeError(f"Invalid focus type({self.focus}) for how_many")
337
+
338
+
339
+
340
+ @cached_property
341
+ def root(self) -> AST[T]:
342
+ while self.parent is not None:
343
+ self = self.parent
344
+ return self
345
+