syncraft 0.1.38__py3-none-any.whl → 0.2.1__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
@@ -7,7 +7,6 @@ from typing import (
7
7
  import traceback
8
8
  from dataclasses import dataclass, replace
9
9
  from weakref import WeakKeyDictionary
10
- from abc import ABC
11
10
  from syncraft.ast import ThenKind, Then, Choice, Many, ChoiceKind, shallow_dict
12
11
  from syncraft.constraint import Bindable
13
12
 
@@ -75,12 +74,16 @@ class Error:
75
74
 
76
75
 
77
76
  @dataclass(frozen=True)
78
- class Algebra(ABC, Generic[A, S]):
77
+ class Algebra(Generic[A, S]):
79
78
  ######################################################## shared among all subclasses ########################################################
80
79
  run_f: Callable[[S, bool], Either[Any, Tuple[A, S]]]
81
80
  name: Hashable
82
81
  _cache: ClassVar[WeakKeyDictionary[Any, Dict[Any, object | Either[Any, Tuple[Any, Any]]]]] = WeakKeyDictionary()
83
82
 
83
+ @classmethod
84
+ def state(cls, *args:Any, **kwargs:Any)->Optional[S]:
85
+ return None
86
+
84
87
  def named(self, name: Hashable) -> 'Algebra[A, S]':
85
88
  return replace(self, name=name)
86
89
 
syncraft/constraint.py CHANGED
@@ -5,6 +5,7 @@ from dataclasses import dataclass, field, replace
5
5
  import collections.abc
6
6
  from collections import defaultdict
7
7
  from itertools import product
8
+ import inspect
8
9
 
9
10
  K = TypeVar('K')
10
11
  V = TypeVar('V')
@@ -33,85 +34,23 @@ class FrozenDict(collections.abc.Mapping, Generic[K, V]):
33
34
 
34
35
  def __repr__(self):
35
36
  return f"{self.__class__.__name__}({self._data})"
36
-
37
-
38
-
39
-
40
- @dataclass(frozen=True)
41
- class Expr:
42
- left: Any
43
- op: str
44
- right: Any
45
-
46
-
47
- @dataclass(frozen=True)
48
- class Variable:
49
- name: Optional[str] = None
50
- _root: Optional[Variable] = field(default=None, compare=False, repr=False)
51
- _mapf: Optional[Callable[[Any], Any]] = field(default=None, compare=False, repr=False)
52
-
53
- def __post_init__(self):
54
- if self._root is None:
55
- object.__setattr__(self, '_root', self)
56
-
57
- def raw(self, b:'BoundVar') -> Tuple[Any, ...]:
58
- if self._root is None:
59
- raise ValueError("_rawf can not be None")
60
- return b.get(self._root, ())
61
-
62
-
63
- def map(self, f: Callable[[Any], Any]) -> "Variable":
64
- if self._mapf is None:
65
- return replace(self, _mapf=f)
66
- else:
67
- oldf = self._mapf
68
- return replace(self, _mapf=lambda a: f(oldf(a)))
69
37
 
70
- def get(self, b: 'BoundVar') -> Tuple[Any, ...]:
71
- vals = self.raw(b)
72
- if self._mapf is not None:
73
- return tuple(self._mapf(v) for v in vals)
74
- else:
75
- return vals
76
-
77
- def __call__(self, b:'BoundVar', raw:bool=False) -> Any:
78
- if raw:
79
- return self.raw(b)
80
- else:
81
- return self.get(b)
82
-
83
- def __eq__(self, other):
84
- return Expr(self, '==', other)
85
- def __ne__(self, other):
86
- return Expr(self, '!=', other)
87
- def __lt__(self, other):
88
- return Expr(self, '<', other)
89
- def __le__(self, other):
90
- return Expr(self, '<=', other)
91
- def __gt__(self, other):
92
- return Expr(self, '>', other)
93
- def __ge__(self, other):
94
- return Expr(self, '>=', other)
95
-
96
- BoundVar = FrozenDict[Variable, Tuple[Any, ...]]
97
-
98
-
99
38
  @dataclass(frozen=True)
100
39
  class Binding:
101
- bindings : frozenset[Tuple[Variable, Any]] = frozenset()
102
- def bind(self, var: Variable, node: Any) -> Binding:
40
+ bindings : frozenset[Tuple[str, Any]] = frozenset()
41
+ def bind(self, name: str, node: Any) -> Binding:
103
42
  new_binding = set(self.bindings)
104
- new_binding.add((var, node))
43
+ new_binding.add((name, node))
105
44
  return Binding(bindings=frozenset(new_binding))
106
45
 
107
- def to_dict(self)->BoundVar:
46
+ def bound(self)->FrozenDict[str, Tuple[Any, ...]]:
108
47
  ret = defaultdict(list)
109
- for var, node in self.bindings:
110
- ret[var].append(node)
48
+ for name, node in self.bindings:
49
+ ret[name].append(node)
111
50
  return FrozenDict({k: tuple(vs) for k, vs in ret.items()})
112
51
 
113
52
 
114
- A = TypeVar('A')
53
+
115
54
  @dataclass(frozen=True)
116
55
  class Bindable:
117
56
  binding: Binding = field(default_factory=Binding)
@@ -119,8 +58,8 @@ class Bindable:
119
58
  def map(self, f: Callable[[Any], Any])->Self:
120
59
  return self
121
60
 
122
- def bind(self, var: Variable, node:Any)->Self:
123
- return replace(self, binding=self.binding.bind(var, node))
61
+ def bind(self, name: str, node:Any)->Self:
62
+ return replace(self, binding=self.binding.bind(name, node))
124
63
 
125
64
 
126
65
  class Quantifier(Enum):
@@ -128,71 +67,105 @@ class Quantifier(Enum):
128
67
  EXISTS = "exists"
129
68
 
130
69
  @dataclass(frozen=True)
70
+ class ConstraintResult:
71
+ result: bool
72
+ unbound: frozenset[str] = frozenset()
73
+ @dataclass(frozen=True)
131
74
  class Constraint:
132
- run_f: Callable[[BoundVar], bool]
75
+ run_f: Callable[[FrozenDict[str, Tuple[Any, ...]]], ConstraintResult]
133
76
  name: str = ""
134
- def __call__(self, bound: BoundVar)->bool:
77
+ def __call__(self, bound: FrozenDict[str, Tuple[Any, ...]])->ConstraintResult:
135
78
  return self.run_f(bound)
136
79
  def __and__(self, other: Constraint) -> Constraint:
80
+ def and_run(bound: FrozenDict[str, Tuple[Any, ...]]) -> ConstraintResult:
81
+ res1 = self(bound)
82
+ res2 = other(bound)
83
+ combined_result = res1.result and res2.result
84
+ combined_unbound = res1.unbound.union(res2.unbound)
85
+ return ConstraintResult(result=combined_result, unbound=combined_unbound)
137
86
  return Constraint(
138
- run_f=lambda bound: self(bound) and other(bound),
87
+ run_f=and_run,
139
88
  name=f"({self.name} && {other.name})"
140
89
  )
141
90
  def __or__(self, other: Constraint) -> Constraint:
91
+ def or_run(bound: FrozenDict[str, Tuple[Any, ...]]) -> ConstraintResult:
92
+ res1 = self(bound)
93
+ res2 = other(bound)
94
+ combined_result = res1.result or res2.result
95
+ combined_unbound = res1.unbound.union(res2.unbound)
96
+ return ConstraintResult(result=combined_result, unbound=combined_unbound)
142
97
  return Constraint(
143
- run_f=lambda bound: self(bound) or other(bound),
98
+ run_f=or_run,
144
99
  name=f"({self.name} || {other.name})"
145
100
  )
146
101
  def __xor__(self, other: Constraint) -> Constraint:
102
+ def xor_run(bound: FrozenDict[str, Tuple[Any, ...]]) -> ConstraintResult:
103
+ res1 = self(bound)
104
+ res2 = other(bound)
105
+ combined_result = res1.result ^ res2.result
106
+ combined_unbound = res1.unbound.union(res2.unbound)
107
+ return ConstraintResult(result=combined_result, unbound=combined_unbound)
147
108
  return Constraint(
148
- run_f=lambda bound: self(bound) ^ other(bound),
109
+ run_f=xor_run,
149
110
  name=f"({self.name} ^ {other.name})"
150
111
  )
151
112
  def __invert__(self) -> Constraint:
113
+ def invert_run(bound: FrozenDict[str, Tuple[Any, ...]]) -> ConstraintResult:
114
+ res = self(bound)
115
+ return ConstraintResult(result=not res.result, unbound=res.unbound)
152
116
  return Constraint(
153
- run_f=lambda bound: not self(bound),
117
+ run_f=invert_run,
154
118
  name=f"!({self.name})"
155
119
  )
156
120
 
157
121
  @classmethod
158
- def predicate(cls, f: Callable[..., bool],*, name: Optional[str] = None, quant: Quantifier = Quantifier.FORALL)->Callable[..., Constraint]:
159
- def wrapper(*args: Any, **kwargs:Any) -> Constraint:
160
- arg_list = list(args)
161
- kw_list = [(k, v) for k, v in kwargs.items()]
162
- def run_f(bound: BoundVar) -> bool:
163
- # positional argument values
164
- pos_values = [
165
- arg.get(bound) if isinstance(arg, Variable) else (arg,)
166
- for arg in arg_list
167
- ]
168
- # keyword argument values
169
- kw_keys, kw_values = zip(*[
170
- (k, v.get(bound) if isinstance(v, Variable) else (v,))
171
- for k, v in kw_list
172
- ]) if kw_list else ([], [])
173
-
174
- # Cartesian product over all argument values
175
- all_combos = product(*pos_values, *kw_values)
176
-
177
- # evaluate predicate on each combination
178
- def eval_combo(combo):
179
- pos_args = combo[:len(pos_values)]
180
- kw_args = dict(zip(kw_keys, combo[len(pos_values):]))
181
- return f(*pos_args, **kw_args)
182
-
183
- if quant is Quantifier.EXISTS:
184
- return any(eval_combo(c) for c in all_combos)
185
- else:
186
- return all(eval_combo(c) for c in all_combos)
187
- return cls(run_f=run_f, name = name or f.__name__)
188
- return wrapper
189
-
190
- @classmethod
191
- def forall(cls, f: Callable[..., bool], name: Optional[str] = None) -> Callable[..., Constraint]:
192
- return cls.predicate(f, name=name, quant=Quantifier.FORALL)
122
+ def predicate(cls,
123
+ f: Callable[..., bool],
124
+ *,
125
+ name: Optional[str] = None,
126
+ quant: Quantifier = Quantifier.FORALL)->Constraint:
127
+ sig = inspect.signature(f)
128
+ pos_params = []
129
+ kw_params = []
130
+ for pname, param in sig.parameters.items():
131
+ if param.kind in (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD):
132
+ pos_params.append(pname)
133
+ elif param.kind == inspect.Parameter.KEYWORD_ONLY:
134
+ kw_params.append(pname)
135
+ else:
136
+ raise TypeError(f"Unsupported parameter kind: {param.kind}")
137
+ def run_f(bound: FrozenDict[str, Tuple[Any, ...]]) -> ConstraintResult:
138
+ # positional argument values
139
+ pos_values = [bound.get(pname, ()) for pname in pos_params]
140
+ # keyword argument values
141
+ kw_values = [bound.get(pname, ()) for pname in kw_params]
142
+
143
+ # If any param is unbound, fail
144
+ all_params = pos_params + kw_params
145
+ all_values = pos_values + kw_values
146
+ unbound_args = [p for p, vs in zip(all_params, all_values) if not vs]
147
+ if unbound_args:
148
+ return ConstraintResult(result=quant is Quantifier.FORALL, unbound=frozenset(unbound_args))
149
+
150
+ # Cartesian product
151
+ all_combos = product(*pos_values, *kw_values)
152
+
153
+ def eval_combo(combo):
154
+ pos_args = combo[: len(pos_values)]
155
+ kw_args = dict(zip(kw_params, combo[len(pos_values) :]))
156
+ return f(*pos_args, **kw_args)
157
+
158
+ if quant is Quantifier.EXISTS:
159
+ return ConstraintResult(result = any(eval_combo(c) for c in all_combos), unbound=frozenset())
160
+ else:
161
+ return ConstraintResult(result = all(eval_combo(c) for c in all_combos), unbound=frozenset())
162
+
163
+ return cls(run_f=run_f, name=name or f.__name__)
164
+
165
+ def forall(f: Callable[..., bool], name: Optional[str] = None) -> Constraint:
166
+ return Constraint.predicate(f, name=name, quant=Quantifier.FORALL)
193
167
 
194
- @classmethod
195
- def exists(cls, f: Callable[..., bool], name: Optional[str] = None):
196
- return cls.predicate(f, name=name, quant=Quantifier.EXISTS)
168
+ def exists(f: Callable[..., bool], name: Optional[str] = None) -> Constraint:
169
+ return Constraint.predicate(f, name=name, quant=Quantifier.EXISTS)
197
170
 
198
171
 
syncraft/generator.py CHANGED
@@ -16,14 +16,14 @@ from syncraft.ast import (
16
16
  Choice, Many, ChoiceKind,
17
17
  Then, ThenKind, Marked
18
18
  )
19
-
19
+ from syncraft.constraint import FrozenDict
20
20
  from syncraft.syntax import Syntax
21
21
  from sqlglot import TokenType
22
22
  import re
23
23
  import rstr
24
24
  from functools import lru_cache
25
25
  import random
26
- from rich import print
26
+
27
27
  from syncraft.constraint import Bindable
28
28
 
29
29
  T = TypeVar('T', bound=TokenProtocol)
@@ -144,6 +144,10 @@ class TokenGen(TokenSpec):
144
144
 
145
145
  @dataclass(frozen=True)
146
146
  class Generator(Algebra[ParseResult[T], GenState[T]]):
147
+ @classmethod
148
+ def state(cls, ast: Optional[ParseResult[T]] = None, seed: int = 0, restore_pruned: bool = False)->GenState[T]:
149
+ return GenState.from_ast(ast=ast, seed=seed, restore_pruned=restore_pruned)
150
+
147
151
  def flat_map(self, f: Callable[[ParseResult[T]], Algebra[B, GenState[T]]]) -> Algebra[B, GenState[T]]:
148
152
  def flat_map_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[B, GenState[T]]]:
149
153
  try:
@@ -293,14 +297,9 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
293
297
  def generate(syntax: Syntax[Any, Any],
294
298
  data: Optional[ParseResult[Any]] = None,
295
299
  seed: int = 0,
296
- restore_pruned: bool = False) -> AST | Any:
297
- gen = syntax(Generator)
298
- state = GenState.from_ast(ast=data, seed=seed, restore_pruned=restore_pruned)
299
- result = gen.run(state, use_cache=False)
300
- if isinstance(result, Right):
301
- return result.value[0]
302
- assert isinstance(result, Left), "Generator must return Either[Any, Tuple[Any, Any]]"
303
- return result.value
300
+ restore_pruned: bool = False) -> Tuple[AST, FrozenDict[str, AST]] | Tuple[Any, None]:
301
+ from syncraft.syntax import run
302
+ return run(syntax, Generator, False, ast=data, seed=seed, restore_pruned=restore_pruned)
304
303
 
305
304
 
306
305
 
syncraft/parser.py CHANGED
@@ -5,6 +5,7 @@ from typing import (
5
5
  Optional, List, Any, Tuple, TypeVar,
6
6
  Generic
7
7
  )
8
+ from syncraft.constraint import FrozenDict
8
9
  from syncraft.algebra import (
9
10
  Either, Left, Right, Error, Algebra
10
11
  )
@@ -67,6 +68,11 @@ class ParserState(Bindable, Generic[T]):
67
68
 
68
69
  @dataclass(frozen=True)
69
70
  class Parser(Algebra[T, ParserState[T]]):
71
+ @classmethod
72
+ def state(cls, sql: str, dialect: str) -> ParserState[Token]:
73
+ tokens = tuple([Token(token_type=token.token_type, text=token.text) for token in tokenize(sql, dialect=dialect)])
74
+ return ParserState.from_tokens(tokens)
75
+
70
76
  @classmethod
71
77
  def token(cls,
72
78
  token_type: Optional[Enum] = None,
@@ -182,21 +188,12 @@ def sqlglot(parser: Syntax[Any, Any],
182
188
  return parser.map(lambda tokens: [e for e in gp.parse(raw_tokens=tokens) if e is not None])
183
189
 
184
190
 
185
- def parse(syntax: Syntax[Any, Any],
186
- sql: str,
187
- dialect: str) -> AST | Any:
188
- parser = syntax(Parser)
189
- input: ParserState[Token] = token_state(sql, dialect=dialect)
190
- result = parser.run(input, True)
191
- if isinstance(result, Right):
192
- return result.value[0]
193
- assert isinstance(result, Left), "Parser must return Either[E, Tuple[A, S]]"
194
- return result.value
191
+ def parse(syntax: Syntax[Any, Any], sql: str, dialect: str) -> Tuple[AST, FrozenDict[str, AST]] | Tuple[Any, None]:
192
+ from syncraft.syntax import run
193
+ return run(syntax, Parser, True, sql=sql, dialect=dialect)
194
+
195
195
 
196
196
 
197
- def token_state(sql: str, dialect: str) -> ParserState[Token]:
198
- tokens = tuple([Token(token_type=token.token_type, text=token.text) for token in tokenize(sql, dialect=dialect)])
199
- return ParserState.from_tokens(tokens)
200
197
 
201
198
  def token(token_type: Optional[Enum] = None,
202
199
  text: Optional[str] = None,
syncraft/syntax.py CHANGED
@@ -2,18 +2,23 @@ from __future__ import annotations
2
2
 
3
3
  from typing import (
4
4
  Optional, Any, TypeVar, Generic, Callable, Tuple, cast,
5
- Type, Literal, List, overload
5
+ Type, Literal, List
6
6
  )
7
7
  from dataclasses import dataclass, field, replace
8
8
  from functools import reduce
9
- from syncraft.algebra import Algebra, Error, Either, Right
10
- from syncraft.constraint import Variable, Bindable
9
+ from syncraft.algebra import Algebra, Error, Either, Right, Left
10
+ from syncraft.constraint import Bindable, FrozenDict
11
11
  from syncraft.ast import Then, ThenKind, Marked, Choice, Many, ChoiceKind, Nothing, Collect, E, Collector
12
12
  from types import MethodType, FunctionType
13
+ import keyword
13
14
 
14
- from rich import print
15
15
 
16
16
 
17
+ def valid_name(name: str) -> bool:
18
+ return (name.isidentifier()
19
+ and not keyword.iskeyword(name)
20
+ and not (name.startswith('__') and name.endswith('__')))
21
+
17
22
  A = TypeVar('A') # Result type
18
23
  B = TypeVar('B') # Result type for mapping
19
24
  C = TypeVar('C') # Result type for else branch
@@ -249,27 +254,18 @@ class Syntax(Generic[A, S]):
249
254
  return self.optional()
250
255
 
251
256
 
252
- ######################################################################## data processing combinators #########################################################
253
- @overload
254
- def bind(self,
255
- var: Variable,
256
- collector: None = None)-> Syntax[A | Marked[A], S]: ...
257
-
258
- @overload
259
- def bind(self,
260
- var: Variable,
261
- collector: Type[E]) -> Syntax[Collect[A, E] | Marked[Collect[A, E]], S]: ...
262
-
263
- def bind(self,
264
- var: Variable,
265
- collector: Optional[Type[E]]=None) -> Syntax[Any, S]:
257
+ ######################################################################## data processing combinators #########################################################
258
+ def bind(self, name: Optional[str] = None) -> Syntax[A, S]:
259
+ if name:
260
+ assert valid_name(name), f"Invalid mark name: {name}"
266
261
  def bind_v(v: Any, s: S)->Tuple[Any, S]:
267
- return v, s.bind(var, v)
268
- if callable(collector):
269
- ret = self.to(collector).mark(var.name).map_all(bind_v) if var.name else self.to(collector).map_all(bind_v)
270
- else:
271
- ret = self.mark(var.name).map_all(bind_v) if var.name else self.map_all(bind_v)
272
- return ret.describe(name=f'bind({var.name})', fixity='postfix', parameter=(self,))
262
+ if name:
263
+ return v, s.bind(name, v)
264
+ elif isinstance(v, Marked):
265
+ return v.value, s.bind(v.name, v.value)
266
+ else:
267
+ return v, s
268
+ return self.map_all(bind_v).describe(name=f'bind({name})', fixity='postfix', parameter=(self,))
273
269
 
274
270
  def to(self, f: Collector[E])-> Syntax[Collect[A, E], S]:
275
271
  def to_f(v: A) -> Collect[A, E]:
@@ -283,15 +279,16 @@ class Syntax(Generic[A, S]):
283
279
 
284
280
 
285
281
  def mark(self, name: str) -> Syntax[Marked[A], S]:
286
- def bind_s(value: A) -> Marked[A]:
282
+ assert valid_name(name), f"Invalid mark name: {name}"
283
+ def mark_s(value: A) -> Marked[A]:
287
284
  if isinstance(value, Marked):
288
285
  return replace(value, name=name)
289
286
  else:
290
287
  return Marked(name=name, value=value)
291
- def ibind_s(m : Marked[A]) -> A:
288
+ def imark_s(m : Marked[A]) -> A:
292
289
  return m.value if isinstance(m, Marked) else m
293
290
 
294
- return self.bimap(bind_s, ibind_s).describe(name=f'bind("{name}")', fixity='postfix', parameter=(self,))
291
+ return self.bimap(mark_s, imark_s).describe(name=f'mark("{name}")', fixity='postfix', parameter=(self,))
295
292
 
296
293
 
297
294
 
@@ -322,50 +319,15 @@ def success(value: Any) -> Syntax[Any, Any]:
322
319
  def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
323
320
  return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(Nothing())
324
321
 
325
-
326
- def all(*parsers: Syntax[Any, S]) -> Syntax[Then[Any, Any], S]:
327
- return reduce(lambda a, b: a + b, parsers) if len(parsers) > 0 else success(Nothing())
328
-
329
- def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
330
- return reduce(lambda a, b: a // b, parsers) if len(parsers) > 0 else success(Nothing())
331
-
332
- def last(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
333
- return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(Nothing())
334
-
335
- def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Syntax[Any, S]:
336
- def is_named_parser(x: Any) -> bool:
337
- return isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], (str, Variable)) and isinstance(x[1], Syntax)
338
-
339
- def to_parser(x: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]])->Syntax[Any, S]:
340
- if isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], (str, Variable)) and isinstance(x[1], Syntax):
341
- if isinstance(x[0], str):
342
- return x[1].mark(x[0])
343
- elif isinstance(x[0], Variable):
344
- return x[1].bind(x[0])
345
- else:
346
- raise ValueError(f"Invalid variable type(must be str | Variable): {x[0]}", x)
347
- elif isinstance(x, Syntax):
348
- return x
349
- else:
350
- raise ValueError(f"Invalid parser or tuple: {x}", x)
351
- ret: Optional[Syntax[Any, S]] = None
352
- has_data = False
353
- for p in parsers:
354
- just_parser = to_parser(p)
355
- if has_data:
356
- if ret is not None:
357
- if is_named_parser(p):
358
- ret = ret + just_parser
359
- else:
360
- ret = ret // just_parser
361
- else:
362
- ret = just_parser
363
- else:
364
- has_data = is_named_parser(p)
365
- if ret is None:
366
- ret = just_parser
367
- else:
368
- ret = ret >> just_parser
369
-
370
- return ret if ret is not None else success(Nothing())
322
+ def run(syntax: Syntax[A, S], alg: Type[Algebra[A, S]], use_cache:bool, *args: Any, **kwargs: Any) -> Tuple[Any, FrozenDict[str, Any]] | Tuple[Any, None]:
323
+ parser = syntax(alg)
324
+ input: Optional[S] = alg.state(*args, **kwargs)
325
+ if input:
326
+ result = parser.run(input, use_cache=use_cache)
327
+ if isinstance(result, Right):
328
+ return result.value[0], result.value[1].binding.bound()
329
+ assert isinstance(result, Left), "Algebra must return Either[E, Tuple[A, S]]"
330
+ return result.value, None
331
+ else:
332
+ return Error(this=None, message="Algebra failed to create initial state"), None
371
333
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.38
3
+ Version: 0.2.1
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=2pyxbllcRsaDS_rEXfagBenZ_etSjMNdlVYxPkoU7cQ,15849
3
+ syncraft/ast.py,sha256=TAHj8IOgx_QtlI4zxFr2DRGZ4dwNGsb6TiH0TH9adIg,14556
4
+ syncraft/constraint.py,sha256=9VvnZtL3Q63qnU-j3cgTQm8cMrIXdgSgl4qIEF-YCx4,6509
5
+ syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
6
+ syncraft/finder.py,sha256=Wr7wiBuO9IaXBmYBA4DNXmoeEWteRIp-UetnuRScapM,1920
7
+ syncraft/generator.py,sha256=inNBE4dk1AHXCCgnBHsgVOuHRIsHXIY1BXnpy1oLq8s,13062
8
+ syncraft/parser.py,sha256=XjLEkPnOb8YwlIJeqtn9VT6oAWLu_otD2H_o4utZP5I,11270
9
+ syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
11
+ syncraft/syntax.py,sha256=qqPOxcelIwFNyzLdTKkQVtUzzz1Xkv0wEBJQ9vt8HTc,16282
12
+ syncraft-0.2.1.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
13
+ syncraft-0.2.1.dist-info/METADATA,sha256=ByoEIvwRb64_WH9MDoMJqJeEpwCqyJYi1OP9FJHNgsk,988
14
+ syncraft-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ syncraft-0.2.1.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
16
+ syncraft-0.2.1.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- syncraft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- syncraft/algebra.py,sha256=U28UIKK4oh2yIuvOqKYeo4_lDkX3Sy_nHSaHnIabcbk,15769
3
- syncraft/ast.py,sha256=TAHj8IOgx_QtlI4zxFr2DRGZ4dwNGsb6TiH0TH9adIg,14556
4
- syncraft/constraint.py,sha256=eTXINghioXmJzAtW3jM-Q_TW2LYXu6mUDq34KlI_x-w,6440
5
- syncraft/diagnostic.py,sha256=cgwcQnCcgrCRX3h-oGTDb5rcJAtitPV3LfH9eLvO93E,2837
6
- syncraft/finder.py,sha256=Wr7wiBuO9IaXBmYBA4DNXmoeEWteRIp-UetnuRScapM,1920
7
- syncraft/generator.py,sha256=C0vpJotp982anrSXq1-EdjQU_OMI_vDCa7uTdJK8M5M,12987
8
- syncraft/parser.py,sha256=RkQwFv00rOI-n4kvG4baGVTdK46T-_Hw7OJ0FB7g72g,11379
9
- syncraft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- syncraft/sqlite3.py,sha256=Pq09IHZOwuWg5W82l9D1flzd36QV0TOHQpTJ5U02V8g,34701
11
- syncraft/syntax.py,sha256=RxRdXR0KF5awmbaumfpkb1rvgMuCl0nhVIm3V20MB-U,17699
12
- syncraft-0.1.38.dist-info/licenses/LICENSE,sha256=wHSV424U5csa3339dy1AZbsz2xsd0hrkMx2QK48CcUk,1062
13
- syncraft-0.1.38.dist-info/METADATA,sha256=aO928d0qKdo3EiiNIfeAIoAnbKs84902-TKwHZ2B9JU,989
14
- syncraft-0.1.38.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- syncraft-0.1.38.dist-info/top_level.txt,sha256=Kq3t8ESXB2xW1Xt3uPmkENFc-c4f2pamNmaURBk7zc8,9
16
- syncraft-0.1.38.dist-info/RECORD,,