syncraft 0.2.8__py3-none-any.whl → 0.2.10__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.
syncraft/algebra.py CHANGED
@@ -8,6 +8,7 @@ from dataclasses import dataclass, replace
8
8
  from syncraft.ast import ThenKind, Then, Choice, Many, ChoiceKind, shallow_dict, SyncraftError
9
9
  from syncraft.cache import Cache
10
10
  from syncraft.constraint import Bindable
11
+ from functools import cached_property
11
12
  from rich import print
12
13
 
13
14
 
@@ -76,16 +77,22 @@ class Error:
76
77
  class Algebra(Generic[A, S]):
77
78
  ######################################################## shared among all subclasses ########################################################
78
79
  run_f: Callable[[S, bool], Generator[Incomplete[S], S, Either[Any, Tuple[A, S]]]]
79
- name: Hashable
80
+ name: str
80
81
  cache: Cache[Either[Any, Tuple[A, S]]]
82
+
81
83
 
82
84
  @classmethod
83
85
  def state(cls, **kwargs:Any)->Optional[S]:
84
86
  return None
85
87
 
86
- def named(self, name: Hashable) -> 'Algebra[A, S]':
88
+ def named(self, name: str) -> Algebra[A, S]:
87
89
  return replace(self, name=name)
88
-
90
+
91
+ @cached_property
92
+ def hashable(self)->Hashable:
93
+ return frozenset({'name': self.name, 'run_f': self.run_f})
94
+
95
+
89
96
  def __call__(self, input: S, use_cache: bool) -> Generator[Incomplete[S], S, Either[Any, Tuple[A, S]]]:
90
97
  return self.run(input, use_cache=use_cache)
91
98
 
@@ -366,7 +373,7 @@ class Algebra(Generic[A, S]):
366
373
  def map_all_run_f(input:S, use_cache:bool) -> Generator[Incomplete[S], S, Either[Any, Tuple[B, S]]]:
367
374
  yield from ()
368
375
  return Right(f(a, input))
369
- return self.__class__(map_all_run_f, name=self.name, cache=self.cache) # type: ignore
376
+ return self.__class__(map_all_run_f, name=self.name, cache=self.cache) # type: ignore
370
377
  return self.flat_map(map_all_f)
371
378
 
372
379
 
syncraft/ast.py CHANGED
@@ -5,11 +5,11 @@ import re
5
5
  from typing import (
6
6
  Optional, Any, TypeVar, Tuple, runtime_checkable, cast,
7
7
  Generic, Callable, Union, Protocol, Type, List, ClassVar,
8
- Dict
8
+ Dict, Hashable
9
9
  )
10
10
 
11
11
 
12
- from dataclasses import dataclass, replace, is_dataclass, fields
12
+ from dataclasses import dataclass, replace, is_dataclass, fields, field
13
13
  from enum import Enum
14
14
 
15
15
 
@@ -480,26 +480,51 @@ T = TypeVar('T', bound=TokenProtocol)
480
480
 
481
481
  @dataclass(frozen=True)
482
482
  class SyntaxSpec:
483
- pass
484
- @dataclass(frozen=True)
485
- class ChoiceSpec(SyntaxSpec, Generic[A, B]):
486
- left: A
487
- right: B
483
+ id: int = field(init=False)
484
+ def __post_init__(self) -> None:
485
+ object.__setattr__(self, 'id', id(self))
488
486
 
489
487
  @dataclass(frozen=True)
488
+ class RefSpec(SyntaxSpec, Generic[A]):
489
+ ref: int | A
490
+ referent: str
491
+ @dataclass(frozen=True)
490
492
  class LazySpec(SyntaxSpec, Generic[A]):
491
- value: A
493
+ name: str
494
+ value: None | A
492
495
  @dataclass(frozen=True)
493
496
  class ThenSpec(SyntaxSpec, Generic[A, B]):
497
+ name: str
494
498
  kind: ThenKind
495
499
  left: A
496
500
  right: B
501
+ # def __repr__(self) -> str:
502
+ # return f"ThenSpec(id={id(self)}, kind={self.kind}, left={self.left}, right={self.right})"
503
+ # def __str__(self) -> str:
504
+ # return self.__repr__()
505
+
506
+ @dataclass(frozen=True)
507
+ class ChoiceSpec(SyntaxSpec, Generic[A, B]):
508
+ name: str
509
+ left: A
510
+ right: B
511
+
512
+ # def __repr__(self) -> str:
513
+ # return f"ChoiceSpec(id={id(self)}, left={self.left}, right={self.right})"
514
+ # def __str__(self) -> str:
515
+ # return self.__repr__()
497
516
 
498
517
  @dataclass(frozen=True)
499
518
  class ManySpec(SyntaxSpec, Generic[A]):
519
+ name: str
500
520
  value: A
501
521
  at_least: int
502
522
  at_most: Optional[int]
523
+ # def __repr__(self) -> str:
524
+ # return f"ManySpec(id={id(self)}, value={self.value}, at_least={self.at_least}, at_most={self.at_most})"
525
+ # def __str__(self) -> str:
526
+ # return self.__repr__()
527
+
503
528
 
504
529
 
505
530
  @dataclass(frozen=True)
@@ -509,6 +534,19 @@ class TokenSpec(SyntaxSpec):
509
534
  case_sensitive: bool = False
510
535
  regex: Optional[re.Pattern[str]] = None
511
536
 
537
+ def __repr__(self) -> str:
538
+ parts = []
539
+ if self.token_type is not None:
540
+ parts.append(f"type={self.token_type.name}")
541
+ if self.text is not None:
542
+ parts.append(f"text={'`'+self.text+'`' if self.case_sensitive else self.text}")
543
+ if self.regex is not None:
544
+ parts.append(f"regex=/{self.regex.pattern}/")
545
+ return f"TokenSpec(id={id(self)}, " + ", ".join(parts) + ")"
546
+
547
+ def __str__(self) -> str:
548
+ return self.__repr__()
549
+
512
550
  @classmethod
513
551
  def create(cls,
514
552
  *,
syncraft/cache.py CHANGED
@@ -70,7 +70,7 @@ class Cache(Generic[Ret]):
70
70
  if key in c:
71
71
  v = c[key]
72
72
  if isinstance(v, InProgress):
73
- raise RecursionError("Left-recursion detected in parser", offending=f, state=args)
73
+ raise RecursionError(f"Left-recursion detected in Algebra {f}", offending=f, state=args)
74
74
  else:
75
75
  return v
76
76
  try:
@@ -98,7 +98,7 @@ class Cache(Generic[Ret]):
98
98
  if key in c:
99
99
  v = c[key]
100
100
  if isinstance(v, InProgress):
101
- raise RecursionError("Left-recursion detected in parser", offending=f, state=args)
101
+ raise RecursionError(f"Left-recursion detected in Algebra {f}", offending=f, state=args)
102
102
  else:
103
103
  return v
104
104
  try:
syncraft/syntax.py CHANGED
@@ -107,7 +107,7 @@ class Syntax(Generic[A, S]):
107
107
  *,
108
108
  name: Optional[str] = None,
109
109
  fixity: Optional[Literal['infix', 'prefix', 'postfix']] = None,
110
- parameter: Optional[Tuple[Syntax[Any, S], ...]] = None,
110
+ parameter: Optional[Tuple[Syntax[Any, S] | Any, ...]] = None,
111
111
  ) -> Syntax[A, S]:
112
112
  return self.__class__(
113
113
  alg=self.alg,
@@ -116,7 +116,16 @@ class Syntax(Generic[A, S]):
116
116
  ),
117
117
  )
118
118
 
119
+ def named(self, name: str) -> Syntax[A, S]:
120
+ """Assign a name to this syntax node for better debugging output.
119
121
 
122
+ Args:
123
+ name: Name to assign; must be a valid identifier.
124
+
125
+ Returns:
126
+ Syntax with the given name.
127
+ """
128
+ return self.__class__(lambda cls, cache: self(cls, cache).named(name), meta=self.meta)
120
129
  ######################################################## value transformation ########################################################
121
130
  def map(self, f: Callable[[A], B]) -> Syntax[B, S]:
122
131
  """Map the produced value while preserving state and metadata.
@@ -219,7 +228,7 @@ class Syntax(Generic[A, S]):
219
228
  Returns:
220
229
  Syntax producing nested Then with all parts.
221
230
  """
222
- return left >> self // right
231
+ return (left >> self // right).describe(name='between', fixity='postfix', parameter=(self, left, right))
223
232
 
224
233
  def sep_by(self, sep: Syntax[B, S]) -> Syntax[Then[A, Choice[Many[Then[B, A]], Optional[Nothing]]], S]:
225
234
  """Parse one or more items separated by sep.
@@ -316,7 +325,7 @@ class Syntax(Generic[A, S]):
316
325
  Returns:
317
326
  Syntax that marks downstream failures as committed.
318
327
  """
319
- return self.__class__(lambda cls, cache: self(cls, cache).cut())
328
+ return self.__class__(lambda cls, cache: self(cls, cache).cut()).describe(name='cut', fixity='postfix', parameter=(self,))
320
329
 
321
330
  ###################################################### operator overloading #############################################
322
331
  def __floordiv__(self, other: Syntax[B, S]) -> Syntax[Then[A, B], S]:
@@ -433,7 +442,7 @@ class Syntax(Generic[A, S]):
433
442
  else:
434
443
  return v, s
435
444
 
436
- return self.map_all(bind_v).describe(name=f'bind({name})', fixity='postfix', parameter=(self,))
445
+ return self.map_all(bind_v).describe(name='bind', fixity='infix', parameter=(self, name))
437
446
 
438
447
  def to(self, f: Collector[E]) -> Syntax[Collect[A, E], S]:
439
448
  """Attach a collector to the produced value.
@@ -457,7 +466,7 @@ class Syntax(Generic[A, S]):
457
466
  def ito_f(c: Collect[A, E]) -> A:
458
467
  return c.value if isinstance(c, Collect) else c
459
468
 
460
- return self.bimap(to_f, ito_f).describe(name=f'to({f})', fixity='postfix', parameter=(self,))
469
+ return self.bimap(to_f, ito_f).describe(name='to', fixity='infix', parameter=(self, f))
461
470
 
462
471
  def mark(self, name: str) -> Syntax[Marked[A], S]:
463
472
  """Mark the produced value with a name.
@@ -481,7 +490,7 @@ class Syntax(Generic[A, S]):
481
490
  def imark_s(m: Marked[A]) -> A:
482
491
  return m.value if isinstance(m, Marked) else m
483
492
 
484
- return self.bimap(mark_s, imark_s).describe(name=f'mark("{name}")', fixity='postfix', parameter=(self,))
493
+ return self.bimap(mark_s, imark_s).describe(name='mark', fixity='infix', parameter=(self, name))
485
494
 
486
495
  def when(f: Callable[[], bool], then: Syntax[A, S], otherwise: Syntax[B, S]) -> Syntax[A | B, S]:
487
496
  """
@@ -495,7 +504,7 @@ def when(f: Callable[[], bool], then: Syntax[A, S], otherwise: Syntax[B, S]) ->
495
504
  Returns:
496
505
  A Syntax object representing the chosen branch.
497
506
  """
498
- return lazy(lambda: then if f() else otherwise).describe(name='when(?)', fixity='postfix', parameter=(then, otherwise)) # type: ignore
507
+ return lazy(lambda: then if f() else otherwise).describe(name='when', fixity='postfix', parameter=(then, otherwise)) # type: ignore
499
508
 
500
509
  def fail(error: Any) -> Syntax[Any, Any]:
501
510
  """
@@ -507,7 +516,7 @@ def fail(error: Any) -> Syntax[Any, Any]:
507
516
  Returns:
508
517
  A Syntax object that always fails.
509
518
  """
510
- return Syntax(lambda alg, cache: alg.fail(error, cache=cache)).describe(name=f'fail({error})', fixity='prefix')
519
+ return Syntax(lambda alg, cache: alg.fail(error, cache=cache)).describe(name='fail', fixity='prefix', parameter=(error,))
511
520
 
512
521
  def success(value: Any) -> Syntax[Any, Any]:
513
522
  """
@@ -519,7 +528,7 @@ def success(value: Any) -> Syntax[Any, Any]:
519
528
  Returns:
520
529
  A Syntax object that always succeeds.
521
530
  """
522
- return Syntax(lambda alg, cache: alg.success(value, cache=cache)).describe(name=f'success({value})', fixity='prefix')
531
+ return Syntax(lambda alg, cache: alg.success(value, cache=cache)).describe(name='success', fixity='prefix', parameter=(value,))
523
532
 
524
533
 
525
534
 
@@ -581,7 +590,7 @@ def lazy(thunk: Callable[[], Syntax[A, S]]) -> Syntax[A, S]:
581
590
  algebra = cls.lazy(algebra_lazy_f, cache=cache)
582
591
  previous_cls = cls
583
592
  return algebra
584
- return Syntax(syntax_lazy_run).describe(name='lazy(?)', fixity='postfix')
593
+ return Syntax(syntax_lazy_run).describe(name='lazy', fixity='prefix', parameter=(lambda: syntax,))
585
594
 
586
595
 
587
596
 
syncraft/walker.py CHANGED
@@ -7,13 +7,13 @@ from dataclasses import dataclass, replace, field
7
7
  from syncraft.algebra import (
8
8
  Algebra, Either, Right, Incomplete, Left, SyncraftError
9
9
  )
10
- from syncraft.ast import TokenSpec, ThenSpec, ManySpec, ChoiceSpec, LazySpec, ThenKind
10
+ from syncraft.ast import TokenSpec, ThenSpec, ManySpec, ChoiceSpec, LazySpec, ThenKind, RefSpec
11
11
  from syncraft.parser import TokenType
12
12
  from syncraft.constraint import Bindable, FrozenDict
13
13
 
14
14
  import re
15
15
  from syncraft.syntax import Syntax
16
- from syncraft.cache import Cache
16
+ from syncraft.cache import Cache, RecursionError
17
17
  from rich import print
18
18
 
19
19
 
@@ -23,37 +23,51 @@ B = TypeVar('B')
23
23
  SS = TypeVar('SS', bound=Hashable)
24
24
 
25
25
 
26
-
27
-
28
-
29
-
30
-
31
26
  @dataclass(frozen=True)
32
- class WalkerState(Bindable, Generic[SS]):
33
- reducer: Optional[Callable[[Any, SS], SS]] = None
34
- acc: Optional[SS] = None
27
+ class WalkerState(Bindable):
28
+ visited: FrozenDict = field(default_factory=FrozenDict)
29
+ def visit(self, algebra: Algebra, data: Hashable) -> WalkerState:
30
+ return replace(self, visited=self.visited | FrozenDict({algebra.hashable: data}))
35
31
 
36
-
37
- def reduce(self, value: Any) -> WalkerState[SS]:
38
- if self.reducer:
39
- new_acc = self.reducer(value, self.acc) if self.acc is not None else value
40
- return replace(self, acc=new_acc)
41
- else:
42
- return replace(self, acc=value)
43
32
 
44
33
 
45
-
46
-
47
-
48
34
  @dataclass(frozen=True)
49
- class Walker(Algebra[SS, WalkerState[SS]]):
35
+ class Walker(Algebra[Any, WalkerState]):
50
36
  @classmethod
51
- def state(cls, reducer: Callable[[Any, SS], SS], init: SS )->WalkerState[SS]: # type: ignore
52
- assert callable(reducer), f"reducer must be a Reducer or None, got {type(reducer)}"
53
- return WalkerState(reducer=reducer, acc=init)
37
+ def state(cls, **kwargs: Any)->WalkerState:
38
+ return WalkerState()
54
39
 
40
+ def map(self, f: Callable[[Any], Any]) -> Algebra[Any, WalkerState]:
41
+ return self
55
42
 
43
+ def map_state(self, f: Callable[[WalkerState], WalkerState]) -> Algebra[Any, WalkerState]:
44
+ return self
45
+
46
+ def bimap(self, f: Callable[[Any], Any], g: Callable[[Any], Any]) -> Algebra[Any, WalkerState]:
47
+ return self
56
48
 
49
+ def map_all(self, f: Callable[[Any, WalkerState], Tuple[Any, WalkerState]]) -> Algebra[Any, WalkerState]:
50
+ return self
51
+
52
+ def flat_map(self, f: Callable[[Any], Algebra[Any, WalkerState]]) -> Algebra[Any, WalkerState]:
53
+ return self
54
+
55
+ def run(self,
56
+ input: WalkerState,
57
+ use_cache: bool = True
58
+ ) -> PyGenerator[Incomplete[WalkerState], WalkerState, Either[Any, Tuple[Any, WalkerState]]]:
59
+ try:
60
+ if self.hashable in input.visited:
61
+ ref = input.visited[self.hashable]
62
+ return Right((RefSpec(ref=id(ref), referent= '' if not hasattr(ref, 'name') else ref.name), input))
63
+ return (yield from self.cache.gen(self.run_f, input, use_cache))
64
+ except RecursionError as e:
65
+ if self.hashable in input.visited:
66
+ ref = input.visited[self.hashable]
67
+ return Right((RefSpec(ref=id(ref), referent= '' if not hasattr(ref, 'name') else ref.name), input))
68
+ else:
69
+ return Right((RefSpec(ref=e.offending, referent=''), input))
70
+
57
71
  @classmethod
58
72
  def token(cls,
59
73
  *,
@@ -62,82 +76,114 @@ class Walker(Algebra[SS, WalkerState[SS]]):
62
76
  text: Optional[str] = None,
63
77
  case_sensitive: bool = False,
64
78
  regex: Optional[re.Pattern[str]] = None
65
- )-> Algebra[Any, WalkerState[SS]]:
66
- def token_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
79
+ )-> Algebra[Any, WalkerState]:
80
+ name = f'Token({token_type or text or (regex.pattern if regex else "")})'
81
+ this: None | Algebra[Any, WalkerState] = None
82
+ def token_run(input: WalkerState, use_cache:bool) -> PyGenerator[Incomplete[WalkerState], WalkerState, Either[Any, Tuple[Any, WalkerState]]]:
67
83
  yield from ()
68
84
  data = TokenSpec(token_type=token_type, text=text, regex=regex, case_sensitive=case_sensitive)
69
- return Right((data, input.reduce(data)))
70
- return cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})', cache=cache)
85
+ return Right((data, input.visit(this, data) if this is not None else input))
86
+ this = cls(token_run, name=name, cache=cache)
87
+ return this
71
88
 
72
89
  @classmethod
73
90
  def lazy(cls,
74
- thunk: Callable[[], Algebra[Any, WalkerState[SS]]],
75
- cache: Cache) -> Algebra[Any, WalkerState[SS]]:
76
- def algebra_lazy_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
91
+ thunk: Callable[[], Algebra[Any, WalkerState]],
92
+ cache: Cache) -> Algebra[Any, WalkerState]:
93
+ this: None | Algebra[Any, WalkerState] = None
94
+ def algebra_lazy_run(input: WalkerState, use_cache:bool) -> PyGenerator[Incomplete[WalkerState], WalkerState, Either[Any, Tuple[Any, WalkerState]]]:
77
95
  alg = thunk()
78
- print('--' * 20, "Walker.lazy.algebra_lazy_run", '--' * 20)
79
- print('thunk', thunk, id(thunk))
80
- print('input', input, id(input))
81
- print('alg', alg, id(alg))
82
96
  thunk_result = yield from alg.run(input, use_cache)
83
97
  match thunk_result:
84
98
  case Right((value, from_thunk)):
85
- data = LazySpec(value=value)
86
- return Right((data, from_thunk.reduce(data)))
99
+ data: LazySpec[Any] = LazySpec(name=alg.name, value=value)
100
+ from_thunk = from_thunk.visit(alg, value)
101
+ return Right((data, from_thunk.visit(this, data) if this is not None else from_thunk))
87
102
  raise SyncraftError("flat_map should always return a value or an error.", offending=thunk_result, expect=(Left, Right))
88
- return cls(algebra_lazy_run, name=cls.__name__ + '.lazy', cache=cache)
89
-
103
+ this = cls(algebra_lazy_run, name='lazy(?)', cache=cache)
104
+ return this
90
105
 
91
106
 
92
- def then_both(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
93
- def then_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
107
+ def then_all(self, other: Algebra[Any, WalkerState], kind: ThenKind) -> Algebra[Any, WalkerState]:
108
+ name = f"{self.name} {kind.value} {other.name}"
109
+ this: None | Algebra[Any, WalkerState] = None
110
+ def then_run(input: WalkerState, use_cache:bool) -> PyGenerator[Incomplete[WalkerState], WalkerState, Either[Any, Tuple[Any, WalkerState]]]:
94
111
  self_result = yield from self.run(input, use_cache=use_cache)
95
112
  match self_result:
96
113
  case Right((value, from_left)):
97
- other_result = yield from other.run(from_left, use_cache)
114
+ other_result = yield from other.run(from_left.visit(self, value), use_cache)
98
115
  match other_result:
99
116
  case Right((result, from_right)):
100
- data = ThenSpec(kind=ThenKind.BOTH, left=value, right=result)
101
- return Right((data, from_right.reduce(data)))
117
+ data = ThenSpec(name=name, kind=kind, left=value, right=result)
118
+ from_right = from_right.visit(other, result)
119
+ return Right((data, from_right.visit(this, data) if this is not None else from_right))
102
120
  raise SyncraftError("flat_map should always return a value or an error.", offending=self_result, expect=(Left, Right))
103
- return self.__class__(then_run, name=self.name, cache=self.cache | other.cache)
121
+ this = self.__class__(then_run, name=name, cache=self.cache | other.cache)
122
+ return this
104
123
 
105
- def then_left(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
106
- return self.then_both(other).map(lambda t: replace(t, kind=ThenKind.LEFT))
107
124
 
108
- def then_right(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
109
- return self.then_both(other).map(lambda t: replace(t, kind=ThenKind.RIGHT))
110
125
 
126
+ def then_both(self, other: Algebra[Any, WalkerState]) -> Algebra[Any, WalkerState]:
127
+ return self.then_all(other, ThenKind.BOTH)
111
128
 
112
- def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[Any, WalkerState[SS]]:
129
+ def then_left(self, other: Algebra[Any, WalkerState]) -> Algebra[Any, WalkerState]:
130
+ return self.then_all(other, ThenKind.LEFT)
131
+
132
+ def then_right(self, other: Algebra[Any, WalkerState]) -> Algebra[Any, WalkerState]:
133
+ return self.then_all(other, ThenKind.RIGHT)
134
+
135
+
136
+ def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[Any, WalkerState]:
113
137
  if at_least <=0 or (at_most is not None and at_most < at_least):
114
138
  raise SyncraftError(f"Invalid arguments for many: at_least={at_least}, at_most={at_most}", offending=(at_least, at_most), expect="at_least>0 and (at_most is None or at_most>=at_least)")
115
- def many_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
139
+ this: None | Algebra[Any, WalkerState] = None
140
+ def many_run(input: WalkerState, use_cache:bool) -> PyGenerator[Incomplete[WalkerState], WalkerState, Either[Any, Tuple[Any, WalkerState]]]:
116
141
  self_result = yield from self.run(input, use_cache)
117
142
  match self_result:
118
143
  case Right((value, from_self)):
119
- data = ManySpec(value=value, at_least=at_least, at_most=at_most)
120
- return Right((data, from_self.reduce(data)))
144
+ data = ManySpec(name=f"*({self.name})", value=value, at_least=at_least, at_most=at_most)
145
+ from_self = from_self.visit(self, value)
146
+ return Right((data, from_self.visit(this, data) if this is not None else from_self))
121
147
  raise SyncraftError("many should always return a value or an error.", offending=self_result, expect=(Left, Right))
122
- return self.__class__(many_run, name=f"many({self.name})", cache=self.cache)
148
+ this = self.__class__(many_run, name=self.name, cache=self.cache)
149
+ return this
123
150
 
124
151
 
125
- def or_else(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
126
- def or_else_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
152
+ def or_else(self, other: Algebra[Any, WalkerState]) -> Algebra[Any, WalkerState]:
153
+ pattern = re.compile(r'\s')
154
+ self_name = self.name.strip()
155
+ self_name = f"({self_name})" if bool(pattern.search(self_name)) else self_name
156
+ other_name = other.name.strip()
157
+ other_name = f"({other_name})" if bool(pattern.search(other_name)) else other_name
158
+ name = f"{self_name} | {other_name}"
159
+ this: None | Algebra[Any, WalkerState] = None
160
+ def or_else_run(input: WalkerState, use_cache:bool) -> PyGenerator[Incomplete[WalkerState], WalkerState, Either[Any, Tuple[Any, WalkerState]]]:
127
161
  self_result = yield from self.run(input, use_cache=use_cache)
128
162
  match self_result:
129
163
  case Right((value, from_left)):
130
- other_result = yield from other.run(from_left, use_cache)
164
+ other_result = yield from other.run(from_left.visit(self, value), use_cache)
131
165
  match other_result:
132
166
  case Right((result, from_right)):
133
- data = ChoiceSpec(left=value, right=result)
134
- return Right((data, from_right.reduce(data)))
167
+ data = ChoiceSpec(name=name, left=value, right=result)
168
+ from_right = from_right.visit(other, result)
169
+ return Right((data, from_right.visit(this, data) if this is not None else from_right))
135
170
  raise SyncraftError("", offending=self)
136
- return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})", cache=self.cache | other.cache)
171
+ this = self.__class__(or_else_run, name=name, cache=self.cache | other.cache)
172
+ return this
137
173
 
138
174
 
139
175
 
140
- def walk(syntax: Syntax[Any, Any], reducer: Callable[[Any, Any], SS], init: SS)-> Tuple[Any, None | SS]:
176
+ def walk(syntax: Syntax[Any, Any], reducer: Optional[Callable[[Any, Any], SS]] = None, init: Optional[SS] = None) -> Any:
141
177
  from syncraft.syntax import run
142
- v, s = run(syntax=syntax, alg=Walker, use_cache=False, reducer=reducer, init=init)
143
- return v, s
178
+ v, s = run(syntax=syntax,
179
+ alg=Walker,
180
+ use_cache=True,
181
+ reducer=reducer or (lambda a, s: s),
182
+ init=init)
183
+ if reducer is None:
184
+ return v
185
+ else:
186
+ if s is not None:
187
+ return s.acc
188
+ else:
189
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -0,0 +1,20 @@
1
+ syncraft/__init__.py,sha256=g1Rd3DwPJ-Z7dL0PBNyNkcLBmpkp8bIBDWabkeBCD48,1329
2
+ syncraft/algebra.py,sha256=kgJWXxPX1ehdTmmc0nHUKXeC_58MyaUAZ-xBsQoisN8,20857
3
+ syncraft/ast.py,sha256=IAPW4unDl8LWQp1I5BhNmgFUsMRjz1HuiHlor5Y0wBY,22191
4
+ syncraft/cache.py,sha256=imp3LktzdYUaHfen4MELtJg_a5n-PW1Yx7rgfb51b0E,4839
5
+ syncraft/constraint.py,sha256=a6j_VafRor8W7FBXh20DoVnIUO9nfn7eIValigCf9lU,16318
6
+ syncraft/dev.py,sha256=v7jdb2aOVCGbio-Jw14tRhO09FkhWc0vrDdIkIKPu2Y,186
7
+ syncraft/finder.py,sha256=Mv9BYrsDjjq62Z4NO3gElZAbkEgSBINhSbqFbKEQW4Q,4347
8
+ syncraft/generator.py,sha256=2q_dRKZlPLdfyrwdZqL01KpNes00qLKm-D13OWzN79I,21232
9
+ syncraft/lexer.py,sha256=npPHAEEbBRQYcFHPMHtXmiVLbCZAnjpTtj3cnVui4W0,3720
10
+ syncraft/parser.py,sha256=bdCbbeJ5p-j8kJniqmKSRAE8N3pCWN-ltJIpALW0h54,9070
11
+ syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
13
+ syncraft/syntax.py,sha256=YzugKKEttL7wFB3HrJc9IMOFQ3_MQcpHYnBivnB8Zo4,25321
14
+ syncraft/utils.py,sha256=2V446Il2q4mR7bwwGX5_qruJPU0WXU2PniXEjl6EOPE,8746
15
+ syncraft/walker.py,sha256=2aBcKHHcBAyyg_hvlDXkfqKqFjLjVbB_5RHG8Y_AYe4,9254
16
+ syncraft-0.2.10.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
17
+ syncraft-0.2.10.dist-info/METADATA,sha256=BcUwP1Fj4JuX2l0DuYYMiscpGsE4qvrci0RgsWbW4wY,1024
18
+ syncraft-0.2.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
+ syncraft-0.2.10.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
20
+ syncraft-0.2.10.dist-info/RECORD,,
@@ -1,20 +0,0 @@
1
- syncraft/__init__.py,sha256=g1Rd3DwPJ-Z7dL0PBNyNkcLBmpkp8bIBDWabkeBCD48,1329
2
- syncraft/algebra.py,sha256=RfI6IzVPflDMuONbajGmqDYo0gR2Yqt3GSaZ8M0N-cs,20709
3
- syncraft/ast.py,sha256=UZSByp1XZjaBMldc6AVL6bZcjTbPPi7qtlAM1HwY2Tk,20798
4
- syncraft/cache.py,sha256=oqF0E-jHFzYW36YqPRQm-wP_8_YhcB6VfGNdvgllE0w,4827
5
- syncraft/constraint.py,sha256=a6j_VafRor8W7FBXh20DoVnIUO9nfn7eIValigCf9lU,16318
6
- syncraft/dev.py,sha256=v7jdb2aOVCGbio-Jw14tRhO09FkhWc0vrDdIkIKPu2Y,186
7
- syncraft/finder.py,sha256=Mv9BYrsDjjq62Z4NO3gElZAbkEgSBINhSbqFbKEQW4Q,4347
8
- syncraft/generator.py,sha256=2q_dRKZlPLdfyrwdZqL01KpNes00qLKm-D13OWzN79I,21232
9
- syncraft/lexer.py,sha256=npPHAEEbBRQYcFHPMHtXmiVLbCZAnjpTtj3cnVui4W0,3720
10
- syncraft/parser.py,sha256=bdCbbeJ5p-j8kJniqmKSRAE8N3pCWN-ltJIpALW0h54,9070
11
- syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
13
- syncraft/syntax.py,sha256=ILIEK0GQp0_rpXJYEAX9E_wN7ouTUiFfyGqlQtaAJN4,24794
14
- syncraft/utils.py,sha256=2V446Il2q4mR7bwwGX5_qruJPU0WXU2PniXEjl6EOPE,8746
15
- syncraft/walker.py,sha256=-IFS8M_cL5qG2meJleo_AS2ejTis0bnLWu-6Yw-ILUk,6817
16
- syncraft-0.2.8.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
17
- syncraft-0.2.8.dist-info/METADATA,sha256=2QiTtmFXvxrquEPGn1eqBTSbsesiHavUf_KyQgxQtHk,1023
18
- syncraft-0.2.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- syncraft-0.2.8.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
20
- syncraft-0.2.8.dist-info/RECORD,,