syncraft 0.2.9__tar.gz → 0.2.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (33) hide show
  1. {syncraft-0.2.9/syncraft.egg-info → syncraft-0.2.10}/PKG-INFO +1 -1
  2. {syncraft-0.2.9 → syncraft-0.2.10}/pyproject.toml +1 -1
  3. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/algebra.py +11 -4
  4. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/ast.py +45 -7
  5. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/syntax.py +19 -10
  6. syncraft-0.2.10/syncraft/walker.py +189 -0
  7. {syncraft-0.2.9 → syncraft-0.2.10/syncraft.egg-info}/PKG-INFO +1 -1
  8. syncraft-0.2.9/syncraft/walker.py +0 -147
  9. {syncraft-0.2.9 → syncraft-0.2.10}/LICENSE +0 -0
  10. {syncraft-0.2.9 → syncraft-0.2.10}/README.md +0 -0
  11. {syncraft-0.2.9 → syncraft-0.2.10}/setup.cfg +0 -0
  12. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/__init__.py +0 -0
  13. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/cache.py +0 -0
  14. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/constraint.py +0 -0
  15. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/dev.py +0 -0
  16. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/finder.py +0 -0
  17. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/generator.py +0 -0
  18. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/lexer.py +0 -0
  19. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/parser.py +0 -0
  20. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/py.typed +0 -0
  21. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/sqlite3.py +0 -0
  22. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft/utils.py +0 -0
  23. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft.egg-info/SOURCES.txt +0 -0
  24. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft.egg-info/dependency_links.txt +0 -0
  25. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft.egg-info/requires.txt +0 -0
  26. {syncraft-0.2.9 → syncraft-0.2.10}/syncraft.egg-info/top_level.txt +0 -0
  27. {syncraft-0.2.9 → syncraft-0.2.10}/tests/test_bimap.py +0 -0
  28. {syncraft-0.2.9 → syncraft-0.2.10}/tests/test_constraint.py +0 -0
  29. {syncraft-0.2.9 → syncraft-0.2.10}/tests/test_find.py +0 -0
  30. {syncraft-0.2.9 → syncraft-0.2.10}/tests/test_lazy.py +0 -0
  31. {syncraft-0.2.9 → syncraft-0.2.10}/tests/test_parse.py +0 -0
  32. {syncraft-0.2.9 → syncraft-0.2.10}/tests/test_to.py +0 -0
  33. {syncraft-0.2.9 → syncraft-0.2.10}/tests/test_walk.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.2.9
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "syncraft"
3
- version = "0.2.9"
3
+ version = "0.2.10"
4
4
  description = "Parser combinator library"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -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
 
@@ -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]):
493
+ name: str
491
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
  *,
@@ -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
 
@@ -0,0 +1,189 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import (
4
+ Any, Tuple, Generator as PyGenerator, TypeVar, Generic, Optional, Callable, Hashable
5
+ )
6
+ from dataclasses import dataclass, replace, field
7
+ from syncraft.algebra import (
8
+ Algebra, Either, Right, Incomplete, Left, SyncraftError
9
+ )
10
+ from syncraft.ast import TokenSpec, ThenSpec, ManySpec, ChoiceSpec, LazySpec, ThenKind, RefSpec
11
+ from syncraft.parser import TokenType
12
+ from syncraft.constraint import Bindable, FrozenDict
13
+
14
+ import re
15
+ from syncraft.syntax import Syntax
16
+ from syncraft.cache import Cache, RecursionError
17
+ from rich import print
18
+
19
+
20
+ S = TypeVar('S', bound=Bindable)
21
+ A = TypeVar('A')
22
+ B = TypeVar('B')
23
+ SS = TypeVar('SS', bound=Hashable)
24
+
25
+
26
+ @dataclass(frozen=True)
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}))
31
+
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class Walker(Algebra[Any, WalkerState]):
36
+ @classmethod
37
+ def state(cls, **kwargs: Any)->WalkerState:
38
+ return WalkerState()
39
+
40
+ def map(self, f: Callable[[Any], Any]) -> Algebra[Any, WalkerState]:
41
+ return self
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
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
+
71
+ @classmethod
72
+ def token(cls,
73
+ *,
74
+ cache: Cache,
75
+ token_type: Optional[TokenType] = None,
76
+ text: Optional[str] = None,
77
+ case_sensitive: bool = False,
78
+ regex: Optional[re.Pattern[str]] = None
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]]]:
83
+ yield from ()
84
+ data = TokenSpec(token_type=token_type, text=text, regex=regex, case_sensitive=case_sensitive)
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
88
+
89
+ @classmethod
90
+ def lazy(cls,
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]]]:
95
+ alg = thunk()
96
+ thunk_result = yield from alg.run(input, use_cache)
97
+ match thunk_result:
98
+ case Right((value, from_thunk)):
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))
102
+ raise SyncraftError("flat_map should always return a value or an error.", offending=thunk_result, expect=(Left, Right))
103
+ this = cls(algebra_lazy_run, name='lazy(?)', cache=cache)
104
+ return this
105
+
106
+
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]]]:
111
+ self_result = yield from self.run(input, use_cache=use_cache)
112
+ match self_result:
113
+ case Right((value, from_left)):
114
+ other_result = yield from other.run(from_left.visit(self, value), use_cache)
115
+ match other_result:
116
+ case Right((result, from_right)):
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))
120
+ raise SyncraftError("flat_map should always return a value or an error.", offending=self_result, expect=(Left, Right))
121
+ this = self.__class__(then_run, name=name, cache=self.cache | other.cache)
122
+ return this
123
+
124
+
125
+
126
+ def then_both(self, other: Algebra[Any, WalkerState]) -> Algebra[Any, WalkerState]:
127
+ return self.then_all(other, ThenKind.BOTH)
128
+
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]:
137
+ if at_least <=0 or (at_most is not None and at_most < at_least):
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)")
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]]]:
141
+ self_result = yield from self.run(input, use_cache)
142
+ match self_result:
143
+ case Right((value, from_self)):
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))
147
+ raise SyncraftError("many should always return a value or an error.", offending=self_result, expect=(Left, Right))
148
+ this = self.__class__(many_run, name=self.name, cache=self.cache)
149
+ return this
150
+
151
+
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]]]:
161
+ self_result = yield from self.run(input, use_cache=use_cache)
162
+ match self_result:
163
+ case Right((value, from_left)):
164
+ other_result = yield from other.run(from_left.visit(self, value), use_cache)
165
+ match other_result:
166
+ case Right((result, from_right)):
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))
170
+ raise SyncraftError("", offending=self)
171
+ this = self.__class__(or_else_run, name=name, cache=self.cache | other.cache)
172
+ return this
173
+
174
+
175
+
176
+ def walk(syntax: Syntax[Any, Any], reducer: Optional[Callable[[Any, Any], SS]] = None, init: Optional[SS] = None) -> Any:
177
+ from syncraft.syntax import run
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.9
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
@@ -1,147 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import (
4
- Any, Tuple, Generator as PyGenerator, TypeVar, Generic, Optional, Callable, Hashable
5
- )
6
- from dataclasses import dataclass, replace, field
7
- from syncraft.algebra import (
8
- Algebra, Either, Right, Incomplete, Left, SyncraftError
9
- )
10
- from syncraft.ast import TokenSpec, ThenSpec, ManySpec, ChoiceSpec, LazySpec, ThenKind
11
- from syncraft.parser import TokenType
12
- from syncraft.constraint import Bindable, FrozenDict
13
-
14
- import re
15
- from syncraft.syntax import Syntax
16
- from syncraft.cache import Cache, RecursionError
17
- from rich import print
18
-
19
-
20
- S = TypeVar('S', bound=Bindable)
21
- A = TypeVar('A')
22
- B = TypeVar('B')
23
- SS = TypeVar('SS', bound=Hashable)
24
-
25
-
26
- @dataclass(frozen=True)
27
- class WalkerState(Bindable, Generic[SS]):
28
- reducer: Optional[Callable[[Any, SS], SS]] = None
29
- acc: Optional[SS] = None
30
-
31
- def reduce(self, value: Any) -> WalkerState[SS]:
32
- if self.reducer:
33
- new_acc = self.reducer(value, self.acc) if self.acc is not None else value
34
- return replace(self, acc=new_acc)
35
- else:
36
- return replace(self, acc=value)
37
-
38
-
39
- @dataclass(frozen=True)
40
- class Walker(Algebra[SS, WalkerState[SS]]):
41
- @classmethod
42
- def state(cls, reducer: Callable[[Any, SS], SS], init: SS )->WalkerState[SS]: # type: ignore
43
- assert callable(reducer), f"reducer must be a Reducer or None, got {type(reducer)}"
44
- return WalkerState(reducer=reducer, acc=init)
45
-
46
-
47
-
48
- @classmethod
49
- def token(cls,
50
- *,
51
- cache: Cache,
52
- token_type: Optional[TokenType] = None,
53
- text: Optional[str] = None,
54
- case_sensitive: bool = False,
55
- regex: Optional[re.Pattern[str]] = None
56
- )-> Algebra[Any, WalkerState[SS]]:
57
- def token_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
58
- yield from ()
59
- data = TokenSpec(token_type=token_type, text=text, regex=regex, case_sensitive=case_sensitive)
60
- return Right((data, input.reduce(data)))
61
- return cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})', cache=cache)
62
-
63
- @classmethod
64
- def lazy(cls,
65
- thunk: Callable[[], Algebra[Any, WalkerState[SS]]],
66
- cache: Cache) -> Algebra[Any, WalkerState[SS]]:
67
- def algebra_lazy_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
68
- alg = thunk()
69
- # print('--' * 20, "Walker.lazy.algebra_lazy_run", '--' * 20)
70
- # print('thunk', thunk, id(thunk))
71
- # print('input', input, id(input))
72
- # print('alg', alg, id(alg))
73
- try:
74
- thunk_result = yield from alg.run(input, use_cache)
75
- match thunk_result:
76
- case Right((value, from_thunk)):
77
- data: LazySpec[Any] = LazySpec(value=value)
78
- return Right((data, from_thunk.reduce(data)))
79
- raise SyncraftError("flat_map should always return a value or an error.", offending=thunk_result, expect=(Left, Right))
80
- except RecursionError as e:
81
- return Right((LazySpec(value=None), input))
82
- return cls(algebra_lazy_run, name=cls.__name__ + '.lazy', cache=cache)
83
-
84
-
85
-
86
- def then_both(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
87
- def then_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
88
- self_result = yield from self.run(input, use_cache=use_cache)
89
- match self_result:
90
- case Right((value, from_left)):
91
- other_result = yield from other.run(from_left, use_cache)
92
- match other_result:
93
- case Right((result, from_right)):
94
- data = ThenSpec(kind=ThenKind.BOTH, left=value, right=result)
95
- return Right((data, from_right.reduce(data)))
96
- raise SyncraftError("flat_map should always return a value or an error.", offending=self_result, expect=(Left, Right))
97
- return self.__class__(then_run, name=self.name, cache=self.cache | other.cache)
98
-
99
- def then_left(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
100
- return self.then_both(other).map(lambda t: replace(t, kind=ThenKind.LEFT))
101
-
102
- def then_right(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
103
- return self.then_both(other).map(lambda t: replace(t, kind=ThenKind.RIGHT))
104
-
105
-
106
- def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[Any, WalkerState[SS]]:
107
- if at_least <=0 or (at_most is not None and at_most < at_least):
108
- 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)")
109
- def many_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
110
- self_result = yield from self.run(input, use_cache)
111
- match self_result:
112
- case Right((value, from_self)):
113
- data = ManySpec(value=value, at_least=at_least, at_most=at_most)
114
- return Right((data, from_self.reduce(data)))
115
- raise SyncraftError("many should always return a value or an error.", offending=self_result, expect=(Left, Right))
116
- return self.__class__(many_run, name=f"many({self.name})", cache=self.cache)
117
-
118
-
119
- def or_else(self, other: Algebra[Any, WalkerState[SS]]) -> Algebra[Any, WalkerState[SS]]:
120
- def or_else_run(input: WalkerState[SS], use_cache:bool) -> PyGenerator[Incomplete[WalkerState[SS]], WalkerState[SS], Either[Any, Tuple[Any, WalkerState[SS]]]]:
121
- self_result = yield from self.run(input, use_cache=use_cache)
122
- match self_result:
123
- case Right((value, from_left)):
124
- other_result = yield from other.run(from_left, use_cache)
125
- match other_result:
126
- case Right((result, from_right)):
127
- data = ChoiceSpec(left=value, right=result)
128
- return Right((data, from_right.reduce(data)))
129
- raise SyncraftError("", offending=self)
130
- return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})", cache=self.cache | other.cache)
131
-
132
-
133
-
134
- def walk(syntax: Syntax[Any, Any], reducer: Optional[Callable[[Any, Any], SS]] = None, init: Optional[SS] = None) -> Any:
135
- from syncraft.syntax import run
136
- v, s = run(syntax=syntax,
137
- alg=Walker,
138
- use_cache=True,
139
- reducer=reducer or (lambda a, s: s),
140
- init=init)
141
- if reducer is None:
142
- return v
143
- else:
144
- if s is not None:
145
- return s.acc
146
- else:
147
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes