syncraft 0.1.20__py3-none-any.whl → 0.1.21__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
@@ -39,34 +39,33 @@ class Bimap(Generic[S, A, B]):
39
39
  forward=lambda s, x: (s, x),
40
40
  inverse=lambda s, y: (s, y)
41
41
  )
42
- @staticmethod
43
- def variable(value: A)->Bimap[S, A, A]:
44
- return Bimap(
45
- forward=lambda s, _: (s, value),
46
- inverse=lambda s, y: (s, y)
47
- )
48
- @staticmethod
49
- def const(state: S, value: A)->Bimap[Any, A, A]:
50
- return Bimap(
51
- forward=lambda s, _: (state, value),
52
- inverse=lambda s, y: (state, value)
53
- )
54
- @staticmethod
55
- def combine(*biarrows: Bimap[Any, Any, Any]) -> Bimap[Any, Any, Any]:
56
- return reduce(lambda a, b: a >> b, biarrows, Bimap.identity())
57
42
 
43
+ @staticmethod
44
+ def when(condition: Callable[..., bool],
45
+ then: Bimap[S, A, B],
46
+ otherwise: Optional[Bimap[S, A, B]] = None) -> Callable[..., Bimap[S, A, B]]:
47
+ def _when(*args:Any, **kwargs:Any) -> Bimap[S, A, B]:
48
+ return then if condition(*args, **kwargs) else (otherwise or Bimap.identity())
49
+ return _when
50
+
51
+
58
52
 
59
-
60
53
  class StructuralResult:
61
- def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, Any, Any]:
54
+ def bimap(self,
55
+ before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
56
+ after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
57
+ ) -> Bimap[Any, Any, Any]:
62
58
  return Bimap.identity()
63
59
 
64
60
  @dataclass(frozen=True)
65
61
  class NamedResult(Generic[A], StructuralResult):
66
62
  name: str
67
63
  value: A
68
- def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, NamedResult[A], NamedResult[Any]]:
69
- inner_b = self.value.bimap(arr) if isinstance(self.value, StructuralResult) else arr
64
+ def bimap(self,
65
+ before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
66
+ after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
67
+ ) -> Bimap[Any, NamedResult[A], NamedResult[Any]]:
68
+ inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
70
69
  def fwd(s: S, a: NamedResult[A])-> Tuple[S, NamedResult[Any]]:
71
70
  assert a == self, f"Expected {self}, got {a}"
72
71
  inner_s, inner_v = inner_b.forward(s, a.value)
@@ -76,33 +75,49 @@ class NamedResult(Generic[A], StructuralResult):
76
75
  assert isinstance(a, NamedResult), f"Expected NamedResult, got {type(a)}"
77
76
  inner_s, inner_v = inner_b.inverse(s, a.value)
78
77
  return (inner_s, replace(self, value=inner_v)) if not isinstance(inner_v, NamedResult) else (inner_s, replace(self, value=inner_v.value))
79
-
80
- return Bimap(
78
+ ret: Bimap[Any, Any, Any] = Bimap(
81
79
  forward=fwd,
82
80
  inverse=inv
83
81
  )
82
+ return before(self) >> ret >> after(self)
84
83
  @dataclass(eq=True, frozen=True)
85
84
  class ManyResult(Generic[A], StructuralResult):
86
85
  value: Tuple[A, ...]
87
- def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, ManyResult[A], List[A]]:
88
- inner_b = [v.bimap(arr) if isinstance(v, StructuralResult) else arr for v in self.value]
86
+ def bimap(self,
87
+ before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
88
+ after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
89
+ ) -> Bimap[Any, ManyResult[A], List[A]]:
90
+ # We don't allow zero length ManyResult e.g. many(at_least >= 1) at the syntax level
91
+ # so inner_b has at least 1 element
92
+ assert len(self.value) > 0, "ManyResult must have at least one element"
93
+ inner_b = [v.bimap(before, after) if isinstance(v, StructuralResult) else before(v) >> after(v) for v in self.value]
89
94
  def fwd(s: Any, a: ManyResult[A]) -> Tuple[Any, List[A]]:
90
95
  assert a == self, f"Expected {self}, got {a}"
91
96
  return s, [inner_b[i].forward(s, v)[1] for i, v in enumerate(a.value)]
92
97
 
93
98
  def inv(s: Any, a: List[A]) -> Tuple[Any, ManyResult[A]]:
94
99
  assert isinstance(a, list), f"Expected list, got {type(a)}"
95
- assert len(a) == len(inner_b), f"Expected {len(inner_b)} elements, got {len(a)}"
96
- return s, ManyResult(value=tuple(inner_b[i].inverse(s, v)[1] for i, v in enumerate(a)))
97
- return Bimap(
100
+ ret = [inner_b[i].inverse(s, v)[1] for i, v in enumerate(a)]
101
+ if len(ret) <= len(inner_b):
102
+ return s, ManyResult(value=tuple(ret))
103
+ else:
104
+ extra = [inner_b[-1].inverse(s, v)[1] for v in a[len(inner_b):]]
105
+ return s, ManyResult(value=tuple(ret + extra))
106
+
107
+ ret = Bimap(
98
108
  forward=fwd,
99
109
  inverse=inv
100
110
  )
111
+ return before(self) >> ret >> after(self)
112
+
101
113
  @dataclass(eq=True, frozen=True)
102
114
  class OrResult(Generic[A], StructuralResult):
103
115
  value: A
104
- def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, OrResult[A], Any]:
105
- inner_b = self.value.bimap(arr) if isinstance(self.value, StructuralResult) else arr
116
+ def bimap(self,
117
+ before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
118
+ after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
119
+ ) -> Bimap[Any, OrResult[A], Any]:
120
+ inner_b = self.value.bimap(before, after) if isinstance(self.value, StructuralResult) else before(self.value) >> after(self.value)
106
121
  def fwd(s: Any, a: OrResult[A]) -> Tuple[Any, Any]:
107
122
  assert a == self, f"Expected {self}, got {a}"
108
123
  return inner_b.forward(s, a.value)
@@ -111,10 +126,11 @@ class OrResult(Generic[A], StructuralResult):
111
126
  inner_s, inner_v = inner_b.inverse(s, a)
112
127
  return inner_s, OrResult(value=inner_v)
113
128
 
114
- return Bimap(
129
+ ret = Bimap(
115
130
  forward=fwd,
116
131
  inverse=inv
117
132
  )
133
+ return before(self) >> ret >> after(self)
118
134
  class ThenKind(Enum):
119
135
  BOTH = '+'
120
136
  LEFT = '//'
@@ -137,10 +153,13 @@ class ThenResult(Generic[A, B], StructuralResult):
137
153
  else:
138
154
  return 1
139
155
 
140
- def bimap(self, arr: Bimap[Any, Any, Any] = Bimap.identity()) -> Bimap[Any, ThenResult[A, B], Tuple[Any, ...] | Any]:
156
+ def bimap(self,
157
+ before: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity(),
158
+ after: Callable[[Any], Bimap[Any, Any, Any]] = lambda _: Bimap.identity()
159
+ ) -> Bimap[Any, ThenResult[A, B], Tuple[Any, ...] | Any]:
141
160
  kind = self.kind
142
- lb = self.left.bimap(arr) if isinstance(self.left, StructuralResult) else arr
143
- rb = self.right.bimap(arr) if isinstance(self.right, StructuralResult) else arr
161
+ lb = self.left.bimap(before, after) if isinstance(self.left, StructuralResult) else before(self.left) >> after(self.left)
162
+ rb = self.right.bimap(before, after) if isinstance(self.right, StructuralResult) else before(self.right) >> after(self.right)
144
163
  left_size = self.left.arity() if isinstance(self.left, ThenResult) else 1
145
164
  right_size = self.right.arity() if isinstance(self.right, ThenResult) else 1
146
165
  def fwd(s : S, a : ThenResult[A, B]) -> Tuple[S, Tuple[Any, ...] | Any]:
@@ -174,10 +193,11 @@ class ThenResult(Generic[A, B], StructuralResult):
174
193
  s2, rv = rb.inverse(s1, rraw)
175
194
  return s2, replace(self, left=lv, right=rv)
176
195
 
177
- return Bimap(
196
+ ret: Bimap[Any, Any, Any] = Bimap(
178
197
  forward=fwd,
179
198
  inverse=inv
180
199
  )
200
+ return before(self) >> ret >> after(self)
181
201
 
182
202
  InProgress = object() # Marker for in-progress state, used to prevent re-entrance in recursive calls
183
203
  L = TypeVar('L') # Left type for combined results
syncraft/finder.py ADDED
@@ -0,0 +1,149 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import (
4
+ Any, Tuple, Optional, Callable,
5
+ List, Generator as YieldGen
6
+ )
7
+ from dataclasses import dataclass
8
+ from syncraft.algebra import (
9
+ Algebra, Either, Left, Right, Error,
10
+ OrResult, ManyResult, ThenResult, NamedResult
11
+ )
12
+
13
+ from syncraft.ast import T, ParseResult, Token, AST
14
+ from syncraft.generator import GenState, TokenGen, B
15
+ from sqlglot import TokenType
16
+ from syncraft.syntax import Syntax
17
+ import re
18
+
19
+ @dataclass(frozen=True)
20
+ class Finder(Algebra[ParseResult[T], GenState[T]]):
21
+ def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
22
+ def flat_map_run(original: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
23
+ wrapper = original.wrapper()
24
+ 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
25
+ try:
26
+ lft = input.left()
27
+ match self.run(lft, use_cache=use_cache):
28
+ case Left(error):
29
+ return Left(error)
30
+ case Right((value, next_input)):
31
+ r = input.right()
32
+ match f(value).run(r, use_cache):
33
+ case Left(e):
34
+ return Left(e)
35
+ case Right((result, next_input)):
36
+ return Right((wrapper(result), next_input))
37
+ raise ValueError("flat_map should always return a value or an error.")
38
+ except Exception as e:
39
+ return Left(Error(
40
+ message=str(e),
41
+ this=self,
42
+ state=original,
43
+ error=e
44
+ ))
45
+ return Finder(run_f = flat_map_run, name=self.name) # type: ignore
46
+
47
+
48
+ def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
49
+ assert at_least > 0, "at_least must be greater than 0"
50
+ assert at_most is None or at_least <= at_most, "at_least must be less than or equal to at_most"
51
+ def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ManyResult[ParseResult[T]], GenState[T]]]:
52
+ wrapper = input.wrapper()
53
+ 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
54
+ ret = []
55
+ for index in range(input.how_many):
56
+ match self.run(input.down(index), use_cache):
57
+ case Right((value, next_input)):
58
+ ret.append(value)
59
+ if at_most is not None and len(ret) > at_most:
60
+ return Left(Error(
61
+ message=f"Expected at most {at_most} matches, got {len(ret)}",
62
+ this=self,
63
+ state=input.down(index)
64
+ ))
65
+ case Left(_):
66
+ pass
67
+ if len(ret) < at_least:
68
+ return Left(Error(
69
+ message=f"Expected at least {at_least} matches, got {len(ret)}",
70
+ this=self,
71
+ state=input.down(index)
72
+ ))
73
+ return Right((wrapper(ManyResult(tuple(ret))), input))
74
+ return self.__class__(many_run, name=f"many({self.name})") # type: ignore
75
+
76
+
77
+ def or_else(self, # type: ignore
78
+ other: Algebra[ParseResult[T], GenState[T]]
79
+ ) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
80
+ def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[ParseResult[T]], GenState[T]]]:
81
+ wrapper = input.wrapper()
82
+ 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
83
+ match self.run(input.down(0), use_cache):
84
+ case Right((value, next_input)):
85
+ return Right((wrapper(OrResult(value)), next_input))
86
+ case Left(error):
87
+ match other.run(input.down(0), use_cache):
88
+ case Right((value, next_input)):
89
+ return Right((wrapper(OrResult(value)), next_input))
90
+ case Left(error):
91
+ return Left(error)
92
+ raise ValueError("or_else should always return a value or an error.")
93
+ return self.__class__(or_else_run, name=f"or_else({self.name} | {other.name})") # type: ignore
94
+
95
+ @classmethod
96
+ def token(cls,
97
+ token_type: Optional[TokenType] = None,
98
+ text: Optional[str] = None,
99
+ case_sensitive: bool = False,
100
+ regex: Optional[re.Pattern[str]] = None
101
+ )-> Algebra[ParseResult[T], GenState[T]]:
102
+ gen = TokenGen(token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
103
+ lazy_self: Algebra[ParseResult[T], GenState[T]]
104
+ def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[Token], GenState[T]]]:
105
+ wrapper = input.wrapper()
106
+ 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
107
+ current = input.focus
108
+ if not isinstance(current, Token) or not gen.is_valid(current):
109
+ return Left(Error(None,
110
+ message=f"Expected a Token, but got {type(current)}.",
111
+ state=input))
112
+ return Right((wrapper(current), input))
113
+ lazy_self = cls(token_run, name=cls.__name__ + f'.token({token_type or text or regex})') # type: ignore
114
+ return lazy_self
115
+
116
+ @classmethod
117
+ def anything(cls)->Algebra[ParseResult[T], GenState[T]]:
118
+ def anything_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[T], GenState[T]]]:
119
+ wrapper = input.wrapper()
120
+ return Right((wrapper(input.focus), input))
121
+ return cls(anything_run, name=cls.__name__ + '.anything()')
122
+
123
+
124
+
125
+ anything = Syntax(lambda cls: cls.factory('anything')).describe(name="anything", fixity='infix')
126
+
127
+ def matches(syntax: Syntax[Any, Any], data: AST[Any])-> bool:
128
+ gen = syntax(Finder)
129
+ state = GenState.from_ast(data)
130
+ result = gen.run(state, use_cache=True)
131
+ return isinstance(result, Right)
132
+
133
+
134
+ def find(syntax: Syntax[Any, Any], data: AST[Any]) -> YieldGen[AST[Any], None, None]:
135
+ if matches(syntax, data):
136
+ yield data
137
+ match data.focus:
138
+ case ThenResult(left = left, right=right):
139
+ yield from find(syntax, AST(left))
140
+ yield from find(syntax, AST(right))
141
+ case ManyResult(value = value):
142
+ for e in value:
143
+ yield from find(syntax, AST(e))
144
+ case NamedResult(value=value):
145
+ yield from find(syntax, AST(value))
146
+ case OrResult(value=value):
147
+ yield from find(syntax, AST(value))
148
+ case _:
149
+ pass
syncraft/generator.py CHANGED
@@ -1,17 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from typing import (
4
- Any, TypeVar, Tuple, Optional, Callable, Generic, Union,
5
- List, Generator as YieldGen
4
+ Any, TypeVar, Tuple, Optional, Callable, Generic,
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, ThenResult, Either, Left, Right, Error,
11
- OrResult, ManyResult, NamedResult
10
+ Algebra, Either, Left, Right, Error,
11
+ OrResult, ManyResult
12
12
  )
13
13
 
14
- from syncraft.ast import TokenProtocol, ParseResult, AST, Token, TokenSpec
14
+ from syncraft.ast import T, ParseResult, AST, Token, TokenSpec
15
15
  from syncraft.syntax import Syntax
16
16
  from sqlglot import TokenType
17
17
  import re
@@ -20,15 +20,7 @@ from functools import lru_cache
20
20
  import random
21
21
 
22
22
  B = TypeVar('B')
23
- T = TypeVar('T', bound=TokenProtocol)
24
23
 
25
- GenResult = Union[
26
- ThenResult['GenResult[T]', 'GenResult[T]'],
27
- ManyResult['GenResult[T]'],
28
- OrResult['GenResult[T]'],
29
-
30
- T
31
- ]
32
24
 
33
25
  @dataclass(frozen=True)
34
26
  class GenState(Generic[T]):
@@ -150,8 +142,8 @@ class TokenGen(TokenSpec):
150
142
 
151
143
 
152
144
  @dataclass(frozen=True)
153
- class Generator(Algebra[GenResult[T], GenState[T]]):
154
- def flat_map(self, f: Callable[[GenResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
145
+ class Generator(Algebra[ParseResult[T], GenState[T]]):
146
+ def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
155
147
  def flat_map_run(original: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
156
148
  wrapper = original.wrapper()
157
149
  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
@@ -178,10 +170,10 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
178
170
  return Generator(run_f = flat_map_run, name=self.name) # type: ignore
179
171
 
180
172
 
181
- def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[GenResult[T]], GenState[T]]:
173
+ def many(self, *, at_least: int, at_most: Optional[int]) -> Algebra[ManyResult[ParseResult[T]], GenState[T]]:
182
174
  assert at_least > 0, "at_least must be greater than 0"
183
175
  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[GenResult[T]], GenState[T]]]:
176
+ def many_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ManyResult[ParseResult[T]], GenState[T]]]:
185
177
  wrapper = input.wrapper()
186
178
  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
187
179
  if input.pruned:
@@ -221,9 +213,9 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
221
213
 
222
214
 
223
215
  def or_else(self, # type: ignore
224
- other: Algebra[GenResult[T], GenState[T]]
225
- ) -> Algebra[OrResult[GenResult[T]], GenState[T]]:
226
- def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[GenResult[T]], GenState[T]]]:
216
+ other: Algebra[ParseResult[T], GenState[T]]
217
+ ) -> Algebra[OrResult[ParseResult[T]], GenState[T]]:
218
+ def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[ParseResult[T]], GenState[T]]]:
227
219
  wrapper = input.wrapper()
228
220
  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
221
  if input.pruned:
@@ -252,10 +244,10 @@ class Generator(Algebra[GenResult[T], GenState[T]]):
252
244
  text: Optional[str] = None,
253
245
  case_sensitive: bool = False,
254
246
  regex: Optional[re.Pattern[str]] = None
255
- )-> Algebra[GenResult[T], GenState[T]]:
247
+ )-> Algebra[ParseResult[T], GenState[T]]:
256
248
  gen = TokenGen(token_type=token_type, text=text, case_sensitive=case_sensitive, regex=regex)
257
- lazy_self: Algebra[GenResult[T], GenState[T]]
258
- def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[GenResult[Token], GenState[T]]]:
249
+ lazy_self: Algebra[ParseResult[T], GenState[T]]
250
+ def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[ParseResult[Token], GenState[T]]]:
259
251
  wrapper = input.wrapper()
260
252
  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
253
  if input.pruned:
@@ -282,27 +274,4 @@ def generate(syntax: Syntax[Any, Any], data: Optional[AST[Any]] = None, seed: in
282
274
  return result.value
283
275
 
284
276
 
285
- def matches(syntax: Syntax[Any, Any], data: AST[Any])-> bool:
286
- gen = syntax(Generator)
287
- state = GenState.from_ast(data)
288
- result = gen.run(state, use_cache=True)
289
- return isinstance(result, Right)
290
-
291
-
292
- def search(syntax: Syntax[Any, Any], data: AST[Any]) -> YieldGen[AST[Any], None, None]:
293
- if matches(syntax, data):
294
- yield data
295
- match data.focus:
296
- case ThenResult(left = left, right=right):
297
- yield from search(syntax, AST(left))
298
- yield from search(syntax, AST(right))
299
- case ManyResult(value = value):
300
- for e in value:
301
- yield from search(syntax, AST(e))
302
- case NamedResult(value=value):
303
- yield from search(syntax, AST(value))
304
- case OrResult(value=value):
305
- yield from search(syntax, AST(value))
306
- case _:
307
- pass
308
277
 
syncraft/parser.py CHANGED
@@ -16,13 +16,6 @@ from syncraft.syntax import Syntax
16
16
  from syncraft.ast import Token, TokenSpec, AST, T
17
17
 
18
18
 
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
19
  @dataclass(frozen=True)
27
20
  class ParserState(Generic[T]):
28
21
  input: Tuple[T, ...] = field(default_factory=tuple)
syncraft/syntax.py CHANGED
@@ -17,6 +17,33 @@ B = TypeVar('B') # Result type for mapping
17
17
  C = TypeVar('C') # Result type for else branch
18
18
  S = TypeVar('S') # State type
19
19
 
20
+
21
+ class Variable(Generic[A]):
22
+ def __init__(self, name: Optional[str] = None):
23
+ self.name = name
24
+ self.initialized = False
25
+ self._value = None
26
+ def __call__(self, a: Any) -> Any:
27
+ if self.initialized:
28
+ if self._value != a:
29
+ raise ValueError(f"Variable {self.name or ''} is already initialized")
30
+ else:
31
+ return a
32
+ elif self.name is not None and isinstance(a, NamedResult):
33
+ if a.name == self.name:
34
+ self._value = a.value
35
+ self.initialized = True
36
+ else:
37
+ self._value = a
38
+ self.initialized = True
39
+ return a
40
+ @property
41
+ def value(self) -> A:
42
+ if self.initialized is False or self._value is None:
43
+ raise ValueError(f"Variable {self.name or ''} is not initialized")
44
+ return self._value
45
+
46
+
20
47
  @dataclass(frozen=True)
21
48
  class Description:
22
49
  name: Optional[str] = None
@@ -98,8 +125,6 @@ class Syntax(Generic[A, S]):
98
125
  return self.__class__(alg=algebra_run, meta=self.meta)
99
126
 
100
127
 
101
-
102
-
103
128
  def as_(self, typ: Type[B])->B:
104
129
  return cast(typ, self) # type: ignore
105
130
 
@@ -234,6 +259,8 @@ class Syntax(Generic[A, S]):
234
259
  return NamedResult(name=name, value=value)
235
260
  return self.map(bind_f).describe(name=f'bind("{name}")', fixity='postfix', parameter=[self])
236
261
 
262
+ def assign(self, var: Variable[A]) -> Syntax[A, S]:
263
+ return self.map(var).describe(name=f'assign({var.name or ""})', fixity='postfix', parameter=[self])
237
264
 
238
265
  def dump_error(self, formatter: Optional[Callable[[Error], None]] = None) -> Syntax[A, S]:
239
266
  def dump_error_run(err: Any)->Any:
@@ -263,12 +290,6 @@ def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
263
290
  return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(None)
264
291
 
265
292
 
266
-
267
-
268
-
269
-
270
-
271
-
272
293
  def all(*parsers: Syntax[Any, S]) -> Syntax[ThenResult[Any, Any], S]:
273
294
  return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(None)
274
295
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.20
3
+ Version: 0.1.21
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -0,0 +1,16 @@
1
+ syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ syncraft/algebra.py,sha256=bya-2b5WpCcOMxgJjGqrFaNG8dCjs5NUqnxbJTc4uOM,21908
3
+ syncraft/ast.py,sha256=Hn88_xOqSzrHYtPjNFKREJuE6yISmNI-VVTcU6PJKI0,4737
4
+ syncraft/cmd.py,sha256=DzXgU8QLVOg0YTFKpmOyzbf02LKPphQ4EeKSLzqpJ_s,2012
5
+ syncraft/diagnostic.py,sha256=mnzzceE9HpN2jVa7ztecZCz3sbCD79JxgkdLhJRBsvY,2822
6
+ syncraft/finder.py,sha256=zADOQQdcHu60413ZrRxTSq-E5EAJ9xv0OQodCpoYpo0,7202
7
+ syncraft/generator.py,sha256=Mh9Sx1hNR0EAWcox6MMvVw7aq2s33TU4stGZFZ1N1cw,11287
8
+ syncraft/parser.py,sha256=PSy9s3q_KXQh-Qlg0NxvJixxcgVYi3yCq0ebqlA5WwM,11288
9
+ syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
11
+ syncraft/syntax.py,sha256=yUp3DmDnkwNEqDAFZH4vesjdnbIUsrwFjWN-Ljy1qN4,14880
12
+ syncraft-0.1.21.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
13
+ syncraft-0.1.21.dist-info/METADATA,sha256=cJyW-BifZOf3RdmFKp1-auJv13aMNjRwk1VDqPG5gR4,1381
14
+ syncraft-0.1.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ syncraft-0.1.21.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
16
+ syncraft-0.1.21.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- syncraft/algebra.py,sha256=utnhCbrF5GFG6P2Oef_Ru6YVgVCgsGccMh6wx80ok5E,20519
3
- syncraft/ast.py,sha256=Hn88_xOqSzrHYtPjNFKREJuE6yISmNI-VVTcU6PJKI0,4737
4
- syncraft/cmd.py,sha256=DzXgU8QLVOg0YTFKpmOyzbf02LKPphQ4EeKSLzqpJ_s,2012
5
- syncraft/diagnostic.py,sha256=mnzzceE9HpN2jVa7ztecZCz3sbCD79JxgkdLhJRBsvY,2822
6
- syncraft/generator.py,sha256=ygt5HXfKG5z1LcTxhU8adqEePegqmokLjbP6pi34hGA,12355
7
- syncraft/parser.py,sha256=hk67kvOTBToOB2GhAh4weJQHR9xv73YPNQnqrW_qYv8,11295
8
- syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
10
- syncraft/syntax.py,sha256=NtxkfPvSNsmpbRcESxEgRAW9d04ZHukWQoSJqIHsHNk,13842
11
- syncraft-0.1.20.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
12
- syncraft-0.1.20.dist-info/METADATA,sha256=j3Ylao4YeT_HxO4Gv95chaRvpoZLBallacoeoaoLQjk,1381
13
- syncraft-0.1.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- syncraft-0.1.20.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
15
- syncraft-0.1.20.dist-info/RECORD,,