syncraft 0.2.1__tar.gz → 0.2.2__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.
- {syncraft-0.2.1 → syncraft-0.2.2}/PKG-INFO +1 -1
- {syncraft-0.2.1 → syncraft-0.2.2}/pyproject.toml +1 -1
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/constraint.py +28 -8
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/generator.py +1 -1
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/parser.py +3 -3
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/syntax.py +1 -1
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft.egg-info/PKG-INFO +1 -1
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft.egg-info/SOURCES.txt +1 -0
- syncraft-0.2.2/tests/test_constraint.py +53 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/LICENSE +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/README.md +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/setup.cfg +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/__init__.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/algebra.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/ast.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/diagnostic.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/finder.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/py.typed +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft/sqlite3.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft.egg-info/dependency_links.txt +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft.egg-info/requires.txt +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/syncraft.egg-info/top_level.txt +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/tests/test_bimap.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/tests/test_find.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/tests/test_parse.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/tests/test_to.py +0 -0
- {syncraft-0.2.1 → syncraft-0.2.2}/tests/test_until.py +0 -0
|
@@ -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
|
+
from inspect import Signature
|
|
8
9
|
import inspect
|
|
9
10
|
|
|
10
11
|
K = TypeVar('K')
|
|
@@ -122,9 +123,9 @@ class Constraint:
|
|
|
122
123
|
def predicate(cls,
|
|
123
124
|
f: Callable[..., bool],
|
|
124
125
|
*,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
sig: Signature,
|
|
127
|
+
name: str,
|
|
128
|
+
quant: Quantifier)->Constraint:
|
|
128
129
|
pos_params = []
|
|
129
130
|
kw_params = []
|
|
130
131
|
for pname, param in sig.parameters.items():
|
|
@@ -160,12 +161,31 @@ class Constraint:
|
|
|
160
161
|
else:
|
|
161
162
|
return ConstraintResult(result = all(eval_combo(c) for c in all_combos), unbound=frozenset())
|
|
162
163
|
|
|
163
|
-
return cls(run_f=run_f, name=name
|
|
164
|
+
return cls(run_f=run_f, name=name)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def predicate(f: Callable[..., bool],
|
|
168
|
+
*,
|
|
169
|
+
name: Optional[str] = None,
|
|
170
|
+
quant: Quantifier = Quantifier.FORALL,
|
|
171
|
+
bimap: bool = True) -> Constraint:
|
|
172
|
+
name = name or f.__name__
|
|
173
|
+
sig = inspect.signature(f)
|
|
174
|
+
if bimap:
|
|
175
|
+
def wrapper(*args: Any, **kwargs:Any) -> bool:
|
|
176
|
+
mapped_args = [a.bimap()[0] if hasattr(a, "bimap") else a for a in args]
|
|
177
|
+
mapped_kwargs = {k: (v.bimap()[0] if hasattr(v, "bimap") else v) for k,v in kwargs.items()}
|
|
178
|
+
return f(*mapped_args, **mapped_kwargs)
|
|
179
|
+
|
|
180
|
+
return Constraint.predicate(wrapper, sig=sig, name=name, quant=quant)
|
|
181
|
+
else:
|
|
182
|
+
return Constraint.predicate(f, sig=sig, name=name, quant=quant)
|
|
164
183
|
|
|
165
|
-
def forall(f: Callable[..., bool], name: Optional[str] = None) -> Constraint:
|
|
166
|
-
return
|
|
184
|
+
def forall(f: Callable[..., bool], name: Optional[str] = None, bimap: bool=True) -> Constraint:
|
|
185
|
+
return predicate(f, name=name, quant=Quantifier.FORALL, bimap=bimap)
|
|
167
186
|
|
|
168
|
-
def exists(f: Callable[..., bool], name: Optional[str] = None) -> Constraint:
|
|
169
|
-
return
|
|
187
|
+
def exists(f: Callable[..., bool], name: Optional[str] = None, bimap:bool = True) -> Constraint:
|
|
188
|
+
return predicate(f, name=name, quant=Quantifier.EXISTS, bimap=bimap)
|
|
189
|
+
|
|
170
190
|
|
|
171
191
|
|
|
@@ -297,7 +297,7 @@ class Generator(Algebra[ParseResult[T], GenState[T]]):
|
|
|
297
297
|
def generate(syntax: Syntax[Any, Any],
|
|
298
298
|
data: Optional[ParseResult[Any]] = None,
|
|
299
299
|
seed: int = 0,
|
|
300
|
-
restore_pruned: bool = False) -> Tuple[AST, FrozenDict[str, AST]] | Tuple[Any, None]:
|
|
300
|
+
restore_pruned: bool = False) -> Tuple[AST, FrozenDict[str, Tuple[AST, ...]]] | Tuple[Any, None]:
|
|
301
301
|
from syncraft.syntax import run
|
|
302
302
|
return run(syntax, Generator, False, ast=data, seed=seed, restore_pruned=restore_pruned)
|
|
303
303
|
|
|
@@ -69,9 +69,9 @@ class ParserState(Bindable, Generic[T]):
|
|
|
69
69
|
@dataclass(frozen=True)
|
|
70
70
|
class Parser(Algebra[T, ParserState[T]]):
|
|
71
71
|
@classmethod
|
|
72
|
-
def state(cls, sql: str, dialect: str) -> ParserState[
|
|
72
|
+
def state(cls, sql: str, dialect: str) -> ParserState[T]:
|
|
73
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)
|
|
74
|
+
return ParserState.from_tokens(tokens) # type: ignore
|
|
75
75
|
|
|
76
76
|
@classmethod
|
|
77
77
|
def token(cls,
|
|
@@ -188,7 +188,7 @@ def sqlglot(parser: Syntax[Any, Any],
|
|
|
188
188
|
return parser.map(lambda tokens: [e for e in gp.parse(raw_tokens=tokens) if e is not None])
|
|
189
189
|
|
|
190
190
|
|
|
191
|
-
def parse(syntax: Syntax[Any, Any], sql: str, dialect: str) -> Tuple[AST, FrozenDict[str, AST]] | Tuple[Any, None]:
|
|
191
|
+
def parse(syntax: Syntax[Any, Any], sql: str, dialect: str) -> Tuple[AST, FrozenDict[str, Tuple[AST, ...]]] | Tuple[Any, None]:
|
|
192
192
|
from syncraft.syntax import run
|
|
193
193
|
return run(syntax, Parser, True, sql=sql, dialect=dialect)
|
|
194
194
|
|
|
@@ -319,7 +319,7 @@ def success(value: Any) -> Syntax[Any, Any]:
|
|
|
319
319
|
def choice(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
|
|
320
320
|
return reduce(lambda a, b: a | b, parsers) if len(parsers) > 0 else success(Nothing())
|
|
321
321
|
|
|
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]:
|
|
322
|
+
def run(syntax: Syntax[A, S], alg: Type[Algebra[A, S]], use_cache:bool, *args: Any, **kwargs: Any) -> Tuple[Any, FrozenDict[str, Tuple[Any, ...]]] | Tuple[Any, None]:
|
|
323
323
|
parser = syntax(alg)
|
|
324
324
|
input: Optional[S] = alg.state(*args, **kwargs)
|
|
325
325
|
if input:
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Any, List, Tuple
|
|
3
|
+
from syncraft.algebra import Either, Left, Right, Error
|
|
4
|
+
from syncraft.ast import Marked, Then, ThenKind, Many, Nothing
|
|
5
|
+
from syncraft.parser import literal, variable, parse, Parser, Token
|
|
6
|
+
from syncraft.generator import TokenGen
|
|
7
|
+
from syncraft.constraint import forall, exists
|
|
8
|
+
from rich import print
|
|
9
|
+
import syncraft.generator as gen
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_to() -> None:
|
|
14
|
+
@dataclass
|
|
15
|
+
class IfThenElse:
|
|
16
|
+
condition: Any
|
|
17
|
+
then: Any
|
|
18
|
+
otherwise: Any
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class While:
|
|
22
|
+
condition:Any
|
|
23
|
+
body:Any
|
|
24
|
+
|
|
25
|
+
WHILE = literal("while")
|
|
26
|
+
IF = literal("if")
|
|
27
|
+
ELSE = literal("else")
|
|
28
|
+
THEN = literal("then")
|
|
29
|
+
END = literal("end")
|
|
30
|
+
A = literal('a')
|
|
31
|
+
B = literal('b')
|
|
32
|
+
C = literal('c')
|
|
33
|
+
D = literal('d')
|
|
34
|
+
M = literal(',')
|
|
35
|
+
var = A | B | C | D
|
|
36
|
+
condition = var.sep_by(M).mark('condition').bind()
|
|
37
|
+
ifthenelse = (IF >> condition
|
|
38
|
+
// THEN
|
|
39
|
+
+ var.sep_by(M).mark('then').bind()
|
|
40
|
+
// ELSE
|
|
41
|
+
+ var.sep_by(M).mark('otherwise').bind()
|
|
42
|
+
// END).to(IfThenElse).many()
|
|
43
|
+
syntax = (WHILE >> condition
|
|
44
|
+
+ ifthenelse.mark('body').bind()
|
|
45
|
+
// ~END).to(While)
|
|
46
|
+
sql = 'while b if a,b then c,d else a,d end if a,b then c,d else a,d end'
|
|
47
|
+
ast, bound = parse(syntax, sql, dialect='sqlite')
|
|
48
|
+
def p(condition, then, otherwise)->bool:
|
|
49
|
+
print({'condition':condition, 'then':then, 'otherwise':otherwise})
|
|
50
|
+
return True
|
|
51
|
+
if bound is not None:
|
|
52
|
+
forall(p)(bound)
|
|
53
|
+
g, bound = gen.generate(syntax, ast, restore_pruned=True)
|
|
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
|