syncraft 0.1.26__tar.gz → 0.1.28__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.

Potentially problematic release.


This version of syncraft might be problematic. Click here for more details.

Files changed (25) hide show
  1. {syncraft-0.1.26 → syncraft-0.1.28}/PKG-INFO +3 -3
  2. {syncraft-0.1.26 → syncraft-0.1.28}/README.md +2 -2
  3. {syncraft-0.1.26 → syncraft-0.1.28}/pyproject.toml +1 -1
  4. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/algebra.py +38 -26
  5. syncraft-0.1.28/syncraft/ast.py +378 -0
  6. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/finder.py +4 -4
  7. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/generator.py +28 -16
  8. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/syntax.py +83 -63
  9. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft.egg-info/PKG-INFO +3 -3
  10. {syncraft-0.1.26 → syncraft-0.1.28}/tests/test_bimap.py +5 -2
  11. syncraft-0.1.26/syncraft/ast.py +0 -187
  12. {syncraft-0.1.26 → syncraft-0.1.28}/LICENSE +0 -0
  13. {syncraft-0.1.26 → syncraft-0.1.28}/setup.cfg +0 -0
  14. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/__init__.py +0 -0
  15. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/constraint.py +0 -0
  16. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/diagnostic.py +0 -0
  17. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/parser.py +0 -0
  18. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/py.typed +0 -0
  19. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft/sqlite3.py +0 -0
  20. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft.egg-info/SOURCES.txt +0 -0
  21. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft.egg-info/dependency_links.txt +0 -0
  22. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft.egg-info/requires.txt +0 -0
  23. {syncraft-0.1.26 → syncraft-0.1.28}/syncraft.egg-info/top_level.txt +0 -0
  24. {syncraft-0.1.26 → syncraft-0.1.28}/tests/test_parse.py +0 -0
  25. {syncraft-0.1.26 → syncraft-0.1.28}/tests/test_until.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.26
3
+ Version: 0.1.28
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -19,8 +19,6 @@ Syncraft is a parser/generator combinator library with full round-trip support:
19
19
 
20
20
  - Parse source code into AST or dataclasses
21
21
  - Generate source code from dataclasses
22
- - Bidirectional transformations via lenses
23
- - Convenience combinators: `all`, `first`, `last`, `named`
24
22
  - SQLite syntax support included
25
23
 
26
24
  ## Installation
@@ -33,5 +31,7 @@ pip install syncraft
33
31
  ## TODO
34
32
  - [ ] simplify the result of then_left and then_right by bimap the result in syntax.
35
33
  - [ ] simplify the result of sep_by and between by bimap the result in syntax
34
+ - [ ] convert to dict/dataclass via bimap in syntax
35
+ - [ ] define DSL over Variable to construct predicates
36
36
  - [ ] Try the parsing, generation, and data processing machinery on SQLite3 syntax. So that I can have direct feedback on the usability of this library and a fully functional SQLite3 library.
37
37
  - [ ] Make the library as fast as possible and feasible.
@@ -4,8 +4,6 @@ Syncraft is a parser/generator combinator library with full round-trip support:
4
4
 
5
5
  - Parse source code into AST or dataclasses
6
6
  - Generate source code from dataclasses
7
- - Bidirectional transformations via lenses
8
- - Convenience combinators: `all`, `first`, `last`, `named`
9
7
  - SQLite syntax support included
10
8
 
11
9
  ## Installation
@@ -18,5 +16,7 @@ pip install syncraft
18
16
  ## TODO
19
17
  - [ ] simplify the result of then_left and then_right by bimap the result in syntax.
20
18
  - [ ] simplify the result of sep_by and between by bimap the result in syntax
19
+ - [ ] convert to dict/dataclass via bimap in syntax
20
+ - [ ] define DSL over Variable to construct predicates
21
21
  - [ ] Try the parsing, generation, and data processing machinery on SQLite3 syntax. So that I can have direct feedback on the usability of this library and a fully functional SQLite3 library.
22
22
  - [ ] Make the library as fast as possible and feasible.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "syncraft"
3
- version = "0.1.26"
3
+ version = "0.1.28"
4
4
  description = "Parser combinator library"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -128,10 +128,7 @@ class Algebra(ABC, Generic[A, S]):
128
128
  def lazy_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
129
129
  return thunk().run(input, use_cache)
130
130
  return cls(lazy_run, name=cls.__name__ + '.lazy')
131
-
132
-
133
-
134
-
131
+
135
132
  @classmethod
136
133
  def fail(cls, error: Any) -> Algebra[Any, S]:
137
134
  def fail_run(input: S, use_cache:bool) -> Either[Any, Tuple[Any, S]]:
@@ -141,6 +138,7 @@ class Algebra(ABC, Generic[A, S]):
141
138
  state=input
142
139
  ))
143
140
  return cls(fail_run, name=cls.__name__ + '.fail')
141
+
144
142
  @classmethod
145
143
  def success(cls, value: Any) -> Algebra[Any, S]:
146
144
  def success_run(input: S, use_cache:bool) -> Either[Any, Tuple[Any, S]]:
@@ -234,7 +232,35 @@ class Algebra(ABC, Generic[A, S]):
234
232
  lazy_self = self.__class__(debug_run, name=label)
235
233
  return lazy_self
236
234
 
235
+ ######################################################## map on state ###########################################
236
+ def post_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
237
+ def post_state_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
238
+ match self.run(input, use_cache):
239
+ case Right((value, state)):
240
+ return Right((value, f(state)))
241
+ case Left(err):
242
+ return Left(err)
243
+ case x:
244
+ raise ValueError(f"Unexpected result from self.run {x}")
245
+ return self.__class__(post_state_run, name=self.name)
246
+
247
+ def pre_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
248
+ def pre_state_run(state: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
249
+ return self.run(f(state), use_cache)
250
+ return self.__class__(pre_state_run, name=self.name)
251
+
237
252
 
253
+ def map_all(self, f: Callable[[A, S], Tuple[B, S]]) -> Algebra[B, S]:
254
+ def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
255
+ match self.run(input, use_cache):
256
+ case Right((value, state)):
257
+ new_value, new_state = f(value, state)
258
+ return Right((new_value, new_state))
259
+ case Left(err):
260
+ return Left(err)
261
+ case x:
262
+ raise ValueError(f"Unexpected result from self.run {x}")
263
+ return self.__class__(map_all_run, name=self.name) # type: ignore
238
264
  ######################################################## fundamental combinators ############################################
239
265
  def fmap(self, f: Callable[[A], B]) -> Algebra[B, S]:
240
266
  def fmap_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
@@ -245,20 +271,12 @@ class Algebra(ABC, Generic[A, S]):
245
271
  return cast(Either[Any, Tuple[B, S]], parsed)
246
272
  return self.__class__(fmap_run, name=self.name) # type: ignore
247
273
 
248
- def imap(self, f: Callable[[B], A]) -> Algebra[A, S]:
249
- return self.map_state(lambda s: s.map(f))
250
274
 
251
275
  def map(self, f: Callable[[A], B]) -> Algebra[B, S]:
252
276
  return self.fmap(f)
253
277
 
254
- def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Algebra[A, S]:
255
- return self.fmap(f).as_(Algebra[A, S]).imap(i)
256
-
257
- def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]])->Algebra[B, S]:
258
- def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
259
- parsed = self.run(input, use_cache)
260
- return f(parsed)
261
- return self.__class__(map_all_run, name=self.name) # type: ignore
278
+ def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Algebra[B, S]:
279
+ return self.fmap(f).pre_state(lambda s: s.map(i))
262
280
 
263
281
  def map_error(self, f: Callable[[Optional[Any]], Any]) -> Algebra[A, S]:
264
282
  def map_error_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
@@ -268,12 +286,6 @@ class Algebra(ABC, Generic[A, S]):
268
286
  return parsed
269
287
  return self.__class__(map_error_run, name=self.name)
270
288
 
271
- def map_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
272
- def map_state_run(state: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
273
- return self.run(f(state), use_cache)
274
- return self.__class__(map_state_run, name=self.name)
275
-
276
-
277
289
  def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Algebra[B, S]:
278
290
  def flat_map_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
279
291
  parsed = self.run(input, use_cache)
@@ -288,34 +300,34 @@ class Algebra(ABC, Generic[A, S]):
288
300
  def or_else_run(input: S, use_cache:bool) -> Either[Any, Tuple[Choice[A, B], S]]:
289
301
  match self.run(input, use_cache):
290
302
  case Right((value, state)):
291
- return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), state))
303
+ return Right((Choice(kind=ChoiceKind.LEFT, value=value), state))
292
304
  case Left(err):
293
305
  if isinstance(err, Error) and err.committed:
294
- return Left(err)
306
+ return Left(replace(err, committed=False))
295
307
  match other.run(input, use_cache):
296
308
  case Right((other_value, other_state)):
297
- return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=other_value), other_state))
309
+ return Right((Choice(kind=ChoiceKind.RIGHT, value=other_value), other_state))
298
310
  case Left(other_err):
299
311
  return Left(other_err)
300
312
  raise TypeError(f"Unexpected result type from {other}")
301
313
  raise TypeError(f"Unexpected result type from {self}")
302
314
  return self.__class__(or_else_run, name=f'{self.name} | {other.name}') # type: ignore
303
315
 
304
- def then_both(self, other: 'Algebra[B, S]') -> 'Algebra[Then[A, B], S]':
316
+ def then_both(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
305
317
  def then_both_f(a: A) -> Algebra[Then[A, B], S]:
306
318
  def combine(b: B) -> Then[A, B]:
307
319
  return Then(left=a, right=b, kind=ThenKind.BOTH)
308
320
  return other.fmap(combine)
309
321
  return self.flat_map(then_both_f).named(f'{self.name} + {other.name}')
310
322
 
311
- def then_left(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
323
+ def then_left(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
312
324
  def then_left_f(a: A) -> Algebra[Then[A, B], S]:
313
325
  def combine(b: B) -> Then[A, B]:
314
326
  return Then(left=a, right=b, kind=ThenKind.LEFT)
315
327
  return other.fmap(combine)
316
328
  return self.flat_map(then_left_f).named(f'{self.name} // {other.name}')
317
329
 
318
- def then_right(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
330
+ def then_right(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
319
331
  def then_right_f(a: A) -> Algebra[Then[A, B], S]:
320
332
  def combine(b: B) -> Then[A, B]:
321
333
  return Then(left=a, right=b, kind=ThenKind.RIGHT)
@@ -0,0 +1,378 @@
1
+
2
+
3
+ from __future__ import annotations
4
+ import re
5
+ from typing import (
6
+ Optional, Any, TypeVar, Tuple, runtime_checkable, Self,
7
+ Dict, Generic, Callable, Union, Protocol, Type
8
+ )
9
+
10
+
11
+ from dataclasses import dataclass
12
+ from enum import Enum
13
+ from syncraft.constraint import Bindable
14
+
15
+
16
+
17
+ A = TypeVar('A')
18
+ B = TypeVar('B')
19
+ C = TypeVar('C')
20
+ D = TypeVar('D')
21
+ S = TypeVar('S', bound=Bindable)
22
+
23
+ @dataclass(frozen=True)
24
+ class Biarrow(Generic[A, B]):
25
+ forward: Callable[[A], B]
26
+ inverse: Callable[[B], A]
27
+ def __rshift__(self, other: Biarrow[B, C]) -> Biarrow[A, C]:
28
+ def fwd(a: A) -> C:
29
+ b = self.forward(a)
30
+ return other.forward(b)
31
+ def inv(c: C) -> A:
32
+ b = other.inverse(c)
33
+ return self.inverse(b)
34
+ return Biarrow(
35
+ forward=fwd,
36
+ inverse=inv
37
+ )
38
+ @staticmethod
39
+ def identity()->Biarrow[A, A]:
40
+ return Biarrow(
41
+ forward=lambda x: x,
42
+ inverse=lambda y: y
43
+ )
44
+
45
+ @staticmethod
46
+ def when(condition: Callable[..., bool],
47
+ then: Biarrow[A, B],
48
+ otherwise: Optional[Biarrow[A, B]] = None) -> Callable[..., Biarrow[A, B]]:
49
+ def _when(*args:Any, **kwargs:Any) -> Biarrow[A, B]:
50
+ return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
51
+ return _when
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class Lens(Generic[C, A]):
56
+ get: Callable[[C], A]
57
+ set: Callable[[C, A], C]
58
+
59
+ def modify(self, source: C, f: Callable[[A], A]) -> C:
60
+ return self.set(source, f(self.get(source)))
61
+
62
+ def bimap(self, ff: Callable[[A], B], bf: Callable[[B], A]) -> Lens[C, B]:
63
+ def getf(data: C) -> B:
64
+ return ff(self.get(data))
65
+
66
+ def setf(data: C, value: B) -> C:
67
+ return self.set(data, bf(value))
68
+
69
+ return Lens(get=getf, set=setf)
70
+
71
+ def __truediv__(self, other: Lens[A, B]) -> Lens[C, B]:
72
+ def get_composed(obj: C) -> B:
73
+ return other.get(self.get(obj))
74
+ def set_composed(obj: C, value: B) -> C:
75
+ return self.set(obj, other.set(self.get(obj), value))
76
+ return Lens(get=get_composed, set=set_composed)
77
+
78
+ def __rtruediv__(self, other: Lens[B, C])->Lens[B, A]:
79
+ return other.__truediv__(self)
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class Reducer(Generic[A, S]):
84
+ run_f: Callable[[A, S], S]
85
+ def __call__(self, a: A, s: S) -> S:
86
+ return self.run_f(a, s)
87
+
88
+ def map(self, f: Callable[[B], A]) -> Reducer[B, S]:
89
+ def map_run(b: B, s: S) -> S:
90
+ return self(f(b), s)
91
+ return Reducer(map_run)
92
+
93
+ def __rshift__(self, other: Reducer[A, S]) -> Reducer[A, S]:
94
+ return Reducer(lambda a, s: other(a, self(a, s)))
95
+
96
+ @dataclass(frozen=True)
97
+ class Bimap(Generic[A, B]):
98
+ run_f: Callable[[A], Tuple[B, Callable[[B], A]]]
99
+ def __call__(self, a: A) -> Tuple[B, Callable[[B], A]]:
100
+ return self.run_f(a)
101
+ def __rshift__(self, other: Bimap[B, C] | Biarrow[B, C]) -> Bimap[A, C]:
102
+ if isinstance(other, Biarrow):
103
+ def biarrow_then_run(a: A) -> Tuple[C, Callable[[C], A]]:
104
+ b, inv1 = self(a)
105
+ c = other.forward(b)
106
+ def inv(c2: C) -> A:
107
+ b2 = other.inverse(c2)
108
+ return inv1(b2)
109
+ return c, inv
110
+ return Bimap(biarrow_then_run)
111
+ elif isinstance(other, Bimap):
112
+ def bimap_then_run(a: A) -> Tuple[C, Callable[[C], A]]:
113
+ b, inv1 = self(a)
114
+ c, inv2 = other(b)
115
+ def inv(c2: C) -> A:
116
+ return inv1(inv2(c2))
117
+ return c, inv
118
+ return Bimap(bimap_then_run)
119
+ else:
120
+ raise TypeError(f"Unsupported type for Bimap >>: {type(other)}")
121
+ def __rrshift__(self, other: Bimap[C, A] | Biarrow[C, A]) -> Bimap[C, B]:
122
+ if isinstance(other, Biarrow):
123
+ def biarrow_then_run(c: C) -> Tuple[B, Callable[[B], C]]:
124
+ a = other.forward(c)
125
+ b2, inv1 = self(a)
126
+ def inv(a2: B) -> C:
127
+ a3 = inv1(a2)
128
+ return other.inverse(a3)
129
+ return b2, inv
130
+ return Bimap(biarrow_then_run)
131
+ elif isinstance(other, Bimap):
132
+ def bimap_then_run(c: C)->Tuple[B, Callable[[B], C]]:
133
+ a, a2c = other(c)
134
+ b2, b2a = self(a)
135
+ def inv(b3: B) -> C:
136
+ a2 = b2a(b3)
137
+ return a2c(a2)
138
+ return b2, inv
139
+ return Bimap(bimap_then_run)
140
+ else:
141
+ raise TypeError(f"Unsupported type for Bimap <<: {type(other)}")
142
+
143
+
144
+ @staticmethod
145
+ def const(a: B)->Bimap[B, B]:
146
+ return Bimap(lambda _: (a, lambda b: b))
147
+
148
+ @staticmethod
149
+ def identity()->Bimap[A, A]:
150
+ return Bimap(lambda a: (a, lambda b: b))
151
+
152
+ @staticmethod
153
+ def when(cond: Callable[[A], bool],
154
+ then: Bimap[A, B],
155
+ otherwise: Optional[Bimap[A, C]] = None) -> Bimap[A, A | B | C]:
156
+ def when_run(a:A) -> Tuple[A | B | C, Callable[[A | B | C], A]]:
157
+ bimap = then if cond(a) else (otherwise if otherwise is not None else Bimap.identity())
158
+ abc, inv = bimap(a)
159
+ def inv_f(b: Any) -> A:
160
+ return inv(b)
161
+ return abc, inv_f
162
+ return Bimap(when_run)
163
+
164
+
165
+
166
+
167
+
168
+ @dataclass(frozen=True)
169
+ class AST:
170
+ def walk(self, r: Reducer[Any, S], s: S) -> S:
171
+ return s
172
+
173
+ @dataclass(frozen=True)
174
+ class Nothing(AST):
175
+ def __str__(self)->str:
176
+ return self.__class__.__name__
177
+ def __repr__(self)->str:
178
+ return self.__str__()
179
+
180
+
181
+ @dataclass(frozen=True)
182
+ class Marked(Generic[A], AST):
183
+ name: str
184
+ value: A
185
+ def walk(self, r: Reducer[A, S], s: S) -> S:
186
+ return self.value.walk(r, s) if isinstance(self.value, AST) else r(self.value, s)
187
+
188
+
189
+ class DataclassProtocol(Protocol):
190
+ __dataclass_fields__: dict
191
+
192
+ E = TypeVar("E")
193
+ @dataclass(frozen=True)
194
+ class Collect(Generic[A, E], AST):
195
+ collector: Type[E]
196
+ value: A
197
+
198
+ class ChoiceKind(Enum):
199
+ LEFT = 'left'
200
+ RIGHT = 'right'
201
+
202
+ @dataclass(frozen=True)
203
+ class Choice(Generic[A, B], AST):
204
+ kind: Optional[ChoiceKind]
205
+ value: Optional[A | B] = None
206
+ def walk(self, r: Reducer[A | B, S], s: S) -> S:
207
+ if self.value is not None:
208
+ if isinstance(self.value, AST):
209
+ return self.value.walk(r, s)
210
+ else:
211
+ return r(self.value, s)
212
+ return s
213
+
214
+ @dataclass(frozen=True)
215
+ class Many(Generic[A], AST):
216
+ value: Tuple[A, ...]
217
+ def walk(self, r: Reducer[A, S], s: S) -> S:
218
+ for item in self.value:
219
+ if isinstance(item, AST):
220
+ s = item.walk(r, s)
221
+ else:
222
+ s = r(item, s)
223
+ return s
224
+
225
+ class ThenKind(Enum):
226
+ BOTH = '+'
227
+ LEFT = '//'
228
+ RIGHT = '>>'
229
+
230
+ FlatThen = Tuple[Any, ...]
231
+ MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
232
+
233
+ @dataclass(eq=True, frozen=True)
234
+ class Then(Generic[A, B], AST):
235
+ kind: ThenKind
236
+ left: A
237
+ right: B
238
+ def walk(self, r: Reducer[A | B, S], s: S) -> S:
239
+ if isinstance(self.left, AST):
240
+ s = self.left.walk(r, s)
241
+ else:
242
+ s = r(self.left, s)
243
+ if isinstance(self.right, AST):
244
+ s = self.right.walk(r, s)
245
+ else:
246
+ s = r(self.right, s)
247
+ return s
248
+
249
+ @dataclass(frozen=True)
250
+ class Token(AST):
251
+ token_type: Enum
252
+ text: str
253
+ def __str__(self) -> str:
254
+ return f"{self.token_type.name}({self.text})"
255
+
256
+ def __repr__(self) -> str:
257
+ return self.__str__()
258
+
259
+ def walk(self, r: Reducer['Token', S], s: S) -> S:
260
+ return r(self, s)
261
+
262
+
263
+ @runtime_checkable
264
+ class TokenProtocol(Protocol):
265
+ @property
266
+ def token_type(self) -> Enum: ...
267
+ @property
268
+ def text(self) -> str: ...
269
+
270
+ T = TypeVar('T', bound=TokenProtocol)
271
+
272
+
273
+ @dataclass(frozen=True)
274
+ class TokenSpec:
275
+ token_type: Optional[Enum] = None
276
+ text: Optional[str] = None
277
+ case_sensitive: bool = False
278
+ regex: Optional[re.Pattern[str]] = None
279
+
280
+ def is_valid(self, token: TokenProtocol) -> bool:
281
+ type_match = self.token_type is None or token.token_type == self.token_type
282
+ value_match = self.text is None or (token.text.strip() == self.text.strip() if self.case_sensitive else
283
+ token.text.strip().upper() == self.text.strip().upper())
284
+ value_match = value_match or (self.regex is not None and self.regex.fullmatch(token.text) is not None)
285
+ return type_match and value_match
286
+
287
+
288
+ ParseResult = Union[
289
+ Then['ParseResult[T]', 'ParseResult[T]'],
290
+ Marked['ParseResult[T]'],
291
+ Choice['ParseResult[T]', 'ParseResult[T]'],
292
+ Many['ParseResult[T]'],
293
+ Nothing,
294
+ T,
295
+ ]
296
+
297
+
298
+
299
+ """
300
+ @staticmethod
301
+ def collect_marked(a: FlatThen, f: Optional[Callable[..., Any]] = None)->Tuple[MarkedThen, Callable[[MarkedThen], FlatThen]]:
302
+ index: List[str | int] = []
303
+ named_count = 0
304
+ for i, v in enumerate(a):
305
+ if isinstance(v, Marked):
306
+ index.append(v.name)
307
+ named_count += 1
308
+ else:
309
+ index.append(i - named_count)
310
+ named = {v.name: v.value for v in a if isinstance(v, Marked)}
311
+ unnamed = [v for v in a if not isinstance(v, Marked)]
312
+ if f is None:
313
+ ret = (named, tuple(unnamed))
314
+ else:
315
+ ret = (f(**named), tuple(unnamed))
316
+ def invf(b: MarkedThen) -> Tuple[Any, ...]:
317
+ named_value, unnamed_value = b
318
+ assert isinstance(named_value, dict) or is_dataclass(named_value), f"Expected dict or dataclass for named values, got {type(named_value)}"
319
+ if is_dataclass(named_value):
320
+ named_dict = named | asdict(cast(Any, named_value))
321
+ else:
322
+ named_dict = named | named_value
323
+ ret = []
324
+ for x in index:
325
+ if isinstance(x, str):
326
+ assert x in named_dict, f"Missing named value: {x}"
327
+ ret.append(named_dict[x])
328
+ else:
329
+ assert 0 <= x < len(unnamed_value), f"Missing unnamed value at index: {x}"
330
+ ret.append(unnamed_value[x])
331
+ return tuple(ret)
332
+ return ret, invf
333
+
334
+ def bimap(self, f: Bimap[Any, Any]=Bimap.identity()) -> Tuple[FlatThen, Callable[[FlatThen], Then[A, B]]]:
335
+ match self.kind:
336
+ case ThenKind.LEFT:
337
+ lb, linv = self.left.bimap(f) if isinstance(self.left, AST) else f(self.left)
338
+ return lb, lambda b: replace(self, left=linv(b))
339
+ case ThenKind.RIGHT:
340
+ rb, rinv = self.right.bimap(f) if isinstance(self.right, AST) else f(self.right)
341
+ return rb, lambda b: replace(self, right=rinv(b))
342
+ case ThenKind.BOTH:
343
+ lb, linv = self.left.bimap(f) if isinstance(self.left, AST) else f(self.left)
344
+ rb, rinv = self.right.bimap(f) if isinstance(self.right, AST) else f(self.right)
345
+ left_v = (lb,) if not isinstance(self.left, Then) else lb
346
+ right_v = (rb,) if not isinstance(self.right, Then) else rb
347
+ def invf(b: Tuple[Any, ...]) -> Then[A, B]:
348
+ left_size = self.left.arity() if isinstance(self.left, Then) else 1
349
+ right_size = self.right.arity() if isinstance(self.right, Then) else 1
350
+ lraw = b[:left_size]
351
+ rraw = b[left_size:left_size + right_size]
352
+ lraw = lraw[0] if left_size == 1 else lraw
353
+ rraw = rraw[0] if right_size == 1 else rraw
354
+ la = linv(lraw)
355
+ ra = rinv(rraw)
356
+ return replace(self, left=la, right=ra)
357
+ return left_v + right_v, invf
358
+
359
+ def bimap_collected(self, f: Bimap[Any, Any]=Bimap.identity()) -> Tuple[MarkedThen, Callable[[MarkedThen], Then[A, B]]]:
360
+ data, invf = self.bimap(f)
361
+ data, func = Then.collect_marked(data)
362
+ return data, lambda d: invf(func(d))
363
+
364
+
365
+ def arity(self)->int:
366
+ if self.kind == ThenKind.LEFT:
367
+ return self.left.arity() if isinstance(self.left, Then) else 1
368
+ elif self.kind == ThenKind.RIGHT:
369
+ return self.right.arity() if isinstance(self.right, Then) else 1
370
+ elif self.kind == ThenKind.BOTH:
371
+ left_arity = self.left.arity() if isinstance(self.left, Then) else 1
372
+ right_arity = self.right.arity() if isinstance(self.right, Then) else 1
373
+ return left_arity + right_arity
374
+ else:
375
+ return 1
376
+
377
+
378
+ """
@@ -1,18 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import (
4
- Any, Tuple, Optional, Generator as YieldGen
4
+ Any, Tuple, Generator as YieldGen
5
5
  )
6
- from dataclasses import dataclass, replace
6
+ from dataclasses import dataclass
7
7
  from syncraft.algebra import (
8
8
  Algebra, Either, Right,
9
9
  )
10
10
  from syncraft.ast import T, ParseResult, Choice, Many, Then, Marked
11
11
 
12
12
  from syncraft.generator import GenState, Generator
13
- from sqlglot import TokenType
13
+
14
14
  from syncraft.syntax import Syntax
15
- import re
15
+
16
16
 
17
17
 
18
18
  @dataclass(frozen=True)
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import (
4
- Any, TypeVar, Tuple, Optional, Callable, Generic, cast,
4
+ Any, TypeVar, Tuple, Optional, Callable, Generic,
5
5
  List,
6
6
  )
7
7
  from functools import cached_property
@@ -11,8 +11,8 @@ from syncraft.algebra import (
11
11
  )
12
12
 
13
13
  from syncraft.ast import (
14
- T, ParseResult, AST, Token, TokenSpec, Binding, Variable,
15
- Bindable,
14
+ T, ParseResult, AST, Token, TokenSpec,
15
+ Bindable, Nothing,
16
16
  Choice, Many, ChoiceKind,
17
17
  Then, ThenKind, Marked
18
18
  )
@@ -23,7 +23,7 @@ import re
23
23
  import rstr
24
24
  from functools import lru_cache
25
25
  import random
26
-
26
+ from rich import print
27
27
  B = TypeVar('B')
28
28
 
29
29
 
@@ -136,7 +136,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
136
136
  def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
137
137
  def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
138
138
  try:
139
- if not isinstance(input.ast, Then):
139
+ if not isinstance(input.ast, Then) or isinstance(input.ast, Nothing):
140
140
  return Left(Error(this=self,
141
141
  message=f"Expect Then got {input.ast}",
142
142
  state=input))
@@ -179,7 +179,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
179
179
  pass
180
180
  return Right((Many(value=tuple(ret)), input))
181
181
  else:
182
- if not isinstance(input.ast, Many):
182
+ if not isinstance(input.ast, Many) or isinstance(input.ast, Nothing):
183
183
  return Left(Error(this=self,
184
184
  message=f"Expect Many got {input.ast}",
185
185
  state=input))
@@ -194,7 +194,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
194
194
  this=self,
195
195
  state=input.inject(x)
196
196
  ))
197
- case Left(_):
197
+ case Left(e):
198
198
  pass
199
199
  if len(ret) < at_least:
200
200
  return Left(Error(
@@ -210,22 +210,34 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
210
210
  other: Algebra[ParseResult[T], GenState[T]]
211
211
  ) -> Algebra[Choice[ParseResult[T], ParseResult[T]], GenState[T]]:
212
212
  def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Choice[ParseResult[T], ParseResult[T]], GenState[T]]]:
213
- def exec(kind: ChoiceKind,
213
+ def exec(kind: ChoiceKind | None,
214
214
  left: GenState[T],
215
215
  right: GenState[T])->Either[Any, Tuple[Choice[ParseResult[T], ParseResult[T]], GenState[T]]]:
216
216
  match kind:
217
217
  case ChoiceKind.LEFT:
218
218
  match self.run(left, use_cache):
219
219
  case Right((value, next_input)):
220
- return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), next_input))
220
+ return Right((Choice(kind=ChoiceKind.LEFT, value=value), next_input))
221
221
  case Left(error):
222
222
  return Left(error)
223
223
  case ChoiceKind.RIGHT:
224
224
  match other.run(right, use_cache):
225
225
  case Right((value, next_input)):
226
- return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=value), next_input))
226
+ return Right((Choice(kind=ChoiceKind.RIGHT, value=value), next_input))
227
227
  case Left(error):
228
228
  return Left(error)
229
+ case None:
230
+ match self.run(left, use_cache):
231
+ case Right((value, next_input)):
232
+ return Right((Choice(kind=ChoiceKind.LEFT, value=value), next_input))
233
+ case Left(error):
234
+ if isinstance(error, Error) and error.committed:
235
+ return Left(replace(error, committed=False))
236
+ match other.run(left, use_cache):
237
+ case Right((value, next_input)):
238
+ return Right((Choice(kind=ChoiceKind.RIGHT, value=value), next_input))
239
+ case Left(error):
240
+ return Left(error)
229
241
  raise ValueError(f"Invalid ChoiceKind: {kind}")
230
242
 
231
243
  if input.pruned:
@@ -233,14 +245,14 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
233
245
  which = forked_input.rng("or_else").choice((ChoiceKind.LEFT, ChoiceKind.RIGHT))
234
246
  return exec(which, forked_input, forked_input)
235
247
  else:
236
- if isinstance(input.ast, Choice):
237
- return exec(input.ast.kind,
238
- input.inject(input.ast.left),
239
- input.inject(input.ast.right))
240
- else:
248
+ if not isinstance(input.ast, Choice) or isinstance(input.ast, Nothing):
241
249
  return Left(Error(this=self,
242
250
  message=f"Expect Choice got {input.ast}",
243
251
  state=input))
252
+ else:
253
+ return exec(input.ast.kind,
254
+ input.inject(input.ast.value),
255
+ input.inject(input.ast.value))
244
256
  return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})") # type: ignore
245
257
 
246
258
  @classmethod
@@ -259,7 +271,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
259
271
  current = input.ast
260
272
  if not isinstance(current, Token) or not gen.is_valid(current):
261
273
  return Left(Error(None,
262
- message=f"Expected a Token, but got {type(current)}.",
274
+ message=f"Expected a Token({gen.text}), but got {current}.",
263
275
  state=input))
264
276
  return Right((current, input))
265
277
  lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
@@ -1,27 +1,29 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import (
4
- Optional, List, Any, TypeVar, Generic, Callable, Tuple, cast,
4
+ Optional, Any, TypeVar, Generic, Callable, Tuple, cast,
5
5
  Type, Literal
6
6
  )
7
7
  from dataclasses import dataclass, field, replace
8
8
  from functools import reduce
9
9
  from syncraft.algebra import Algebra, Error, Either, Right
10
10
  from syncraft.constraint import Variable, Bindable
11
- from syncraft.ast import Then, ThenKind, Marked
11
+ from syncraft.ast import Then, ThenKind, Marked, Choice, Many, ChoiceKind, Nothing, Collect, E
12
12
  from types import MethodType, FunctionType
13
13
 
14
-
14
+ from rich import print
15
15
 
16
16
 
17
17
  A = TypeVar('A') # Result type
18
18
  B = TypeVar('B') # Result type for mapping
19
19
  C = TypeVar('C') # Result type for else branch
20
+ D = TypeVar('D') # Result type for else branch
20
21
  S = TypeVar('S', bound=Bindable) # State type
21
22
 
22
23
 
23
24
 
24
25
 
26
+
25
27
  @dataclass(frozen=True)
26
28
  class Description:
27
29
  name: Optional[str] = None
@@ -134,49 +136,50 @@ class Syntax(Generic[A, S]):
134
136
  ######################################################## value transformation ########################################################
135
137
  def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
136
138
  return self.__class__(lambda cls: self.alg(cls).map(f), meta = self.meta) # type: ignore
137
-
138
- def imap(self, f: Callable[[B], A]) -> Syntax[A, S]:
139
- return self.__class__(lambda cls: self.alg(cls).imap(f), meta=self.meta)
140
-
141
- def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Syntax[A, S]:
142
- return self.__class__(lambda cls: self.alg(cls).bimap(f, i), meta=self.meta)
139
+
140
+ def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Syntax[B, S]:
141
+ return self.__class__(lambda cls: self.alg(cls).bimap(f, i), meta=self.meta) # type: ignore
143
142
 
144
- def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]]) -> Syntax[B, S]:
143
+ def map_all(self, f: Callable[[A, S], Tuple[B, S]]) -> Syntax[B, S]:
145
144
  return self.__class__(lambda cls: self.alg(cls).map_all(f), meta=self.meta) # type: ignore
146
-
145
+
147
146
  def map_error(self, f: Callable[[Optional[Any]], Any]) -> Syntax[A, S]:
148
147
  return self.__class__(lambda cls: self.alg(cls).map_error(f), meta=self.meta)
149
148
 
150
- def map_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
151
- return self.__class__(lambda cls: self.alg(cls).map_state(f), meta=self.meta)
149
+ def pre_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
150
+ return self.__class__(lambda cls: self.alg(cls).pre_state(f), meta=self.meta)
152
151
 
152
+ def post_state(self, f: Callable[[S], S]) -> Syntax[A, S]:
153
+ return self.__class__(lambda cls: self.alg(cls).post_state(f), meta=self.meta)
153
154
 
154
155
  def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
155
156
  return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
156
157
 
157
- def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[Tuple[A, ...], S]:
158
+ def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[Many[A], S]:
158
159
  return self.__class__(lambda cls:self.alg(cls).many(at_least=at_least, at_most=at_most)).describe(name='*', # type: ignore
159
160
  fixity='prefix',
160
161
  parameter=(self,))
161
162
 
162
163
  ################################################ facility combinators ############################################################
163
164
 
164
-
165
-
166
- def between(self, left: Syntax[Any, S], right: Syntax[Any, S]) -> Syntax[Then[Any, Then[A, Any]], S]:
165
+ def between(self, left: Syntax[B, S], right: Syntax[C, S]) -> Syntax[Then[B, Then[A, C]], S]:
167
166
  return left >> self // right
168
167
 
169
- def sep_by(self, sep: Syntax[Any, S]) -> Syntax[Tuple[A, ...], S]:
170
- return (self + (sep >> self).many().optional()).describe( # type: ignore
168
+ def sep_by(self, sep: Syntax[B, S]) -> Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S]:
169
+ ret: Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S] = (self + (sep >> self).many().optional())
170
+ return ret.describe(
171
171
  name='sep_by',
172
172
  fixity='prefix',
173
173
  parameter=(self, sep))
174
-
175
- def parens(self, sep: Syntax[Any, S], open: Syntax[Any, S], close: Syntax[Any, S]) -> Syntax[Any, S]:
174
+
175
+ def parens(self,
176
+ sep: Syntax[C, S],
177
+ open: Syntax[B, S],
178
+ close: Syntax[D, S]) -> Syntax[Then[B, Then[Then[A, Choice[Many[Then[C, A]], Optional[Nothing]]], D]], S]:
176
179
  return self.sep_by(sep=sep).between(left=open, right=close)
177
180
 
178
- def optional(self, default: Optional[B] = None) -> Syntax[Optional[A | B], S]:
179
- return (self | success(default)).describe(name='~', fixity='prefix', parameter=(self,))
181
+ def optional(self) -> Syntax[Choice[A, Optional[Nothing]], S]:
182
+ return (self | success(Nothing())).describe(name='~', fixity='prefix', parameter=(self,))
180
183
 
181
184
 
182
185
  def cut(self) -> Syntax[A, S]:
@@ -184,63 +187,80 @@ class Syntax(Generic[A, S]):
184
187
 
185
188
 
186
189
  ####################################################### operator overloading #############################################
187
- def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A, None], S]:
190
+ def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
188
191
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
189
- return self.__class__(
190
- lambda cls: self.alg(cls).then_left(other.alg(cls)) # type: ignore
191
- ).describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, None], S])
192
+ ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_left(other.alg(cls))) # type: ignore
193
+ return ret.describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
194
+
195
+ def __lshift__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
196
+ return self.__floordiv__(other)
192
197
 
193
- def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, None], S]:
198
+ def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
194
199
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
195
200
  return other.__floordiv__(self)
201
+
202
+ def __rlshift__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
203
+ return self.__rfloordiv__(other)
196
204
 
197
- def __invert__(self) -> Syntax[A | None, S]:
198
- return self.optional()
205
+ def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
206
+ other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
207
+ ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_both(other.alg(cls))) # type: ignore
208
+ return ret.describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
199
209
 
200
210
  def __radd__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
201
211
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
202
212
  return other.__add__(self)
203
213
 
204
- def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
205
- other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
206
- return self.__class__(
207
- lambda cls: self.alg(cls).then_both(other.alg(cls)) # type: ignore
208
- ).describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other))
209
-
210
- def __rshift__(self, other: Syntax[B, S]) -> Syntax[Then[None, B], S]:
214
+ def __rshift__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
211
215
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
212
- return self.__class__(
213
- lambda cls: self.alg(cls).then_right(other.alg(cls)) # type: ignore
214
- ).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[None, B], S])
215
-
216
+ ret: Syntax[Then[A, B], S] = self.__class__(lambda cls: self.alg(cls).then_right(other.alg(cls))) # type: ignore
217
+ return ret.describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, B], S])
216
218
 
217
- def __rrshift__(self, other: Syntax[B, S]) -> Syntax[Then[None, A], S]:
219
+ def __rrshift__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
218
220
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
219
221
  return other.__rshift__(self)
220
-
221
-
222
- def __or__(self, other: Syntax[B, S]) -> Syntax[A | B, S]:
222
+
223
+ def __or__(self, other: Syntax[B, S]) -> Syntax[Choice[A, B], S]:
223
224
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
224
- return self.__class__(lambda cls: self.alg(cls).or_else(other.alg(cls))).describe(name='|', fixity='infix', parameter=[self, other]) # type: ignore
225
-
225
+ ret: Syntax[Choice[A, B], S] = self.__class__(lambda cls: self.alg(cls).or_else(other.alg(cls))) # type: ignore
226
+ return ret.describe(name='|', fixity='infix', parameter=(self, other))
226
227
 
227
- def __ror__(self, other: Syntax[B, S]) -> Syntax[A | B, S]:
228
+ def __ror__(self, other: Syntax[B, S]) -> Syntax[Choice[B, A], S]:
228
229
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
229
- return other.__or__(self).as_(Syntax[A | B, S])
230
+ return other.__or__(self)
231
+
232
+ def __invert__(self) -> Syntax[Choice[A, Optional[Nothing]], S]:
233
+ return self.optional()
230
234
 
231
235
 
232
236
  ######################################################################## data processing combinators #########################################################
233
- def bind(self, var: Variable) -> Syntax[A, S]:
234
- def bind_v(result: Either[Any, Tuple[A, S]])->Either[Any, Tuple[A, S]]:
235
- if isinstance(result, Right):
236
- value, state = result.value
237
- return Right((value, state.bind(var, value)))
238
- return result
239
-
240
- ret = self.mark(var.name).map_all(bind_v) if var.name else self.map_all(bind_v) # type: ignore
237
+ def bind(self,
238
+ var: Variable,
239
+ collector: Optional[Type[E]]=None) -> Syntax[A |
240
+ Marked[A] |
241
+ Marked[Collect[A, E]] |
242
+ Collect[A, E], S]:
243
+ def bind_v(v: A | Marked[A] | Marked[Collect[A, E]] | Collect[A, E],
244
+ s: S)->Tuple[A | Marked[A] | Marked[Collect[A, E]] | Collect[A, E], S]:
245
+ return v, s.bind(var, v)
246
+ if callable(collector):
247
+ ret = self.to(collector).mark(var.name).map_all(bind_v) if var.name else self.to(collector).map_all(bind_v)
248
+ else:
249
+ ret = self.mark(var.name).map_all(bind_v) if var.name else self.map_all(bind_v)
241
250
  return ret.describe(name=f'bind({var.name})', fixity='postfix', parameter=(self,))
242
251
 
243
- def mark(self, var: str) -> Syntax[A, S]:
252
+ def to(self, f: Type[E])-> Syntax[Collect[A, E], S]:
253
+ def to_f(v: A) -> Collect[A, E]:
254
+ if isinstance(v, Collect):
255
+ return replace(v, collector=f)
256
+ else:
257
+ return Collect(collector=f, value=v)
258
+ def ito_f(c: Collect[A, E]) -> A:
259
+ return c.value if isinstance(c, Collect) else c
260
+ return self.bimap(to_f, ito_f).describe(name=f'to({f})', fixity='postfix', parameter=(self,))
261
+
262
+
263
+ def mark(self, var: str) -> Syntax[Marked[A], S]:
244
264
  def bind_s(value: A) -> Marked[A]:
245
265
  if isinstance(value, Marked):
246
266
  return replace(value, name=var)
@@ -278,17 +298,17 @@ def success(value: Any) -> Syntax[Any, Any]:
278
298
  return Syntax(lambda alg: alg.success(value)).describe(name=f'success({value})', fixity='prefix')
279
299
 
280
300
  def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
281
- return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(None)
301
+ return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(Nothing())
282
302
 
283
303
 
284
304
  def all(*parsers: Syntax[Any, S]) -> Syntax[Then[Any, Any], S]:
285
- return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(None)
305
+ return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(Nothing())
286
306
 
287
307
  def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
288
- return reduce(lambda a, b: a // b, parsers) if len(parsers) > 0 else success(None)
308
+ return reduce(lambda a, b: a // b, parsers) if len(parsers) > 0 else success(Nothing())
289
309
 
290
310
  def last(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
291
- return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(None)
311
+ return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(Nothing())
292
312
 
293
313
  def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Syntax[Any, S]:
294
314
  def is_named_parser(x: Any) -> bool:
@@ -325,5 +345,5 @@ def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Sy
325
345
  else:
326
346
  ret = ret >> just_parser
327
347
 
328
- return ret if ret is not None else success(None)
348
+ return ret if ret is not None else success(Nothing())
329
349
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.26
3
+ Version: 0.1.28
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -19,8 +19,6 @@ Syncraft is a parser/generator combinator library with full round-trip support:
19
19
 
20
20
  - Parse source code into AST or dataclasses
21
21
  - Generate source code from dataclasses
22
- - Bidirectional transformations via lenses
23
- - Convenience combinators: `all`, `first`, `last`, `named`
24
22
  - SQLite syntax support included
25
23
 
26
24
  ## Installation
@@ -33,5 +31,7 @@ pip install syncraft
33
31
  ## TODO
34
32
  - [ ] simplify the result of then_left and then_right by bimap the result in syntax.
35
33
  - [ ] simplify the result of sep_by and between by bimap the result in syntax
34
+ - [ ] convert to dict/dataclass via bimap in syntax
35
+ - [ ] define DSL over Variable to construct predicates
36
36
  - [ ] Try the parsing, generation, and data processing machinery on SQLite3 syntax. So that I can have direct feedback on the usability of this library and a fully functional SQLite3 library.
37
37
  - [ ] Make the library as fast as possible and feasible.
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from syncraft.ast import Then, ThenKind, Many
3
+ from syncraft.ast import Then, ThenKind, Many, Choice, ChoiceKind, Token
4
4
  from syncraft.algebra import Error
5
5
  from syncraft.parser import literal, parse
6
6
  import syncraft.generator as gen
@@ -248,7 +248,7 @@ def test_ambiguous() -> None:
248
248
  sql = "a"
249
249
  ast = parse(syntax, sql, dialect="sqlite")
250
250
  # Does it prefer A (shorter) or B (fails)? Depends on design.
251
- # assert ast == Choice(TokenGen.from_string("a"))
251
+ assert ast == Choice[Token, Token](value=TokenGen.from_string("a"), kind=ChoiceKind.LEFT)
252
252
 
253
253
 
254
254
  def test_combo() -> None:
@@ -258,8 +258,11 @@ def test_combo() -> None:
258
258
  syntax = ((A + B).many() | C) + B
259
259
  sql = "a b a b c b"
260
260
  # Should fail, as we discussed earlier
261
+ # the working syntax should be ((A + B) | C).many() + B
261
262
  ast = parse(syntax, sql, dialect="sqlite")
262
263
  assert isinstance(ast, Error)
264
+ ast = parse(((A + B) | C).many() + B, sql, dialect="sqlite")
265
+ assert not isinstance(ast, Error)
263
266
 
264
267
 
265
268
  def test_optional():
@@ -1,187 +0,0 @@
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
-
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
- @dataclass(frozen=True)
103
- class AST:
104
- pass
105
-
106
- class ChoiceKind(Enum):
107
- LEFT = 'left'
108
- RIGHT = 'right'
109
- @dataclass(frozen=True)
110
- class Choice(Generic[A, B], AST):
111
- kind: ChoiceKind
112
- left: Optional[A]
113
- right: Optional[B]
114
-
115
-
116
- @dataclass(frozen=True)
117
- class Many(Generic[A], AST):
118
- value: Tuple[A, ...]
119
-
120
- @dataclass(frozen=True)
121
- class Marked(Generic[A], AST):
122
- name: str
123
- value: A
124
- class ThenKind(Enum):
125
- BOTH = '+'
126
- LEFT = '//'
127
- RIGHT = '>>'
128
-
129
- FlatThen = Tuple[Any, ...]
130
- MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
131
-
132
- @dataclass(eq=True, frozen=True)
133
- class Then(Generic[A, B], AST):
134
- kind: ThenKind
135
- left: Optional[A]
136
- right: Optional[B]
137
-
138
- @runtime_checkable
139
- class TokenProtocol(Protocol):
140
- @property
141
- def token_type(self) -> Enum: ...
142
- @property
143
- def text(self) -> str: ...
144
-
145
-
146
- @dataclass(frozen=True)
147
- class Token:
148
- token_type: Enum
149
- text: str
150
- def __str__(self) -> str:
151
- return f"{self.token_type.name}({self.text})"
152
-
153
- def __repr__(self) -> str:
154
- return self.__str__()
155
-
156
-
157
-
158
- @dataclass(frozen=True)
159
- class TokenSpec:
160
- token_type: Optional[Enum] = None
161
- text: Optional[str] = None
162
- case_sensitive: bool = False
163
- regex: Optional[re.Pattern[str]] = None
164
-
165
- def is_valid(self, token: TokenProtocol) -> bool:
166
- type_match = self.token_type is None or token.token_type == self.token_type
167
- value_match = self.text is None or (token.text.strip() == self.text.strip() if self.case_sensitive else
168
- token.text.strip().upper() == self.text.strip().upper())
169
- value_match = value_match or (self.regex is not None and self.regex.fullmatch(token.text) is not None)
170
- return type_match and value_match
171
-
172
-
173
-
174
-
175
- T = TypeVar('T', bound=TokenProtocol)
176
-
177
-
178
- ParseResult = Union[
179
- Then['ParseResult[T]', 'ParseResult[T]'],
180
- Marked['ParseResult[T]'],
181
- Choice['ParseResult[T]', 'ParseResult[T]'],
182
- Many['ParseResult[T]'],
183
- T,
184
- ]
185
-
186
-
187
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes