syncraft 0.1.27__py3-none-any.whl → 0.1.29__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.

Potentially problematic release.


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

syncraft/algebra.py CHANGED
@@ -8,7 +8,10 @@ import traceback
8
8
  from dataclasses import dataclass, replace, asdict
9
9
  from weakref import WeakKeyDictionary
10
10
  from abc import ABC
11
- from syncraft.ast import ThenKind, Then, S, Choice, Many, ChoiceKind
11
+ from syncraft.ast import ThenKind, Then, Choice, Many, ChoiceKind
12
+ from syncraft.constraint import Bindable
13
+
14
+ S = TypeVar('S', bound=Bindable)
12
15
 
13
16
  A = TypeVar('A') # Result type
14
17
  B = TypeVar('B') # Mapped result type
@@ -128,10 +131,7 @@ class Algebra(ABC, Generic[A, S]):
128
131
  def lazy_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
129
132
  return thunk().run(input, use_cache)
130
133
  return cls(lazy_run, name=cls.__name__ + '.lazy')
131
-
132
-
133
-
134
-
134
+
135
135
  @classmethod
136
136
  def fail(cls, error: Any) -> Algebra[Any, S]:
137
137
  def fail_run(input: S, use_cache:bool) -> Either[Any, Tuple[Any, S]]:
@@ -141,6 +141,7 @@ class Algebra(ABC, Generic[A, S]):
141
141
  state=input
142
142
  ))
143
143
  return cls(fail_run, name=cls.__name__ + '.fail')
144
+
144
145
  @classmethod
145
146
  def success(cls, value: Any) -> Algebra[Any, S]:
146
147
  def success_run(input: S, use_cache:bool) -> Either[Any, Tuple[Any, S]]:
@@ -234,7 +235,35 @@ class Algebra(ABC, Generic[A, S]):
234
235
  lazy_self = self.__class__(debug_run, name=label)
235
236
  return lazy_self
236
237
 
238
+ ######################################################## map on state ###########################################
239
+ def post_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
240
+ def post_state_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
241
+ match self.run(input, use_cache):
242
+ case Right((value, state)):
243
+ return Right((value, f(state)))
244
+ case Left(err):
245
+ return Left(err)
246
+ case x:
247
+ raise ValueError(f"Unexpected result from self.run {x}")
248
+ return self.__class__(post_state_run, name=self.name)
249
+
250
+ def pre_state(self, f: Callable[[S], S]) -> Algebra[A, S]:
251
+ def pre_state_run(state: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
252
+ return self.run(f(state), use_cache)
253
+ return self.__class__(pre_state_run, name=self.name)
254
+
237
255
 
256
+ def map_all(self, f: Callable[[A, S], Tuple[B, S]]) -> Algebra[B, S]:
257
+ def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
258
+ match self.run(input, use_cache):
259
+ case Right((value, state)):
260
+ new_value, new_state = f(value, state)
261
+ return Right((new_value, new_state))
262
+ case Left(err):
263
+ return Left(err)
264
+ case x:
265
+ raise ValueError(f"Unexpected result from self.run {x}")
266
+ return self.__class__(map_all_run, name=self.name) # type: ignore
238
267
  ######################################################## fundamental combinators ############################################
239
268
  def fmap(self, f: Callable[[A], B]) -> Algebra[B, S]:
240
269
  def fmap_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
@@ -245,20 +274,12 @@ class Algebra(ABC, Generic[A, S]):
245
274
  return cast(Either[Any, Tuple[B, S]], parsed)
246
275
  return self.__class__(fmap_run, name=self.name) # type: ignore
247
276
 
248
- def imap(self, f: Callable[[B], A]) -> Algebra[A, S]:
249
- return self.map_state(lambda s: s.map(f))
250
277
 
251
278
  def map(self, f: Callable[[A], B]) -> Algebra[B, S]:
252
279
  return self.fmap(f)
253
280
 
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
281
+ def bimap(self, f: Callable[[A], B], i: Callable[[B], A]) -> Algebra[B, S]:
282
+ return self.fmap(f).pre_state(lambda s: s.map(i))
262
283
 
263
284
  def map_error(self, f: Callable[[Optional[Any]], Any]) -> Algebra[A, S]:
264
285
  def map_error_run(input: S, use_cache:bool) -> Either[Any, Tuple[A, S]]:
@@ -268,12 +289,6 @@ class Algebra(ABC, Generic[A, S]):
268
289
  return parsed
269
290
  return self.__class__(map_error_run, name=self.name)
270
291
 
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
292
  def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Algebra[B, S]:
278
293
  def flat_map_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
279
294
  parsed = self.run(input, use_cache)
@@ -288,34 +303,34 @@ class Algebra(ABC, Generic[A, S]):
288
303
  def or_else_run(input: S, use_cache:bool) -> Either[Any, Tuple[Choice[A, B], S]]:
289
304
  match self.run(input, use_cache):
290
305
  case Right((value, state)):
291
- return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), state))
306
+ return Right((Choice(kind=ChoiceKind.LEFT, value=value), state))
292
307
  case Left(err):
293
308
  if isinstance(err, Error) and err.committed:
294
- return Left(err)
309
+ return Left(replace(err, committed=False))
295
310
  match other.run(input, use_cache):
296
311
  case Right((other_value, other_state)):
297
- return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=other_value), other_state))
312
+ return Right((Choice(kind=ChoiceKind.RIGHT, value=other_value), other_state))
298
313
  case Left(other_err):
299
314
  return Left(other_err)
300
315
  raise TypeError(f"Unexpected result type from {other}")
301
316
  raise TypeError(f"Unexpected result type from {self}")
302
317
  return self.__class__(or_else_run, name=f'{self.name} | {other.name}') # type: ignore
303
318
 
304
- def then_both(self, other: 'Algebra[B, S]') -> 'Algebra[Then[A, B], S]':
319
+ def then_both(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
305
320
  def then_both_f(a: A) -> Algebra[Then[A, B], S]:
306
321
  def combine(b: B) -> Then[A, B]:
307
322
  return Then(left=a, right=b, kind=ThenKind.BOTH)
308
323
  return other.fmap(combine)
309
324
  return self.flat_map(then_both_f).named(f'{self.name} + {other.name}')
310
325
 
311
- def then_left(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
326
+ def then_left(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
312
327
  def then_left_f(a: A) -> Algebra[Then[A, B], S]:
313
328
  def combine(b: B) -> Then[A, B]:
314
329
  return Then(left=a, right=b, kind=ThenKind.LEFT)
315
330
  return other.fmap(combine)
316
331
  return self.flat_map(then_left_f).named(f'{self.name} // {other.name}')
317
332
 
318
- def then_right(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
333
+ def then_right(self, other: Algebra[B, S]) -> Algebra[Then[A, B], S]:
319
334
  def then_right_f(a: A) -> Algebra[Then[A, B], S]:
320
335
  def combine(b: B) -> Then[A, B]:
321
336
  return Then(left=a, right=b, kind=ThenKind.RIGHT)
syncraft/ast.py CHANGED
@@ -3,54 +3,138 @@
3
3
  from __future__ import annotations
4
4
  import re
5
5
  from typing import (
6
- Optional, Any, TypeVar, Tuple, runtime_checkable,
7
- Dict, Generic, Callable, Union, Protocol
6
+ Optional, Any, TypeVar, Tuple, runtime_checkable, cast,
7
+ Generic, Callable, Union, Protocol, Type, List, ClassVar
8
8
  )
9
9
 
10
10
 
11
- from dataclasses import dataclass
11
+ from dataclasses import dataclass, replace, is_dataclass, asdict, fields
12
12
  from enum import Enum
13
- from syncraft.constraint import Bindable
13
+
14
14
 
15
15
 
16
16
 
17
17
  A = TypeVar('A')
18
18
  B = TypeVar('B')
19
19
  C = TypeVar('C')
20
- S = TypeVar('S', bound=Bindable)
20
+ D = TypeVar('D')
21
+ S = TypeVar('S')
22
+ S1 = TypeVar('S1')
23
+
21
24
 
22
25
  @dataclass(frozen=True)
23
- class Reducer(Generic[A, S]):
24
- run_f: Callable[[A, S], S]
25
- def __call__(self, a: A, s: S) -> S:
26
- return self.run_f(a, s)
27
-
28
- def map(self, f: Callable[[B], A]) -> Reducer[B, S]:
29
- def map_run(b: B, s: S) -> S:
30
- return self(f(b), s)
31
- return Reducer(map_run)
26
+ class Biarrow(Generic[A, B]):
27
+ forward: Callable[[A], B]
28
+ inverse: Callable[[B], A]
29
+ def __rshift__(self, other: Biarrow[B, C]) -> Biarrow[A, C]:
30
+ def fwd(a: A) -> C:
31
+ b = self.forward(a)
32
+ return other.forward(b)
33
+ def inv(c: C) -> A:
34
+ b = other.inverse(c)
35
+ return self.inverse(b)
36
+ return Biarrow(
37
+ forward=fwd,
38
+ inverse=inv
39
+ )
40
+ @staticmethod
41
+ def identity()->Biarrow[A, A]:
42
+ return Biarrow(
43
+ forward=lambda x: x,
44
+ inverse=lambda y: y
45
+ )
46
+
47
+ @staticmethod
48
+ def when(condition: Callable[..., bool],
49
+ then: Biarrow[A, B],
50
+ otherwise: Optional[Biarrow[A, B]] = None) -> Callable[..., Biarrow[A, B]]:
51
+ def _when(*args:Any, **kwargs:Any) -> Biarrow[A, B]:
52
+ return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
53
+ return _when
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class Lens(Generic[C, A]):
58
+ get: Callable[[C], A]
59
+ set: Callable[[C, A], C]
60
+
61
+ def modify(self, source: C, f: Callable[[A], A]) -> C:
62
+ return self.set(source, f(self.get(source)))
32
63
 
64
+ def bimap(self, ff: Callable[[A], B], bf: Callable[[B], A]) -> Lens[C, B]:
65
+ def getf(data: C) -> B:
66
+ return ff(self.get(data))
33
67
 
68
+ def setf(data: C, value: B) -> C:
69
+ return self.set(data, bf(value))
70
+
71
+ return Lens(get=getf, set=setf)
72
+
73
+ def __truediv__(self, other: Lens[A, B]) -> Lens[C, B]:
74
+ def get_composed(obj: C) -> B:
75
+ return other.get(self.get(obj))
76
+ def set_composed(obj: C, value: B) -> C:
77
+ return self.set(obj, other.set(self.get(obj), value))
78
+ return Lens(get=get_composed, set=set_composed)
34
79
 
80
+ def __rtruediv__(self, other: Lens[B, C])->Lens[B, A]:
81
+ return other.__truediv__(self)
82
+
83
+
35
84
  @dataclass(frozen=True)
36
85
  class Bimap(Generic[A, B]):
37
86
  run_f: Callable[[A], Tuple[B, Callable[[B], A]]]
38
87
  def __call__(self, a: A) -> Tuple[B, Callable[[B], A]]:
39
88
  return self.run_f(a)
40
- def __rshift__(self, other: Bimap[B, C]) -> Bimap[A, C]:
41
- def then_run(a: A) -> Tuple[C, Callable[[C], A]]:
42
- b, inv1 = self(a)
43
- c, inv2 = other(b)
44
- def inv(c2: C) -> A:
45
- return inv1(inv2(c2))
46
- return c, inv
47
- return Bimap(then_run)
89
+ def __rshift__(self, other: Bimap[B, C] | Biarrow[B, C]) -> Bimap[A, C]:
90
+ if isinstance(other, Biarrow):
91
+ def biarrow_then_run(a: A) -> Tuple[C, Callable[[C], A]]:
92
+ b, inv1 = self(a)
93
+ c = other.forward(b)
94
+ def inv(c2: C) -> A:
95
+ b2 = other.inverse(c2)
96
+ return inv1(b2)
97
+ return c, inv
98
+ return Bimap(biarrow_then_run)
99
+ elif isinstance(other, Bimap):
100
+ def bimap_then_run(a: A) -> Tuple[C, Callable[[C], A]]:
101
+ b, inv1 = self(a)
102
+ c, inv2 = other(b)
103
+ def inv(c2: C) -> A:
104
+ return inv1(inv2(c2))
105
+ return c, inv
106
+ return Bimap(bimap_then_run)
107
+ else:
108
+ raise TypeError(f"Unsupported type for Bimap >>: {type(other)}")
109
+ def __rrshift__(self, other: Bimap[C, A] | Biarrow[C, A]) -> Bimap[C, B]:
110
+ if isinstance(other, Biarrow):
111
+ def biarrow_then_run(c: C) -> Tuple[B, Callable[[B], C]]:
112
+ a = other.forward(c)
113
+ b2, inv1 = self(a)
114
+ def inv(a2: B) -> C:
115
+ a3 = inv1(a2)
116
+ return other.inverse(a3)
117
+ return b2, inv
118
+ return Bimap(biarrow_then_run)
119
+ elif isinstance(other, Bimap):
120
+ def bimap_then_run(c: C)->Tuple[B, Callable[[B], C]]:
121
+ a, a2c = other(c)
122
+ b2, b2a = self(a)
123
+ def inv(b3: B) -> C:
124
+ a2 = b2a(b3)
125
+ return a2c(a2)
126
+ return b2, inv
127
+ return Bimap(bimap_then_run)
128
+ else:
129
+ raise TypeError(f"Unsupported type for Bimap <<: {type(other)}")
130
+
131
+
48
132
  @staticmethod
49
133
  def const(a: B)->Bimap[B, B]:
50
134
  return Bimap(lambda _: (a, lambda b: b))
51
135
 
52
136
  @staticmethod
53
- def identity()->Bimap[Any, Any]:
137
+ def identity()->Bimap[A, A]:
54
138
  return Bimap(lambda a: (a, lambda b: b))
55
139
 
56
140
  @staticmethod
@@ -65,86 +149,179 @@ class Bimap(Generic[A, B]):
65
149
  return abc, inv_f
66
150
  return Bimap(when_run)
67
151
 
68
-
69
152
 
70
153
  @dataclass(frozen=True)
71
- class Biarrow(Generic[S, A, B]):
72
- forward: Callable[[S, A], Tuple[S, B]]
73
- inverse: Callable[[S, B], Tuple[S, A]]
74
- def __rshift__(self, other: Biarrow[S, B, C]) -> Biarrow[S, A, C]:
75
- def fwd(s: S, a: A) -> Tuple[S, C]:
76
- s1, b = self.forward(s, a)
77
- return other.forward(s1, b)
78
- def inv(s: S, c: C) -> Tuple[S, A]:
79
- s1, b = other.inverse(s, c)
80
- return self.inverse(s1, b)
81
- return Biarrow(
82
- forward=fwd,
83
- inverse=inv
84
- )
85
- @staticmethod
86
- def identity()->Biarrow[S, A, A]:
87
- return Biarrow(
88
- forward=lambda s, x: (s, x),
89
- inverse=lambda s, y: (s, y)
90
- )
91
-
92
- @staticmethod
93
- def when(condition: Callable[..., bool],
94
- then: Biarrow[S, A, B],
95
- otherwise: Optional[Biarrow[S, A, B]] = None) -> Callable[..., Biarrow[S, A, B]]:
96
- def _when(*args:Any, **kwargs:Any) -> Biarrow[S, A, B]:
97
- return then if condition(*args, **kwargs) else (otherwise or Biarrow.identity())
98
- return _when
154
+ class Reducer(Generic[A, S]):
155
+ run_f: Callable[[A, S], S]
156
+ def __call__(self, a: A, s: S) -> S:
157
+ return self.run_f(a, s)
158
+
159
+ def map(self, f: Callable[[B], A]) -> Reducer[B, S]:
160
+ def map_run(b: B, s: S) -> S:
161
+ return self(f(b), s)
162
+ return Reducer(map_run)
163
+
164
+ def __rshift__(self, other: Reducer[A, S]) -> Reducer[A, S]:
165
+ return Reducer(lambda a, s: other(a, self(a, s)))
166
+
167
+ def zip(self, other: Reducer[A, S1])-> Reducer[A, Tuple[S, S1]]:
168
+ return Reducer(lambda a, s: (self(a, s[0]), other(a, s[1])))
169
+
170
+ def diff(self, other: Reducer[B, S]) -> Reducer[Tuple[A, B], S]:
171
+ return Reducer(lambda ab, s: other(ab[1], self(ab[0], s)))
99
172
 
173
+ def filter(self, f: Callable[[A, S], bool]) -> Reducer[A, S]:
174
+ return Reducer(lambda a, s: self(a, s) if f(a, s) else s)
175
+
176
+
100
177
 
101
178
  @dataclass(frozen=True)
102
179
  class AST:
103
- pass
180
+ def bimap(self, r: Bimap[Any, Any]=Bimap.identity()) -> Tuple[Any, Callable[[Any], Any]]:
181
+ return r(self)
182
+
183
+ @dataclass(frozen=True)
184
+ class Nothing(AST):
185
+ def __str__(self)->str:
186
+ return self.__class__.__name__
187
+ def __repr__(self)->str:
188
+ return self.__str__()
104
189
 
190
+
191
+ @dataclass(frozen=True)
192
+ class Marked(Generic[A], AST):
193
+ name: str
194
+ value: A
195
+ def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[Marked[B], Callable[[Marked[B]], Marked[A]]]:
196
+ v, inner_f = self.value.bimap(r) if isinstance(self.value, AST) else r(self.value)
197
+ return Marked(name=self.name, value=v), lambda b: Marked(name = b.name, value=inner_f(b.value))
198
+
105
199
  class ChoiceKind(Enum):
106
200
  LEFT = 'left'
107
201
  RIGHT = 'right'
108
-
202
+
109
203
  @dataclass(frozen=True)
110
204
  class Choice(Generic[A, B], AST):
111
- kind: ChoiceKind
112
- left: Optional[A]
113
- right: Optional[B]
114
-
205
+ kind: Optional[ChoiceKind]
206
+ value: Optional[A | B] = None
207
+ def bimap(self, r: Bimap[A | B, C]=Bimap.identity()) -> Tuple[Optional[C], Callable[[Optional[C]], Choice[A, B]]]:
208
+ if self.value is None:
209
+ return None, lambda c: replace(self, value=None, kind=None)
210
+ else:
211
+ v, inv = self.value.bimap(r) if isinstance(self.value, AST) else r(self.value)
212
+ return v, lambda c: replace(self, value=inv(c) if c is not None else None, kind=None)
115
213
 
116
214
  @dataclass(frozen=True)
117
215
  class Many(Generic[A], AST):
118
216
  value: Tuple[A, ...]
217
+ def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[List[B], Callable[[List[B]], Many[A]]]:
218
+ ret = [v.bimap(r) if isinstance(v, AST) else r(v) for v in self.value]
219
+ def inv(bs: List[B]) -> Many[A]:
220
+ if len(bs) <= len(ret):
221
+ return Many(value = tuple(ret[i][1](bs[i]) for i in range(len(bs))))
222
+ else:
223
+ half = [ret[i][1](bs[i]) for i in range(len(bs))]
224
+ tmp = [ret[-1][1](bs[i]) for i in range(len(ret)-1, len(bs))]
225
+ return Many(value = tuple(half + tmp))
226
+ return [v[0] for v in ret], inv
119
227
 
120
- @dataclass(frozen=True)
121
- class Marked(Generic[A], AST):
122
- name: str
123
- value: A
124
228
  class ThenKind(Enum):
125
229
  BOTH = '+'
126
230
  LEFT = '//'
127
231
  RIGHT = '>>'
128
232
 
129
- FlatThen = Tuple[Any, ...]
130
- MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
131
-
132
233
  @dataclass(eq=True, frozen=True)
133
234
  class Then(Generic[A, B], AST):
134
235
  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
-
236
+ left: A
237
+ right: B
238
+ def arity(self)->int:
239
+ if self.kind == ThenKind.LEFT:
240
+ return self.left.arity() if isinstance(self.left, Then) else 1
241
+ elif self.kind == ThenKind.RIGHT:
242
+ return self.right.arity() if isinstance(self.right, Then) else 1
243
+ elif self.kind == ThenKind.BOTH:
244
+ left_arity = self.left.arity() if isinstance(self.left, Then) else 1
245
+ right_arity = self.right.arity() if isinstance(self.right, Then) else 1
246
+ return left_arity + right_arity
247
+ else:
248
+ return 1
249
+
250
+ def bimap(self, r: Bimap[A|B, Any]=Bimap.identity()) -> Tuple[Any | Tuple[Any, ...], Callable[[Any | Tuple[Any, ...]], Then[A, B]]]:
251
+ def need_wrap(x: Any) -> bool:
252
+ return not (isinstance(x, Then) and x.kind == ThenKind.BOTH)
253
+ match self.kind:
254
+ case ThenKind.LEFT:
255
+ lb, linv = self.left.bimap(r) if isinstance(self.left, AST) else r(self.left)
256
+ return lb, lambda c: replace(self, left=cast(A, linv(c)))
257
+ case ThenKind.RIGHT:
258
+ rb, rinv = self.right.bimap(r) if isinstance(self.right, AST) else r(self.right)
259
+ return rb, lambda c: replace(self, right=cast(B, rinv(c)))
260
+ case ThenKind.BOTH:
261
+ lb, linv = self.left.bimap(r) if isinstance(self.left, AST) else r(self.left)
262
+ rb, rinv = self.right.bimap(r) if isinstance(self.right, AST) else r(self.right)
263
+ left_v = (lb,) if need_wrap(self.left) else lb
264
+ right_v = (rb,) if need_wrap(self.right) else rb
265
+ def invf(b: Tuple[C, ...]) -> Then[A, B]:
266
+ left_size = self.left.arity() if isinstance(self.left, Then) else 1
267
+ right_size = self.right.arity() if isinstance(self.right, Then) else 1
268
+ lraw: Tuple[Any, ...] = b[:left_size]
269
+ rraw: Tuple[Any, ...] = b[left_size:left_size + right_size]
270
+ lraw = lraw[0] if left_size == 1 else lraw
271
+ rraw = rraw[0] if right_size == 1 else rraw
272
+ la = linv(lraw)
273
+ ra = rinv(rraw)
274
+ return replace(self, left=cast(A, la), right=cast(B, ra))
275
+ return left_v + right_v, invf
276
+
277
+
278
+ class DataclassInstance(Protocol):
279
+ __dataclass_fields__: ClassVar[dict[str, Any]]
145
280
 
281
+
282
+ E = TypeVar("E", bound=DataclassInstance)
283
+
284
+ Collector = Type[E] | Callable[..., E]
146
285
  @dataclass(frozen=True)
147
- class Token:
286
+ class Collect(Generic[A, E], AST):
287
+ collector: Collector
288
+ value: A
289
+ def bimap(self, r: Bimap[A, B]=Bimap.identity()) -> Tuple[B | E, Callable[[B | E], Collect[A, E]]]:
290
+ b, inner_f = r(self.value)
291
+ if isinstance(self.value, Then) and self.value.kind == ThenKind.BOTH:
292
+ assert isinstance(b, tuple), f"Expected tuple from Then.BOTH combinator, got {type(b)}"
293
+ index: List[str | int] = []
294
+ named_count = 0
295
+ for i, v in enumerate(b):
296
+ if isinstance(v, Marked):
297
+ index.append(v.name)
298
+ named_count += 1
299
+ else:
300
+ index.append(i - named_count)
301
+ named = {v.name: v.value for v in b if isinstance(v, Marked)}
302
+ unnamed = [v for v in b if not isinstance(v, Marked)]
303
+ ret: E = self.collector(*unnamed, **named)
304
+ def invf(e: E) -> Tuple[Any, ...]:
305
+ assert is_dataclass(e), f"Expected dataclass instance for collector inverse, got {type(e)}"
306
+ named_dict = asdict(e)
307
+ unnamed = []
308
+ for f in fields(e):
309
+ if f.name not in named:
310
+ unnamed.append(named_dict[f.name])
311
+ tmp = []
312
+ for x in index:
313
+ if isinstance(x, str):
314
+ tmp.append(Marked(name=x, value=named_dict[x]))
315
+ else:
316
+ tmp.append(unnamed[x])
317
+ return tuple(tmp)
318
+ return ret, lambda e: replace(self, value=inner_f(invf(e))) # type: ignore
319
+ else:
320
+ return b, lambda e: replace(self, value=inner_f(e)) # type: ignore
321
+
322
+ #########################################################################################################################
323
+ @dataclass(frozen=True)
324
+ class Token(AST):
148
325
  token_type: Enum
149
326
  text: str
150
327
  def __str__(self) -> str:
@@ -152,8 +329,16 @@ class Token:
152
329
 
153
330
  def __repr__(self) -> str:
154
331
  return self.__str__()
332
+
333
+ @runtime_checkable
334
+ class TokenProtocol(Protocol):
335
+ @property
336
+ def token_type(self) -> Enum: ...
337
+ @property
338
+ def text(self) -> str: ...
339
+
340
+ T = TypeVar('T', bound=TokenProtocol)
155
341
 
156
-
157
342
 
158
343
  @dataclass(frozen=True)
159
344
  class TokenSpec:
@@ -170,16 +355,13 @@ class TokenSpec:
170
355
  return type_match and value_match
171
356
 
172
357
 
173
-
174
-
175
- T = TypeVar('T', bound=TokenProtocol)
176
-
177
-
178
358
  ParseResult = Union[
179
359
  Then['ParseResult[T]', 'ParseResult[T]'],
180
360
  Marked['ParseResult[T]'],
181
361
  Choice['ParseResult[T]', 'ParseResult[T]'],
182
362
  Many['ParseResult[T]'],
363
+ Collect['ParseResult[T]', Any],
364
+ Nothing,
183
365
  T,
184
366
  ]
185
367
 
syncraft/finder.py CHANGED
@@ -1,40 +1,40 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import (
4
- Any, Tuple, Generator as YieldGen
4
+ Any, Tuple, Generator as YieldGen, TypeVar
5
5
  )
6
6
  from dataclasses import dataclass
7
7
  from syncraft.algebra import (
8
8
  Algebra, Either, Right,
9
9
  )
10
- from syncraft.ast import T, ParseResult, Choice, Many, Then, Marked
10
+ from syncraft.ast import TokenProtocol, ParseResult, Choice, Many, Then, Marked, Collect
11
11
 
12
12
  from syncraft.generator import GenState, Generator
13
13
 
14
14
  from syncraft.syntax import Syntax
15
15
 
16
16
 
17
-
17
+ T=TypeVar('T', bound=TokenProtocol)
18
18
  @dataclass(frozen=True)
19
19
  class Finder(Generator[T]):
20
20
  @classmethod
21
21
  def anything(cls)->Algebra[Any, GenState[T]]:
22
22
  def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Any, GenState[T]]]:
23
23
  return Right((input.ast, input))
24
- return cls(anything_run, name=cls.__name__ + '.anything()')
24
+ return cls(anything_run, name=cls.__name__ + '.anything')
25
25
 
26
26
 
27
27
 
28
- anything = Syntax(lambda cls: cls.factory('anything')).describe(name="anything", fixity='infix')
28
+ anything = Syntax(lambda cls: cls.factory('anything')).describe(name="Anything", fixity='infix')
29
29
 
30
- def matches(syntax: Syntax[Any, Any], data: ParseResult[T])-> bool:
30
+ def matches(syntax: Syntax[Any, Any], data: ParseResult[Any])-> bool:
31
31
  gen = syntax(Finder)
32
- state = GenState.from_ast(ast = data, restore_pruned=True)
32
+ state = GenState[Any].from_ast(ast = data, restore_pruned=True)
33
33
  result = gen.run(state, use_cache=True)
34
34
  return isinstance(result, Right)
35
35
 
36
36
 
37
- def find(syntax: Syntax[Any, Any], data: ParseResult[T]) -> YieldGen[ParseResult[T], None, None]:
37
+ def find(syntax: Syntax[Any, Any], data: ParseResult[Any]) -> YieldGen[ParseResult[Any], None, None]:
38
38
  if matches(syntax, data):
39
39
  yield data
40
40
  match data:
@@ -48,10 +48,10 @@ def find(syntax: Syntax[Any, Any], data: ParseResult[T]) -> YieldGen[ParseResult
48
48
  yield from find(syntax, e)
49
49
  case Marked(value=value):
50
50
  yield from find(syntax, value)
51
- case Choice(left=left, right=right):
52
- if left is not None:
53
- yield from find(syntax, left)
54
- if right is not None:
55
- yield from find(syntax, right)
51
+ case Choice(value=value):
52
+ if value is not None:
53
+ yield from find(syntax, value)
54
+ case Collect(value=value):
55
+ yield from find(syntax, value)
56
56
  case _:
57
57
  pass
syncraft/generator.py CHANGED
@@ -11,8 +11,8 @@ from syncraft.algebra import (
11
11
  )
12
12
 
13
13
  from syncraft.ast import (
14
- T, ParseResult, AST, Token, TokenSpec,
15
- Bindable,
14
+ ParseResult, AST, Token, TokenSpec,
15
+ Nothing, TokenProtocol,
16
16
  Choice, Many, ChoiceKind,
17
17
  Then, ThenKind, Marked
18
18
  )
@@ -23,6 +23,14 @@ import re
23
23
  import rstr
24
24
  from functools import lru_cache
25
25
  import random
26
+ from rich import print
27
+ from syncraft.constraint import Bindable
28
+
29
+ T = TypeVar('T', bound=TokenProtocol)
30
+
31
+ S = TypeVar('S', bound=Bindable)
32
+
33
+
26
34
 
27
35
  B = TypeVar('B')
28
36
 
@@ -63,9 +71,7 @@ class GenState(Bindable, Generic[T]):
63
71
  if isinstance(self.ast, Then) and (self.ast.kind != ThenKind.LEFT or self.restore_pruned):
64
72
  return replace(self, ast=self.ast.right)
65
73
  return replace(self, ast=None)
66
-
67
74
 
68
-
69
75
  def down(self, index: int) -> GenState[T]:
70
76
  if self.ast is None:
71
77
  return self
@@ -76,19 +82,27 @@ class GenState(Bindable, Generic[T]):
76
82
  raise TypeError(f"Invalid AST type({self.ast}) for down traversal")
77
83
 
78
84
  @classmethod
79
- def from_ast(cls, *, ast: Optional[ParseResult[T]], seed: int = 0, restore_pruned:bool=False) -> GenState[T]:
85
+ def from_ast(cls,
86
+ *,
87
+ ast: Optional[ParseResult[T]],
88
+ seed: int = 0,
89
+ restore_pruned:bool=False) -> GenState[T]:
80
90
  return cls(ast=ast, seed=seed, restore_pruned=restore_pruned)
81
91
 
82
-
83
-
84
-
85
-
86
92
  @lru_cache(maxsize=None)
87
- def token_type_from_string(token_type: Optional[TokenType], text: str, case_sensitive:bool)-> TokenType:
93
+ def token_type_from_string(token_type: Optional[TokenType],
94
+ text: str,
95
+ case_sensitive:bool = False)-> TokenType:
88
96
  if not isinstance(token_type, TokenType) or token_type == TokenType.VAR:
89
- for t in TokenType:
90
- if t.value == text or str(t.value).lower() == text.lower():
91
- return t
97
+ if case_sensitive:
98
+ for t in TokenType:
99
+ if t.value == text:
100
+ return t
101
+ else:
102
+ text = text.lower()
103
+ for t in TokenType:
104
+ if t.value == text or str(t.value).lower() == text:
105
+ return t
92
106
  return TokenType.VAR
93
107
  return token_type
94
108
 
@@ -121,13 +135,10 @@ class TokenGen(TokenSpec):
121
135
  else:
122
136
  text = "VALUE"
123
137
 
124
- return Token(token_type= token_type_from_string(self.token_type,
125
- text,
126
- self.case_sensitive),
127
- text=text)
138
+ return Token(token_type=token_type_from_string(self.token_type, text, case_sensitive=False), text=text)
128
139
 
129
140
  @staticmethod
130
- def from_string(string: str)->Token:
141
+ def from_string(string: str) -> Token:
131
142
  return Token(token_type=token_type_from_string(None, string, case_sensitive=False), text=string)
132
143
 
133
144
 
@@ -136,7 +147,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
136
147
  def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
137
148
  def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
138
149
  try:
139
- if not isinstance(input.ast, Then):
150
+ if not isinstance(input.ast, Then) or isinstance(input.ast, Nothing):
140
151
  return Left(Error(this=self,
141
152
  message=f"Expect Then got {input.ast}",
142
153
  state=input))
@@ -179,7 +190,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
179
190
  pass
180
191
  return Right((Many(value=tuple(ret)), input))
181
192
  else:
182
- if not isinstance(input.ast, Many):
193
+ if not isinstance(input.ast, Many) or isinstance(input.ast, Nothing):
183
194
  return Left(Error(this=self,
184
195
  message=f"Expect Many got {input.ast}",
185
196
  state=input))
@@ -194,7 +205,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
194
205
  this=self,
195
206
  state=input.inject(x)
196
207
  ))
197
- case Left(_):
208
+ case Left(e):
198
209
  pass
199
210
  if len(ret) < at_least:
200
211
  return Left(Error(
@@ -210,22 +221,34 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
210
221
  other: Algebra[ParseResult[T], GenState[T]]
211
222
  ) -> Algebra[Choice[ParseResult[T], ParseResult[T]], GenState[T]]:
212
223
  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,
224
+ def exec(kind: ChoiceKind | None,
214
225
  left: GenState[T],
215
226
  right: GenState[T])->Either[Any, Tuple[Choice[ParseResult[T], ParseResult[T]], GenState[T]]]:
216
227
  match kind:
217
228
  case ChoiceKind.LEFT:
218
229
  match self.run(left, use_cache):
219
230
  case Right((value, next_input)):
220
- return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), next_input))
231
+ return Right((Choice(kind=ChoiceKind.LEFT, value=value), next_input))
221
232
  case Left(error):
222
233
  return Left(error)
223
234
  case ChoiceKind.RIGHT:
224
235
  match other.run(right, use_cache):
225
236
  case Right((value, next_input)):
226
- return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=value), next_input))
237
+ return Right((Choice(kind=ChoiceKind.RIGHT, value=value), next_input))
227
238
  case Left(error):
228
239
  return Left(error)
240
+ case None:
241
+ match self.run(left, use_cache):
242
+ case Right((value, next_input)):
243
+ return Right((Choice(kind=ChoiceKind.LEFT, value=value), next_input))
244
+ case Left(error):
245
+ if isinstance(error, Error) and error.committed:
246
+ return Left(replace(error, committed=False))
247
+ match other.run(left, use_cache):
248
+ case Right((value, next_input)):
249
+ return Right((Choice(kind=ChoiceKind.RIGHT, value=value), next_input))
250
+ case Left(error):
251
+ return Left(error)
229
252
  raise ValueError(f"Invalid ChoiceKind: {kind}")
230
253
 
231
254
  if input.pruned:
@@ -233,14 +256,14 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
233
256
  which = forked_input.rng("or_else").choice((ChoiceKind.LEFT, ChoiceKind.RIGHT))
234
257
  return exec(which, forked_input, forked_input)
235
258
  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:
259
+ if not isinstance(input.ast, Choice) or isinstance(input.ast, Nothing):
241
260
  return Left(Error(this=self,
242
261
  message=f"Expect Choice got {input.ast}",
243
262
  state=input))
263
+ else:
264
+ return exec(input.ast.kind,
265
+ input.inject(input.ast.value),
266
+ input.inject(input.ast.value))
244
267
  return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})") # type: ignore
245
268
 
246
269
  @classmethod
@@ -259,7 +282,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
259
282
  current = input.ast
260
283
  if not isinstance(current, Token) or not gen.is_valid(current):
261
284
  return Left(Error(None,
262
- message=f"Expected a Token, but got {type(current)}.",
285
+ message=f"Expected a Token({gen.text}), but got {current}.",
263
286
  state=input))
264
287
  return Right((current, input))
265
288
  lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
syncraft/parser.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  import re
3
3
  from sqlglot import tokenize, TokenType, Parser as GlotParser, exp
4
4
  from typing import (
5
- Optional, List, Any, Tuple,
5
+ Optional, List, Any, Tuple, TypeVar,
6
6
  Generic
7
7
  )
8
8
  from syncraft.algebra import (
@@ -13,9 +13,11 @@ from enum import Enum
13
13
  from functools import reduce
14
14
  from syncraft.syntax import Syntax
15
15
 
16
- from syncraft.ast import Token, TokenSpec, AST, T, Bindable
16
+ from syncraft.ast import Token, TokenSpec, AST, TokenProtocol
17
+ from syncraft.constraint import Bindable
17
18
 
18
19
 
20
+ T = TypeVar('T', bound=TokenProtocol)
19
21
  @dataclass(frozen=True)
20
22
  class ParserState(Bindable, Generic[T]):
21
23
  input: Tuple[T, ...] = field(default_factory=tuple)
syncraft/syntax.py CHANGED
@@ -8,20 +8,22 @@ 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, Collector
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: Collector[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.27
3
+ Version: 0.1.29
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.
@@ -0,0 +1,16 @@
1
+ syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ syncraft/algebra.py,sha256=gXs4rD6Inp6kGf5Vd5kHGgmb2K_qFlWFQ09i7tmcCmo,15662
3
+ syncraft/ast.py,sha256=MSzEvpXoEPxbLX_tYwKbvNEpPzyGc22-poGtHsc1S30,13747
4
+ syncraft/constraint.py,sha256=uT-ELzvv8J-s-Y1VYkXePUezseqCLhqzYUzFBYs0HsE,6418
5
+ syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
6
+ syncraft/finder.py,sha256=L75A0bAjdlntWk-Y1rCeINBOzGeGdE_zHrTmcpQ7m9g,1875
7
+ syncraft/generator.py,sha256=C3N0Nxk9grN6iPxLHt1Ur2xzS5S-9LsNvMMc-ehd36E,12981
8
+ syncraft/parser.py,sha256=RkQwFv00rOI-n4kvG4baGVTdK46T-_Hw7OJ0FB7g72g,11379
9
+ syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
11
+ syncraft/syntax.py,sha256=a3g-qcTHpIicMO0KseHuKe5liNfYawLRO-M1eCBS6d4,16574
12
+ syncraft-0.1.29.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
13
+ syncraft-0.1.29.dist-info/METADATA,sha256=OcqydNw8LWB6R94v-AV_SS4-YSA6QxxTioAqnViK-2w,1204
14
+ syncraft-0.1.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ syncraft-0.1.29.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
16
+ syncraft-0.1.29.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- syncraft/algebra.py,sha256=FBy0cCl4RGnsi1uxoIIZPRcHgfHxWwUal9iLV_Mtc6I,14791
3
- syncraft/ast.py,sha256=5WW3NsnCWWPLHr1tP2dDrZQgkl03h4xsz9zJsaABaq0,5049
4
- syncraft/constraint.py,sha256=uT-ELzvv8J-s-Y1VYkXePUezseqCLhqzYUzFBYs0HsE,6418
5
- syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
6
- syncraft/finder.py,sha256=GJYnUyBlteMb9gblVKkwy_vVxOMxTwP5P80tWA3kM2Q,1813
7
- syncraft/generator.py,sha256=CGasCEu79ce8pC56Lb6_gcGCS7pHN6MERv5OEh_Cl-c,11751
8
- syncraft/parser.py,sha256=UpNtTckygIwWG8cX2ZvdEcUi3Il8gpC7aZAzPKx2crg,11289
9
- syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
11
- syncraft/syntax.py,sha256=JqydSdFkD4Bj0A-S6tqM47ZSbDQEoioNBhOy0WqCEY8,15005
12
- syncraft-0.1.27.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
13
- syncraft-0.1.27.dist-info/METADATA,sha256=vF7-9O08fDH43chYaXyXqKNvwPwuWbIfrROdSCag2wA,1199
14
- syncraft-0.1.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- syncraft-0.1.27.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
16
- syncraft-0.1.27.dist-info/RECORD,,