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 +53 -33
- syncraft/finder.py +149 -0
- syncraft/generator.py +15 -46
- syncraft/parser.py +0 -7
- syncraft/syntax.py +29 -8
- {syncraft-0.1.20.dist-info → syncraft-0.1.21.dist-info}/METADATA +1 -1
- syncraft-0.1.21.dist-info/RECORD +16 -0
- syncraft-0.1.20.dist-info/RECORD +0 -15
- {syncraft-0.1.20.dist-info → syncraft-0.1.21.dist-info}/WHEEL +0 -0
- {syncraft-0.1.20.dist-info → syncraft-0.1.21.dist-info}/licenses/LICENSE +0 -0
- {syncraft-0.1.20.dist-info → syncraft-0.1.21.dist-info}/top_level.txt +0 -0
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,
|
|
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,
|
|
69
|
-
|
|
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,
|
|
88
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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,
|
|
105
|
-
|
|
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
|
-
|
|
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,
|
|
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(
|
|
143
|
-
rb = self.right.bimap(
|
|
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
|
-
|
|
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,
|
|
5
|
-
List,
|
|
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,
|
|
11
|
-
OrResult, ManyResult
|
|
10
|
+
Algebra, Either, Left, Right, Error,
|
|
11
|
+
OrResult, ManyResult
|
|
12
12
|
)
|
|
13
13
|
|
|
14
|
-
from syncraft.ast import
|
|
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[
|
|
154
|
-
def flat_map(self, f: Callable[[
|
|
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[
|
|
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[
|
|
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[
|
|
225
|
-
) -> Algebra[OrResult[
|
|
226
|
-
def or_else_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[OrResult[
|
|
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[
|
|
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[
|
|
258
|
-
def token_run(input: GenState[T], use_cache:bool) -> Either[Any, Tuple[
|
|
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
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
|
|
|
@@ -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,,
|
syncraft-0.1.20.dist-info/RECORD
DELETED
|
@@ -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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|