syncraft 0.1.24__py3-none-any.whl → 0.1.26__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,7 @@ 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, ThenResult, ManyResult, OrResult, S
11
+ from syncraft.ast import ThenKind, Then, S, Choice, Many, ChoiceKind
12
12
 
13
13
  A = TypeVar('A') # Result type
14
14
  B = TypeVar('B') # Mapped result type
@@ -235,15 +235,24 @@ class Algebra(ABC, Generic[A, S]):
235
235
  return lazy_self
236
236
 
237
237
 
238
- ######################################################## fundamental combinators ############################################
239
- def map(self, f: Callable[[A], B]) -> Algebra[B, S]:
240
- def map_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
238
+ ######################################################## fundamental combinators ############################################
239
+ def fmap(self, f: Callable[[A], B]) -> Algebra[B, S]:
240
+ def fmap_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
241
241
  parsed = self.run(input, use_cache)
242
242
  if isinstance(parsed, Right):
243
243
  return Right((f(parsed.value[0]), parsed.value[1]))
244
244
  else:
245
245
  return cast(Either[Any, Tuple[B, S]], parsed)
246
- return self.__class__(map_run, name=self.name) # type: ignore
246
+ return self.__class__(fmap_run, name=self.name) # type: ignore
247
+
248
+ def imap(self, f: Callable[[B], A]) -> Algebra[A, S]:
249
+ return self.map_state(lambda s: s.map(f))
250
+
251
+ def map(self, f: Callable[[A], B]) -> Algebra[B, S]:
252
+ return self.fmap(f)
253
+
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)
247
256
 
248
257
  def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]])->Algebra[B, S]:
249
258
  def map_all_run(input: S, use_cache:bool) -> Either[Any, Tuple[B, S]]:
@@ -275,41 +284,48 @@ class Algebra(ABC, Generic[A, S]):
275
284
  return self.__class__(flat_map_run, name=self.name) # type: ignore
276
285
 
277
286
 
278
- def or_else(self: Algebra[A, S], other: Algebra[B, S]) -> Algebra[OrResult[A | B], S]:
279
- def or_else_run(input: S, use_cache:bool) -> Either[Any, Tuple[OrResult[A | B], S]]:
287
+ def or_else(self: Algebra[A, S], other: Algebra[B, S]) -> Algebra[Choice[A, B], S]:
288
+ def or_else_run(input: S, use_cache:bool) -> Either[Any, Tuple[Choice[A, B], S]]:
280
289
  match self.run(input, use_cache):
281
290
  case Right((value, state)):
282
- return Right((OrResult(value=value), state))
291
+ return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), state))
283
292
  case Left(err):
284
293
  if isinstance(err, Error) and err.committed:
285
294
  return Left(err)
286
295
  match other.run(input, use_cache):
287
296
  case Right((other_value, other_state)):
288
- return Right((OrResult(value=other_value), other_state))
297
+ return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=other_value), other_state))
289
298
  case Left(other_err):
290
299
  return Left(other_err)
291
300
  raise TypeError(f"Unexpected result type from {other}")
292
301
  raise TypeError(f"Unexpected result type from {self}")
293
302
  return self.__class__(or_else_run, name=f'{self.name} | {other.name}') # type: ignore
294
303
 
295
- def then_both(self, other: 'Algebra[B, S]') -> 'Algebra[ThenResult[A, B], S]':
296
- def then_both_f(a: A) -> Algebra[ThenResult[A, B], S]:
297
- def combine(b: B) -> ThenResult[A, B]:
298
- return ThenResult(left=a, right=b, kind=ThenKind.BOTH)
299
- return other.map(combine)
304
+ def then_both(self, other: 'Algebra[B, S]') -> 'Algebra[Then[A, B], S]':
305
+ def then_both_f(a: A) -> Algebra[Then[A, B], S]:
306
+ def combine(b: B) -> Then[A, B]:
307
+ return Then(left=a, right=b, kind=ThenKind.BOTH)
308
+ return other.fmap(combine)
300
309
  return self.flat_map(then_both_f).named(f'{self.name} + {other.name}')
301
-
302
- def then_left(self, other: Algebra[B, S]) -> Algebra[ThenResult[A, B], S]:
303
- return self.then_both(other).map(lambda b: replace(b, kind = ThenKind.LEFT)).named(f'{self.name} // {other.name}')
304
-
305
- def then_right(self, other: Algebra[B, S]) -> Algebra[ThenResult[A, B], S]:
306
- return self.then_both(other).map(lambda b: replace(b, kind=ThenKind.RIGHT)).named(f'{self.name} >> {other.name}')
307
-
308
310
 
309
- def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[A], S]:
311
+ def then_left(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
312
+ def then_left_f(a: A) -> Algebra[Then[A, B], S]:
313
+ def combine(b: B) -> Then[A, B]:
314
+ return Then(left=a, right=b, kind=ThenKind.LEFT)
315
+ return other.fmap(combine)
316
+ return self.flat_map(then_left_f).named(f'{self.name} // {other.name}')
317
+
318
+ def then_right(self, other: Algebra[B, S]) -> 'Algebra[Then[A, B], S]':
319
+ def then_right_f(a: A) -> Algebra[Then[A, B], S]:
320
+ def combine(b: B) -> Then[A, B]:
321
+ return Then(left=a, right=b, kind=ThenKind.RIGHT)
322
+ return other.fmap(combine)
323
+ return self.flat_map(then_right_f).named(f'{self.name} >> {other.name}')
324
+
325
+ def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[Many[A], S]:
310
326
  assert at_least > 0, "at_least must be greater than 0"
311
327
  assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
312
- def many_run(input: S, use_cache:bool) -> Either[Any, Tuple[ManyResult[A], S]]:
328
+ def many_run(input: S, use_cache:bool) -> Either[Any, Tuple[Many[A], S]]:
313
329
  ret: List[A] = []
314
330
  current_input = input
315
331
  while True:
@@ -333,7 +349,7 @@ class Algebra(ABC, Generic[A, S]):
333
349
  this=self,
334
350
  state=current_input
335
351
  ))
336
- return Right((ManyResult(value=tuple(ret)), current_input))
352
+ return Right((Many(value=tuple(ret)), current_input))
337
353
  return self.__class__(many_run, name=f'*({self.name})') # type: ignore
338
354
 
339
355
 
syncraft/ast.py CHANGED
@@ -10,7 +10,7 @@ from typing import (
10
10
 
11
11
  from dataclasses import dataclass, replace, is_dataclass, asdict
12
12
  from enum import Enum
13
- from functools import cached_property
13
+
14
14
  from syncraft.constraint import Binding, Variable, Bindable
15
15
 
16
16
 
@@ -99,42 +99,28 @@ class Biarrow(Generic[S, A, B]):
99
99
  return _when
100
100
 
101
101
 
102
-
103
- class StructuralResult:
104
-
105
- def bimap(self, f: Bimap[Any, Any]=Bimap.identity())->Tuple[Any, Callable[[Any], Any]]:
106
- return f(self)
102
+ @dataclass(frozen=True)
103
+ class AST:
104
+ pass
107
105
 
108
-
106
+ class ChoiceKind(Enum):
107
+ LEFT = 'left'
108
+ RIGHT = 'right'
109
109
  @dataclass(frozen=True)
110
- class MarkedResult(Generic[A], StructuralResult):
111
- name: str
112
- value: A
113
- def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[MarkedResult[B], Callable[[MarkedResult[B]], MarkedResult[A]]]:
114
- b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
115
- return MarkedResult(name=self.name, value=b), lambda b: replace(self, value=inv(b.value))
110
+ class Choice(Generic[A, B], AST):
111
+ kind: ChoiceKind
112
+ left: Optional[A]
113
+ right: Optional[B]
116
114
 
117
115
 
118
- @dataclass(eq=True, frozen=True)
119
- class ManyResult(Generic[A], StructuralResult):
116
+ @dataclass(frozen=True)
117
+ class Many(Generic[A], AST):
120
118
  value: Tuple[A, ...]
121
- def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[List[B], Callable[[List[B]], ManyResult[A]]]:
122
- assert self.value
123
- forward = [v.bimap(f) if isinstance(v, StructuralResult) else f(v) for v in self.value]
124
- def invf(b: List[B]) -> ManyResult[A]:
125
- assert len(b) <= len(forward)
126
- return replace(self, value=tuple([forward[i][1](bb) for i, bb in enumerate(b)]))
127
- return [b for b, _ in forward], invf
128
119
 
129
-
130
-
131
- @dataclass(eq=True, frozen=True)
132
- class OrResult(Generic[A], StructuralResult):
120
+ @dataclass(frozen=True)
121
+ class Marked(Generic[A], AST):
122
+ name: str
133
123
  value: A
134
- def bimap(self, f: Bimap[A, B]=Bimap.identity())->Tuple[B, Callable[[B], OrResult[A]]]:
135
- b, inv = self.value.bimap(f) if isinstance(self.value, StructuralResult) else f(self.value)
136
- return b, lambda b: replace(self, value=inv(b))
137
-
138
124
  class ThenKind(Enum):
139
125
  BOTH = '+'
140
126
  LEFT = '//'
@@ -144,83 +130,10 @@ FlatThen = Tuple[Any, ...]
144
130
  MarkedThen = Tuple[Dict[str, Any] | Any, FlatThen]
145
131
 
146
132
  @dataclass(eq=True, frozen=True)
147
- class ThenResult(Generic[A, B], StructuralResult):
133
+ class Then(Generic[A, B], AST):
148
134
  kind: ThenKind
149
- left: A
150
- right: B
151
- @staticmethod
152
- def collect_marked(a: FlatThen, f: Optional[Callable[..., Any]] = None)->Tuple[MarkedThen, Callable[[MarkedThen], FlatThen]]:
153
- index: List[str | int] = []
154
- named_count = 0
155
- for i, v in enumerate(a):
156
- if isinstance(v, MarkedResult):
157
- index.append(v.name)
158
- named_count += 1
159
- else:
160
- index.append(i - named_count)
161
- named = {v.name: v.value for v in a if isinstance(v, MarkedResult)}
162
- unnamed = [v for v in a if not isinstance(v, MarkedResult)]
163
- if f is None:
164
- ret = (named, tuple(unnamed))
165
- else:
166
- ret = (f(**named), tuple(unnamed))
167
- def invf(b: MarkedThen) -> Tuple[Any, ...]:
168
- named_value, unnamed_value = b
169
- assert isinstance(named_value, dict) or is_dataclass(named_value), f"Expected dict or dataclass for named values, got {type(named_value)}"
170
- if is_dataclass(named_value):
171
- named_dict = named | asdict(cast(Any, named_value))
172
- else:
173
- named_dict = named | named_value
174
- ret = []
175
- for x in index:
176
- if isinstance(x, str):
177
- assert x in named_dict, f"Missing named value: {x}"
178
- ret.append(named_dict[x])
179
- else:
180
- assert 0 <= x < len(unnamed_value), f"Missing unnamed value at index: {x}"
181
- ret.append(unnamed_value[x])
182
- return tuple(ret)
183
- return ret, invf
184
-
185
- def bimap(self, f: Bimap[Any, Any]=Bimap.identity()) -> Tuple[MarkedThen, Callable[[MarkedThen], ThenResult[A, B]]]:
186
- match self.kind:
187
- case ThenKind.LEFT:
188
- lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
189
- return lb, lambda b: replace(self, left=linv(b))
190
- case ThenKind.RIGHT:
191
- rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
192
- return rb, lambda b: replace(self, right=rinv(b))
193
- case ThenKind.BOTH:
194
- lb, linv = self.left.bimap(f) if isinstance(self.left, StructuralResult) else f(self.left)
195
- rb, rinv = self.right.bimap(f) if isinstance(self.right, StructuralResult) else f(self.right)
196
- left_v = (lb,) if not isinstance(self.left, ThenResult) else lb
197
- right_v = (rb,) if not isinstance(self.right, ThenResult) else rb
198
- def invf(b: Tuple[Any, ...]) -> ThenResult[A, B]:
199
- left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
200
- right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
201
- lraw = b[:left_size]
202
- rraw = b[left_size:left_size + right_size]
203
- lraw = lraw[0] if left_size == 1 else lraw
204
- rraw = rraw[0] if right_size == 1 else rraw
205
- la = linv(lraw)
206
- ra = rinv(rraw)
207
- return replace(self, left=la, right=ra)
208
- return left_v + right_v, invf
209
- # data, func = ThenResult.collect_marked(left_v + right_v)
210
- # return data, lambda d: invf(func(d))
211
-
212
-
213
- def arity(self)->int:
214
- if self.kind == ThenKind.LEFT:
215
- return self.left.arity() if isinstance(self.left, ThenResult) else 1
216
- elif self.kind == ThenKind.RIGHT:
217
- return self.right.arity() if isinstance(self.right, ThenResult) else 1
218
- elif self.kind == ThenKind.BOTH:
219
- left_arity = self.left.arity() if isinstance(self.left, ThenResult) else 1
220
- right_arity = self.right.arity() if isinstance(self.right, ThenResult) else 1
221
- return left_arity + right_arity
222
- else:
223
- return 1
135
+ left: Optional[A]
136
+ right: Optional[B]
224
137
 
225
138
  @runtime_checkable
226
139
  class TokenProtocol(Protocol):
@@ -263,83 +176,12 @@ T = TypeVar('T', bound=TokenProtocol)
263
176
 
264
177
 
265
178
  ParseResult = Union[
266
- ThenResult['ParseResult[T]', 'ParseResult[T]'],
267
- MarkedResult['ParseResult[T]'],
268
- ManyResult['ParseResult[T]'],
269
- OrResult['ParseResult[T]'],
179
+ Then['ParseResult[T]', 'ParseResult[T]'],
180
+ Marked['ParseResult[T]'],
181
+ Choice['ParseResult[T]', 'ParseResult[T]'],
182
+ Many['ParseResult[T]'],
270
183
  T,
271
184
  ]
272
185
 
273
186
 
274
187
 
275
- @dataclass(frozen=True)
276
- class AST(Generic[T]):
277
- focus: ParseResult[T]
278
- pruned: bool = False
279
- parent: Optional[AST[T]] = None
280
-
281
- def bimap(self)->Tuple[Any, Callable[[Any], AST[T]]]:
282
- if isinstance(self.focus, StructuralResult):
283
- data, invf = self.focus.bimap()
284
- return data, lambda x: replace(self, focus=invf(x))
285
- else:
286
- return self.focus, lambda x: replace(self, focus=x)
287
-
288
- def wrapper(self)-> Callable[[Any], Any]:
289
- if isinstance(self.focus, MarkedResult):
290
- focus = cast(MarkedResult[Any], self.focus)
291
- return lambda x: MarkedResult(name = focus.name, value = x)
292
- else:
293
- return lambda x: x
294
-
295
- def is_named(self) -> bool:
296
- return isinstance(self.focus, MarkedResult)
297
-
298
- def left(self) -> Optional[AST[T]]:
299
- match self.focus:
300
- case ThenResult(left=left, kind=kind):
301
- return replace(self, focus=left, parent=self, pruned = self.pruned or kind == ThenKind.RIGHT)
302
- case _:
303
- raise TypeError(f"Invalid focus type({self.focus}) for left traversal")
304
-
305
- def right(self) -> Optional[AST[T]]:
306
- match self.focus:
307
- case ThenResult(right=right, kind=kind):
308
- return replace(self, focus=right, parent=self, pruned = self.pruned or kind == ThenKind.LEFT)
309
- case _:
310
- raise TypeError(f"Invalid focus type({self.focus}) for right traversal")
311
-
312
-
313
- def down(self, index: int) -> Optional[AST[T]]:
314
- match self.focus:
315
- case ManyResult(value=children):
316
- if 0 <= index < len(children):
317
- return replace(self, focus=children[index], parent=self, pruned=self.pruned)
318
- else:
319
- raise IndexError(f"Index {index} out of bounds for ManyResult with {len(children)} children")
320
- case OrResult(value=value):
321
- if index == 0:
322
- return replace(self, focus=value, parent=self, pruned=self.pruned)
323
- else:
324
- raise IndexError(f"Index {index} out of bounds for OrResult")
325
- case MarkedResult(value=value):
326
- return replace(self, focus=value, parent=self, pruned=self.pruned)
327
- case _:
328
- raise TypeError(f"Invalid focus type({self.focus}) for down traversal")
329
-
330
- def how_many(self)->int:
331
- focus = self.focus.value if isinstance(self.focus, MarkedResult) else self.focus
332
- match focus:
333
- case ManyResult(value=children):
334
- return len(children)
335
- case _:
336
- raise TypeError(f"Invalid focus type({self.focus}) for how_many")
337
-
338
-
339
-
340
- @cached_property
341
- def root(self) -> AST[T]:
342
- while self.parent is not None:
343
- self = self.parent
344
- return self
345
-
syncraft/constraint.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Callable, Generic, Tuple, TypeVar, Optional, Any, Protocol
2
+ from typing import Callable, Generic, Tuple, TypeVar, Optional, Any, Self
3
3
  from enum import Enum
4
4
  from dataclasses import dataclass, field, replace
5
5
  import collections.abc
@@ -109,9 +109,17 @@ class Binding:
109
109
  ret[var].append(node)
110
110
  return FrozenDict({k: tuple(vs) for k, vs in ret.items()})
111
111
 
112
- class Bindable(Protocol):
113
- binding: Binding
114
- def bind(self, var: Variable, node: Any) -> Any: ...
112
+
113
+ A = TypeVar('A')
114
+ @dataclass(frozen=True)
115
+ class Bindable:
116
+ binding: Binding = field(default_factory=Binding)
117
+
118
+ def map(self, f: Callable[[Any], Any])->Self:
119
+ return self
120
+
121
+ def bind(self, var: Variable, node:Any)->Self:
122
+ return replace(self, binding=self.binding.bind(var, node))
115
123
 
116
124
 
117
125
  class Quantifier(Enum):
syncraft/finder.py CHANGED
@@ -6,10 +6,9 @@ from typing import (
6
6
  from dataclasses import dataclass, replace
7
7
  from syncraft.algebra import (
8
8
  Algebra, Either, Right,
9
- OrResult, ManyResult, ThenResult, MarkedResult
10
9
  )
10
+ from syncraft.ast import T, ParseResult, Choice, Many, Then, Marked
11
11
 
12
- from syncraft.ast import T, ParseResult, AST
13
12
  from syncraft.generator import GenState, Generator
14
13
  from sqlglot import TokenType
15
14
  from syncraft.syntax import Syntax
@@ -17,63 +16,42 @@ import re
17
16
 
18
17
 
19
18
  @dataclass(frozen=True)
20
- class Finder(Generator[T]):
21
- def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
22
- assert at_least > 0, "at_least must be greater than 0"
23
- assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
24
- return self.map_state(lambda s: replace(s, is_pruned = False)).many(at_least=at_least, at_most=at_most)
25
-
26
-
27
- def or_else(self, # type: ignore
28
- other: Algebra[ParseResult[T], GenState[T]]
29
- ) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
30
- return self.map_state(lambda s: replace(s, is_pruned = False)).or_else(other)
31
-
32
-
33
- @classmethod
34
- def token(cls,
35
- token_type: Optional[TokenType] = None,
36
- text: Optional[str] = None,
37
- case_sensitive: bool = False,
38
- regex: Optional[re.Pattern[str]] = None
39
- )-> Algebra[ParseResult[T], GenState[T]]:
40
- return super().token(token_type=token_type,
41
- text=text,
42
- case_sensitive=case_sensitive,
43
- regex=regex).map_state(lambda s: replace(s, is_pruned = False)) # type: ignore
44
-
45
-
19
+ class Finder(Generator[T]):
46
20
  @classmethod
47
- def anything(cls)->Algebra[ParseResult[T], GenState[T]]:
48
- def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[T], GenState[T]]]:
49
- wrapper = input.wrapper()
50
- return Right((wrapper(input.focus), input))
21
+ def anything(cls)->Algebra[Any, GenState[T]]:
22
+ def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Any, GenState[T]]]:
23
+ return Right((input.ast, input))
51
24
  return cls(anything_run, name=cls.__name__ + '.anything()')
52
25
 
53
26
 
54
27
 
55
28
  anything = Syntax(lambda cls: cls.factory('anything')).describe(name="anything", fixity='infix')
56
29
 
57
- def matches(syntax: Syntax[Any, Any], data: AST[Any])-> bool:
30
+ def matches(syntax: Syntax[Any, Any], data: ParseResult[T])-> bool:
58
31
  gen = syntax(Finder)
59
- state = GenState.from_ast(data)
32
+ state = GenState.from_ast(ast = data, restore_pruned=True)
60
33
  result = gen.run(state, use_cache=True)
61
34
  return isinstance(result, Right)
62
35
 
63
36
 
64
- def find(syntax: Syntax[Any, Any], data: AST[Any]) -> YieldGen[AST[Any], None, None]:
37
+ def find(syntax: Syntax[Any, Any], data: ParseResult[T]) -> YieldGen[ParseResult[T], None, None]:
65
38
  if matches(syntax, data):
66
39
  yield data
67
- match data.focus:
68
- case ThenResult(left = left, right=right):
69
- yield from find(syntax, AST(left))
70
- yield from find(syntax, AST(right))
71
- case ManyResult(value = value):
40
+ match data:
41
+ case Then(left=left, right=right):
42
+ if left is not None:
43
+ yield from find(syntax, left)
44
+ if right is not None:
45
+ yield from find(syntax, right)
46
+ case Many(value = value):
72
47
  for e in value:
73
- yield from find(syntax, AST(e))
74
- case MarkedResult(value=value):
75
- yield from find(syntax, AST(value))
76
- case OrResult(value=value):
77
- yield from find(syntax, AST(value))
48
+ yield from find(syntax, e)
49
+ case Marked(value=value):
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)
78
56
  case _:
79
57
  pass
syncraft/generator.py CHANGED
@@ -1,17 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import (
4
- Any, TypeVar, Tuple, Optional, Callable, Generic,
4
+ Any, TypeVar, Tuple, Optional, Callable, Generic, cast,
5
5
  List,
6
6
  )
7
7
  from functools import cached_property
8
8
  from dataclasses import dataclass, replace
9
9
  from syncraft.algebra import (
10
- Algebra, Either, Left, Right, Error,
11
- OrResult, ManyResult
10
+ Algebra, Either, Left, Right, Error
12
11
  )
13
12
 
14
- from syncraft.ast import T, ParseResult, AST, Token, TokenSpec, Binding, Variable, Bindable
13
+ from syncraft.ast import (
14
+ T, ParseResult, AST, Token, TokenSpec, Binding, Variable,
15
+ Bindable,
16
+ Choice, Many, ChoiceKind,
17
+ Then, ThenKind, Marked
18
+ )
15
19
 
16
20
  from syncraft.syntax import Syntax
17
21
  from sqlglot import TokenType
@@ -25,78 +29,56 @@ B = TypeVar('B')
25
29
 
26
30
  @dataclass(frozen=True)
27
31
  class GenState(Bindable, Generic[T]):
28
- ast: Optional[AST[T]]
29
- seed: int
30
- is_pruned: Optional[bool] = None
31
- binding: Binding = Binding()
32
- def bind(self, var: Variable, node:ParseResult[T])->GenState[T]:
33
- return replace(self, binding=self.binding.bind(var, node))
34
-
32
+ ast: Optional[ParseResult[T]] = None
33
+ restore_pruned: bool = False
34
+ seed: int = 0
35
+ def map(self, f: Callable[[Any], Any]) -> GenState[T]:
36
+ return replace(self, ast=f(self.ast))
37
+
38
+ def inject(self, a: Any) -> GenState[T]:
39
+ return self.map(lambda _: a)
40
+
35
41
  def fork(self, tag: Any) -> GenState[T]:
36
42
  return replace(self, seed=hash((self.seed, tag)))
37
43
 
38
44
  def rng(self, tag: Any = None) -> random.Random:
39
45
  return random.Random(self.seed if tag is None else hash((self.seed, tag)))
40
46
 
41
- def to_string(self, interested: Callable[[Any], bool]) -> str | None:
42
- return f"GenState(current={self.focus})"
47
+
43
48
 
44
49
  @cached_property
45
50
  def pruned(self)->bool:
46
- if self.is_pruned is None:
47
- return self.ast is None or self.ast.pruned
48
- else:
49
- return self.is_pruned
50
-
51
-
52
- @property
53
- def focus(self) -> Optional[ParseResult[T]]:
54
- if self.ast is None:
55
- return None
56
- return self.ast.focus
57
-
58
- @property
59
- def is_named(self)->bool:
60
- return self.ast is not None and self.ast.is_named()
51
+ return self.ast is None
61
52
 
62
- def wrapper(self)->Callable[[Any], Any]:
63
- if self.ast is not None:
64
- return self.ast.wrapper()
65
- else:
66
- return lambda x: x
67
-
68
53
  def left(self)-> GenState[T]:
69
54
  if self.ast is None:
70
55
  return self
71
- return replace(self, ast=self.ast.left())
56
+ if isinstance(self.ast, Then) and (self.ast.kind != ThenKind.RIGHT or self.restore_pruned):
57
+ return replace(self, ast=self.ast.left)
58
+ return replace(self, ast=None)
72
59
 
73
60
  def right(self) -> GenState[T]:
74
61
  if self.ast is None:
75
62
  return self
76
- return replace(self, ast=self.ast.right())
63
+ if isinstance(self.ast, Then) and (self.ast.kind != ThenKind.LEFT or self.restore_pruned):
64
+ return replace(self, ast=self.ast.right)
65
+ return replace(self, ast=None)
77
66
 
78
67
 
79
68
 
80
69
  def down(self, index: int) -> GenState[T]:
81
70
  if self.ast is None:
82
- return self
83
- return replace(self, ast=self.ast.down(index))
71
+ return self
72
+ match self.ast:
73
+ case Marked(value=value):
74
+ return replace(self, ast=value)
75
+ case _:
76
+ raise TypeError(f"Invalid AST type({self.ast}) for down traversal")
84
77
 
85
- @cached_property
86
- def how_many(self) -> int:
87
- if self.ast is None:
88
- return 0
89
- return self.ast.how_many()
90
-
91
78
  @classmethod
92
- def from_ast(cls, ast: Optional[AST[T]], seed: int = 0) -> GenState[T]:
93
- return cls(ast=ast, seed=seed)
94
-
95
-
96
- @classmethod
97
- def from_parse_result(cls, parse_result: Optional[ParseResult[T]], seed: int = 0) -> GenState[T]:
98
- return cls.from_ast(AST(parse_result) if parse_result else None, seed)
99
-
79
+ def from_ast(cls, *, ast: Optional[ParseResult[T]], seed: int = 0, restore_pruned:bool=False) -> GenState[T]:
80
+ return cls(ast=ast, seed=seed, restore_pruned=restore_pruned)
81
+
100
82
 
101
83
 
102
84
 
@@ -152,10 +134,12 @@ class TokenGen(TokenSpec):
152
134
  @dataclass(frozen=True)
153
135
  class Generator(Algebra[ParseResult[T], GenState[T]]):
154
136
  def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
155
- def flat_map_run(original: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
156
- wrapper = original.wrapper()
157
- input = original if not original.is_named else original.down(0) # If the input is named, we need to go down to the first child
137
+ def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
158
138
  try:
139
+ if not isinstance(input.ast, Then):
140
+ return Left(Error(this=self,
141
+ message=f"Expect Then got {input.ast}",
142
+ state=input))
159
143
  lft = input.left()
160
144
  match self.run(lft, use_cache=use_cache):
161
145
  case Left(error):
@@ -166,47 +150,49 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
166
150
  case Left(e):
167
151
  return Left(e)
168
152
  case Right((result, next_input)):
169
- return Right((wrapper(result), next_input))
153
+ return Right((result, next_input))
170
154
  raise ValueError("flat_map should always return a value or an error.")
171
155
  except Exception as e:
172
156
  return Left(Error(
173
157
  message=str(e),
174
158
  this=self,
175
- state=original,
159
+ state=input,
176
160
  error=e
177
161
  ))
178
162
  return self.__class__(run_f = flat_map_run, name=self.name) # type: ignore
179
163
 
180
164
 
181
- def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
165
+ def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[Many[ParseResult[T]], GenState[T]]:
182
166
  assert at_least > 0, "at_least must be greater than 0"
183
167
  assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
184
- def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ManyResult[ParseResult[T]], GenState[T]]]:
185
- wrapper = input.wrapper()
186
- input = input if not input.is_named else input.down(0) # If the input is named, we need to go down to the first child
168
+ def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[Many[ParseResult[T]], GenState[T]]]:
187
169
  if input.pruned:
188
170
  upper = at_most if at_most is not None else at_least + 2
189
171
  count = input.rng("many").randint(at_least, upper)
190
172
  ret: List[Any] = []
191
173
  for i in range(count):
192
- forked_input = input.down(0).fork(tag=len(ret))
174
+ forked_input = input.fork(tag=len(ret))
193
175
  match self.run(forked_input, use_cache):
194
176
  case Right((value, next_input)):
195
177
  ret.append(value)
196
178
  case Left(_):
197
179
  pass
198
- return Right((wrapper(ManyResult(tuple(ret))), input))
180
+ return Right((Many(value=tuple(ret)), input))
199
181
  else:
182
+ if not isinstance(input.ast, Many):
183
+ return Left(Error(this=self,
184
+ message=f"Expect Many got {input.ast}",
185
+ state=input))
200
186
  ret = []
201
- for index in range(input.how_many):
202
- match self.run(input.down(index), use_cache):
187
+ for x in input.ast.value:
188
+ match self.run(input.inject(x), use_cache):
203
189
  case Right((value, next_input)):
204
190
  ret.append(value)
205
191
  if at_most is not None and len(ret) > at_most:
206
192
  return Left(Error(
207
193
  message=f"Expected at most {at_most} matches, got {len(ret)}",
208
194
  this=self,
209
- state=input.down(index)
195
+ state=input.inject(x)
210
196
  ))
211
197
  case Left(_):
212
198
  pass
@@ -214,36 +200,47 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
214
200
  return Left(Error(
215
201
  message=f"Expected at least {at_least} matches, got {len(ret)}",
216
202
  this=self,
217
- state=input.down(index)
203
+ state=input.inject(x)
218
204
  ))
219
- return Right((wrapper(ManyResult(tuple(ret))), input))
205
+ return Right((Many(value=tuple(ret)), input))
220
206
  return self.__class__(many_run, name=f"many({self.name})") # type: ignore
221
207
 
222
208
 
223
209
  def or_else(self, # type: ignore
224
210
  other: Algebra[ParseResult[T], GenState[T]]
225
- ) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
226
- def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[ParseResult[T]], GenState[T]]]:
227
- wrapper = input.wrapper()
228
- input = input if not input.is_named else input.down(0) # If the input is named, we need to go down to the first child
229
- if input.pruned:
230
- forked_input = input.fork(tag="or_else")
231
- match forked_input.rng("or_else").choice((self, other)).run(forked_input, use_cache):
232
- case Right((value, next_input)):
233
- return Right((wrapper(OrResult(value)), next_input))
234
- case Left(error):
235
- return Left(error)
236
- else:
237
- match self.run(input.down(0), use_cache):
238
- case Right((value, next_input)):
239
- return Right((wrapper(OrResult(value)), next_input))
240
- case Left(error):
241
- match other.run(input.down(0), use_cache):
211
+ ) -> Algebra[Choice[ParseResult[T], ParseResult[T]], GenState[T]]:
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,
214
+ left: GenState[T],
215
+ right: GenState[T])->Either[Any, Tuple[Choice[ParseResult[T], ParseResult[T]], GenState[T]]]:
216
+ match kind:
217
+ case ChoiceKind.LEFT:
218
+ match self.run(left, use_cache):
242
219
  case Right((value, next_input)):
243
- return Right((wrapper(OrResult(value)), next_input))
220
+ return Right((Choice(kind=ChoiceKind.LEFT, left=value, right=None), next_input))
244
221
  case Left(error):
245
222
  return Left(error)
246
- raise ValueError("or_else should always return a value or an error.")
223
+ case ChoiceKind.RIGHT:
224
+ match other.run(right, use_cache):
225
+ case Right((value, next_input)):
226
+ return Right((Choice(kind=ChoiceKind.RIGHT, left=None, right=value), next_input))
227
+ case Left(error):
228
+ return Left(error)
229
+ raise ValueError(f"Invalid ChoiceKind: {kind}")
230
+
231
+ if input.pruned:
232
+ forked_input = input.fork(tag="or_else")
233
+ which = forked_input.rng("or_else").choice((ChoiceKind.LEFT, ChoiceKind.RIGHT))
234
+ return exec(which, forked_input, forked_input)
235
+ 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:
241
+ return Left(Error(this=self,
242
+ message=f"Expect Choice got {input.ast}",
243
+ state=input))
247
244
  return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})") # type: ignore
248
245
 
249
246
  @classmethod
@@ -256,28 +253,29 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
256
253
  gen = TokenGen(token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
257
254
  lazy_self: Algebra[ParseResult[T], GenState[T]]
258
255
  def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[Token], GenState[T]]]:
259
- wrapper = input.wrapper()
260
- input = input if not input.is_named else input.down(0) # If the input is named, we need to go down to the first child
261
256
  if input.pruned:
262
257
  return Right((gen.gen(), input))
263
258
  else:
264
- current = input.focus
259
+ current = input.ast
265
260
  if not isinstance(current, Token) or not gen.is_valid(current):
266
261
  return Left(Error(None,
267
262
  message=f"Expected a Token, but got {type(current)}.",
268
263
  state=input))
269
- return Right((wrapper(current), input))
264
+ return Right((current, input))
270
265
  lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
271
266
  return lazy_self
272
267
 
273
268
 
274
269
 
275
- def generate(syntax: Syntax[Any, Any], data: Optional[AST[Any]] = None, seed: int = 0) -> AST[Any] | Any:
270
+ def generate(syntax: Syntax[Any, Any],
271
+ data: Optional[ParseResult[Any]] = None,
272
+ seed: int = 0,
273
+ restore_pruned: bool = False) -> AST | Any:
276
274
  gen = syntax(Generator)
277
- state = GenState.from_ast(data, seed)
275
+ state = GenState.from_ast(ast=data, seed=seed, restore_pruned=restore_pruned)
278
276
  result = gen.run(state, use_cache=False)
279
277
  if isinstance(result, Right):
280
- return AST(result.value[0])
278
+ return result.value[0]
281
279
  assert isinstance(result, Left), "Generator must return Either[Any, Tuple[Any, Any]]"
282
280
  return result.value
283
281
 
syncraft/parser.py CHANGED
@@ -3,7 +3,7 @@ import re
3
3
  from sqlglot import tokenize, TokenType, Parser as GlotParser, exp
4
4
  from typing import (
5
5
  Optional, List, Any, Tuple,
6
- Generic, Callable, Hashable
6
+ Generic
7
7
  )
8
8
  from syncraft.algebra import (
9
9
  Either, Left, Right, Error, Algebra
@@ -13,16 +13,14 @@ 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, ParseResult, Binding, Variable, Bindable
16
+ from syncraft.ast import Token, TokenSpec, AST, T, Bindable
17
17
 
18
18
 
19
19
  @dataclass(frozen=True)
20
20
  class ParserState(Bindable, Generic[T]):
21
21
  input: Tuple[T, ...] = field(default_factory=tuple)
22
22
  index: int = 0
23
- binding: Binding[ParseResult[T]] = Binding()
24
- def bind(self, var: Variable, node:ParseResult[T])->ParserState[T]:
25
- return replace(self, binding=self.binding.bind(var, node))
23
+
26
24
 
27
25
  def token_sample_string(self)-> str:
28
26
  def encode_tokens(*tokens:T) -> str:
@@ -184,12 +182,12 @@ def sqlglot(parser: Syntax[Any, Any],
184
182
 
185
183
  def parse(syntax: Syntax[Any, Any],
186
184
  sql: str,
187
- dialect: str) -> AST[Any] | Any:
185
+ dialect: str) -> AST | Any:
188
186
  parser = syntax(Parser)
189
187
  input: ParserState[Token] = token_state(sql, dialect=dialect)
190
188
  result = parser.run(input, True)
191
189
  if isinstance(result, Right):
192
- return AST(result.value[0])
190
+ return result.value[0]
193
191
  assert isinstance(result, Left), "Parser must return Either[E, Tuple[A, S]]"
194
192
  return result.value
195
193
 
syncraft/syntax.py CHANGED
@@ -8,7 +8,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 ThenResult, ManyResult, ThenKind, MarkedResult
11
+ from syncraft.ast import Then, ThenKind, Marked
12
12
  from types import MethodType, FunctionType
13
13
 
14
14
 
@@ -27,14 +27,14 @@ class Description:
27
27
  name: Optional[str] = None
28
28
  newline: Optional[str] = None
29
29
  fixity: Literal['infix', 'prefix', 'postfix'] = 'infix'
30
- parameter: List[Any] = field(default_factory=list)
30
+ parameter: Tuple[Any, ...] = field(default_factory=tuple)
31
31
 
32
32
  def update(self,
33
33
  *,
34
34
  newline: Optional[str] = None,
35
35
  name: Optional[str] = None,
36
36
  fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
37
- parameter: Optional[List[Any]] = None) -> 'Description':
37
+ parameter: Optional[Tuple[Any, ...]] = None) -> 'Description':
38
38
  return Description(
39
39
  name=name if name is not None else self.name,
40
40
  newline= newline if newline is not None else self.newline,
@@ -118,7 +118,7 @@ class Syntax(Generic[A, S]):
118
118
  newline: Optional[str] = None,
119
119
  name: Optional[str] = None,
120
120
  fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
121
- parameter: Optional[List[Syntax[Any, S]]] = None) -> Syntax[A, S]:
121
+ parameter: Optional[Tuple[Syntax[Any, S], ...]] = None) -> Syntax[A, S]:
122
122
  return self.__class__(alg=self.alg,
123
123
  meta=self.meta.update(name=name,
124
124
  newline=newline,
@@ -129,11 +129,17 @@ class Syntax(Generic[A, S]):
129
129
  return self.describe(newline=info)
130
130
 
131
131
  def terminal(self, name: str)->Syntax[A, S]:
132
- return self.describe(name=name, fixity='prefix', parameter=[])
132
+ return self.describe(name=name, fixity='prefix')
133
133
 
134
134
  ######################################################## value transformation ########################################################
135
135
  def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
136
136
  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)
137
143
 
138
144
  def map_all(self, f: Callable[[Either[Any, Tuple[A, S]]], Either[Any, Tuple[B, S]]]) -> Syntax[B, S]:
139
145
  return self.__class__(lambda cls: self.alg(cls).map_all(f), meta=self.meta) # type: ignore
@@ -148,30 +154,29 @@ class Syntax(Generic[A, S]):
148
154
  def flat_map(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
149
155
  return self.__class__(lambda cls: self.alg(cls).flat_map(f)) # type: ignore
150
156
 
151
- def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[ManyResult[A], S]:
157
+ def many(self, *, at_least: int = 1, at_most: Optional[int] = None) -> Syntax[Tuple[A, ...], S]:
152
158
  return self.__class__(lambda cls:self.alg(cls).many(at_least=at_least, at_most=at_most)).describe(name='*', # type: ignore
153
159
  fixity='prefix',
154
- parameter=[self])
160
+ parameter=(self,))
155
161
 
156
162
  ################################################ facility combinators ############################################################
157
163
 
158
164
 
159
165
 
160
- def between(self, left: Syntax[Any, S], right: Syntax[Any, S]) -> Syntax[ThenResult[None, ThenResult[A, None]], S]:
166
+ def between(self, left: Syntax[Any, S], right: Syntax[Any, S]) -> Syntax[Then[Any, Then[A, Any]], S]:
161
167
  return left >> self // right
162
168
 
163
- def sep_by(self, sep: Syntax[Any, S]) -> Syntax[ThenResult[A, ManyResult[ThenResult[None, A]]], S]:
164
- return (self + (sep >> self).many()).describe(
165
- name='sep_by',
166
- fixity='prefix',
167
- parameter=[self, sep]
168
- )
169
+ def sep_by(self, sep: Syntax[Any, S]) -> Syntax[Tuple[A, ...], S]:
170
+ return (self + (sep >> self).many().optional()).describe( # type: ignore
171
+ name='sep_by',
172
+ fixity='prefix',
173
+ parameter=(self, sep))
169
174
 
170
175
  def parens(self, sep: Syntax[Any, S], open: Syntax[Any, S], close: Syntax[Any, S]) -> Syntax[Any, S]:
171
176
  return self.sep_by(sep=sep).between(left=open, right=close)
172
177
 
173
178
  def optional(self, default: Optional[B] = None) -> Syntax[Optional[A | B], S]:
174
- return (self | success(default)).describe(name='~', fixity='prefix', parameter=[self])
179
+ return (self | success(default)).describe(name='~', fixity='prefix', parameter=(self,))
175
180
 
176
181
 
177
182
  def cut(self) -> Syntax[A, S]:
@@ -179,45 +184,37 @@ class Syntax(Generic[A, S]):
179
184
 
180
185
 
181
186
  ####################################################### operator overloading #############################################
182
- def __ge__(self, f: Callable[[A], Algebra[B, S]]) -> Syntax[B, S]:
183
- return self.flat_map(f).describe(name='>=', fixity='infix', parameter=[self])
184
-
185
-
186
- def __gt__(self, other: Callable[[A], B])->Syntax[B, S]:
187
- return self.map(other)
188
-
189
-
190
- def __floordiv__(self, other: Syntax[B, S]) -> Syntax[ThenResult[A, None], S]:
187
+ def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A, None], S]:
191
188
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
192
189
  return self.__class__(
193
190
  lambda cls: self.alg(cls).then_left(other.alg(cls)) # type: ignore
194
- ).describe(name=ThenKind.LEFT.value, fixity='infix', parameter=[self, other]).as_(Syntax[ThenResult[A, None], S])
191
+ ).describe(name=ThenKind.LEFT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[A, None], S])
195
192
 
196
- def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[ThenResult[B, None], S]:
193
+ def __rfloordiv__(self, other: Syntax[B, S]) -> Syntax[Then[B, None], S]:
197
194
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
198
195
  return other.__floordiv__(self)
199
196
 
200
197
  def __invert__(self) -> Syntax[A | None, S]:
201
198
  return self.optional()
202
199
 
203
- def __radd__(self, other: Syntax[B, S]) -> Syntax[ThenResult[B, A], S]:
200
+ def __radd__(self, other: Syntax[B, S]) -> Syntax[Then[B, A], S]:
204
201
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
205
202
  return other.__add__(self)
206
203
 
207
- def __add__(self, other: Syntax[B, S]) -> Syntax[ThenResult[A, B], S]:
204
+ def __add__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
208
205
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
209
206
  return self.__class__(
210
207
  lambda cls: self.alg(cls).then_both(other.alg(cls)) # type: ignore
211
- ).describe(name=ThenKind.BOTH.value, fixity='infix', parameter=[self, other])
208
+ ).describe(name=ThenKind.BOTH.value, fixity='infix', parameter=(self, other))
212
209
 
213
- def __rshift__(self, other: Syntax[B, S]) -> Syntax[ThenResult[None, B], S]:
210
+ def __rshift__(self, other: Syntax[B, S]) -> Syntax[Then[None, B], S]:
214
211
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
215
212
  return self.__class__(
216
213
  lambda cls: self.alg(cls).then_right(other.alg(cls)) # type: ignore
217
- ).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=[self, other]).as_(Syntax[ThenResult[None, B], S])
214
+ ).describe(name=ThenKind.RIGHT.value, fixity='infix', parameter=(self, other)).as_(Syntax[Then[None, B], S])
218
215
 
219
216
 
220
- def __rrshift__(self, other: Syntax[B, S]) -> Syntax[ThenResult[None, A], S]:
217
+ def __rrshift__(self, other: Syntax[B, S]) -> Syntax[Then[None, A], S]:
221
218
  other = other if isinstance(other, Syntax) else self.lift(other).as_(Syntax[B, S])
222
219
  return other.__rshift__(self)
223
220
 
@@ -239,15 +236,20 @@ class Syntax(Generic[A, S]):
239
236
  value, state = result.value
240
237
  return Right((value, state.bind(var, value)))
241
238
  return result
242
- return self.map_all(bind_v).describe(name=f'bind({var.name})', fixity='postfix', parameter=[self])
239
+
240
+ ret = self.mark(var.name).map_all(bind_v) if var.name else self.map_all(bind_v) # type: ignore
241
+ return ret.describe(name=f'bind({var.name})', fixity='postfix', parameter=(self,))
243
242
 
244
- def mark(self, var: str) -> Syntax[MarkedResult[A], S]:
245
- def bind_s(value: A) -> MarkedResult[A]:
246
- if isinstance(value, MarkedResult):
243
+ def mark(self, var: str) -> Syntax[A, S]:
244
+ def bind_s(value: A) -> Marked[A]:
245
+ if isinstance(value, Marked):
247
246
  return replace(value, name=var)
248
247
  else:
249
- return MarkedResult(name=var, value=value)
250
- return self.map(bind_s).describe(name=f'bind("{var}")', fixity='postfix', parameter=[self])
248
+ return Marked(name=var, value=value)
249
+ def ibind_s(m : Marked[A]) -> A:
250
+ return m.value if isinstance(m, Marked) else m
251
+
252
+ return self.bimap(bind_s, ibind_s).describe(name=f'bind("{var}")', fixity='postfix', parameter=(self,))
251
253
 
252
254
 
253
255
 
@@ -279,7 +281,7 @@ def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
279
281
  return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(None)
280
282
 
281
283
 
282
- def all(*parsers: Syntax[Any, S]) -> Syntax[ThenResult[Any, Any], S]:
284
+ def all(*parsers: Syntax[Any, S]) -> Syntax[Then[Any, Any], S]:
283
285
  return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(None)
284
286
 
285
287
  def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.24
3
+ Version: 0.1.26
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -31,7 +31,7 @@ pip install syncraft
31
31
 
32
32
 
33
33
  ## TODO
34
- - [ ] Add a collect method to AST to collect all named entries and pack them into a dict or a custom dataclass. This method will be called as the last step of my current bimap. So it shares the signature of bimap and can combine with the current bimap
35
- - [ ] Amend all, first, last, and named helper functions to support bimap and named results.
34
+ - [ ] simplify the result of then_left and then_right by bimap the result in syntax.
35
+ - [ ] simplify the result of sep_by and between by bimap the result in syntax
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=FBy0cCl4RGnsi1uxoIIZPRcHgfHxWwUal9iLV_Mtc6I,14791
3
+ syncraft/ast.py,sha256=WwkA02H00BUNrotsMbvypzqHzJb8HiWROVb3qBDSR-8,5113
4
+ syncraft/constraint.py,sha256=uT-ELzvv8J-s-Y1VYkXePUezseqCLhqzYUzFBYs0HsE,6418
5
+ syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
6
+ syncraft/finder.py,sha256=qrBlPzQ3hK0rsLIfTD0QdypbrPegDKKCMZriWu-qHBA,1870
7
+ syncraft/generator.py,sha256=BiwwFTjPkk7itXK1wAbB7SJhHi8npd0Wx_3bxR6t1Ww,11775
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=aMXnAuGD12_WQxv0TIEvVw-7EO8Bupv0AD1hy6nMJMw,15011
12
+ syncraft-0.1.26.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
13
+ syncraft-0.1.26.dist-info/METADATA,sha256=h5K3SkaNZtfxw3hD5uc7Ac3D92XJMYCll8cMigf8rvs,1199
14
+ syncraft-0.1.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ syncraft-0.1.26.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
16
+ syncraft-0.1.26.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- syncraft/algebra.py,sha256=Um8iMgVwlWcseQDkB_GqY-xmo0hR7zSw5j7oRRxrZhE,14122
3
- syncraft/ast.py,sha256=TM18FkdFIBbxaaKgEvTvellrcXdkzMLLjLm7sRuC7FY,12985
4
- syncraft/constraint.py,sha256=4io1HM_2y5SJzMoeVowAr0a-SDE2jR-7uhCcZpa_FlM,6219
5
- syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
6
- syncraft/finder.py,sha256=QySZcIjWZFMbRxGJVlJDxK9cmIuNA5vnoE-vYwLJiqA,3005
7
- syncraft/generator.py,sha256=6jfkSODx952OiN-oOJdNubvU2TzYhLzh3wibZV-ImbI,11625
8
- syncraft/parser.py,sha256=nSQInixB4j7H22HV_9YdOXfRB5aCPnhQYvMC_N5jnlo,11538
9
- syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
11
- syncraft/syntax.py,sha256=wwMPAfae5mH4ip3Q2JOkvFkccPfoVDkb-Tk5o0U-ecI,14843
12
- syncraft-0.1.24.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
13
- syncraft-0.1.24.dist-info/METADATA,sha256=jleKB866QNoRRkpcmtNflWRHUS4HgNYqQsg9zfJHd4Q,1381
14
- syncraft-0.1.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- syncraft-0.1.24.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
16
- syncraft-0.1.24.dist-info/RECORD,,