syncraft 0.1.37__tar.gz → 0.2.0__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 (28) hide show
  1. {syncraft-0.1.37 → syncraft-0.2.0}/PKG-INFO +1 -1
  2. {syncraft-0.1.37 → syncraft-0.2.0}/pyproject.toml +1 -1
  3. syncraft-0.2.0/syncraft/constraint.py +171 -0
  4. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/syntax.py +28 -26
  5. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft.egg-info/PKG-INFO +1 -1
  6. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft.egg-info/SOURCES.txt +2 -1
  7. syncraft-0.2.0/tests/test_var.py +0 -0
  8. syncraft-0.1.37/syncraft/constraint.py +0 -198
  9. {syncraft-0.1.37 → syncraft-0.2.0}/LICENSE +0 -0
  10. {syncraft-0.1.37 → syncraft-0.2.0}/README.md +0 -0
  11. {syncraft-0.1.37 → syncraft-0.2.0}/setup.cfg +0 -0
  12. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/__init__.py +0 -0
  13. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/algebra.py +0 -0
  14. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/ast.py +0 -0
  15. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/diagnostic.py +0 -0
  16. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/finder.py +0 -0
  17. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/generator.py +0 -0
  18. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/parser.py +0 -0
  19. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/py.typed +0 -0
  20. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft/sqlite3.py +0 -0
  21. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft.egg-info/dependency_links.txt +0 -0
  22. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft.egg-info/requires.txt +0 -0
  23. {syncraft-0.1.37 → syncraft-0.2.0}/syncraft.egg-info/top_level.txt +0 -0
  24. {syncraft-0.1.37 → syncraft-0.2.0}/tests/test_bimap.py +0 -0
  25. {syncraft-0.1.37 → syncraft-0.2.0}/tests/test_find.py +0 -0
  26. {syncraft-0.1.37 → syncraft-0.2.0}/tests/test_parse.py +0 -0
  27. {syncraft-0.1.37 → syncraft-0.2.0}/tests/test_to.py +0 -0
  28. {syncraft-0.1.37 → syncraft-0.2.0}/tests/test_until.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.37
3
+ Version: 0.2.0
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.1.37"
3
+ version = "0.2.0"
4
4
  description = "Parser combinator library"
5
5
  license = "MIT"
6
6
  license-files = ["LICENSE"]
@@ -0,0 +1,171 @@
1
+ from __future__ import annotations
2
+ from typing import Callable, Generic, Tuple, TypeVar, Optional, Any, Self
3
+ from enum import Enum
4
+ from dataclasses import dataclass, field, replace
5
+ import collections.abc
6
+ from collections import defaultdict
7
+ from itertools import product
8
+ import inspect
9
+
10
+ K = TypeVar('K')
11
+ V = TypeVar('V')
12
+ class FrozenDict(collections.abc.Mapping, Generic[K, V]):
13
+ def __init__(self, *args, **kwargs):
14
+ self._data = dict(*args, **kwargs)
15
+ self._hash = None
16
+ def __getitem__(self, key):
17
+ return self._data[key]
18
+
19
+ def __iter__(self):
20
+ return iter(self._data)
21
+
22
+ def __len__(self):
23
+ return len(self._data)
24
+
25
+ def __hash__(self):
26
+ if self._hash is None:
27
+ self._hash = hash(frozenset(self._data.items()))
28
+ return self._hash
29
+
30
+ def __eq__(self, other):
31
+ if isinstance(other, collections.abc.Mapping):
32
+ return self._data == other
33
+ return NotImplemented
34
+
35
+ def __repr__(self):
36
+ return f"{self.__class__.__name__}({self._data})"
37
+
38
+ @dataclass(frozen=True)
39
+ class Binding:
40
+ bindings : frozenset[Tuple[str, Any]] = frozenset()
41
+ def bind(self, name: str, node: Any) -> Binding:
42
+ new_binding = set(self.bindings)
43
+ new_binding.add((name, node))
44
+ return Binding(bindings=frozenset(new_binding))
45
+
46
+ def bound(self)->FrozenDict[str, Tuple[Any, ...]]:
47
+ ret = defaultdict(list)
48
+ for name, node in self.bindings:
49
+ ret[name].append(node)
50
+ return FrozenDict({k: tuple(vs) for k, vs in ret.items()})
51
+
52
+
53
+
54
+ @dataclass(frozen=True)
55
+ class Bindable:
56
+ binding: Binding = field(default_factory=Binding)
57
+
58
+ def map(self, f: Callable[[Any], Any])->Self:
59
+ return self
60
+
61
+ def bind(self, name: str, node:Any)->Self:
62
+ return replace(self, binding=self.binding.bind(name, node))
63
+
64
+
65
+ class Quantifier(Enum):
66
+ FORALL = "forall"
67
+ EXISTS = "exists"
68
+
69
+ @dataclass(frozen=True)
70
+ class ConstraintResult:
71
+ result: bool
72
+ unbound: frozenset[str] = frozenset()
73
+ @dataclass(frozen=True)
74
+ class Constraint:
75
+ run_f: Callable[[FrozenDict[str, Tuple[Any, ...]]], ConstraintResult]
76
+ name: str = ""
77
+ def __call__(self, bound: FrozenDict[str, Tuple[Any, ...]])->ConstraintResult:
78
+ return self.run_f(bound)
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)
86
+ return Constraint(
87
+ run_f=and_run,
88
+ name=f"({self.name} && {other.name})"
89
+ )
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)
97
+ return Constraint(
98
+ run_f=or_run,
99
+ name=f"({self.name} || {other.name})"
100
+ )
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)
108
+ return Constraint(
109
+ run_f=xor_run,
110
+ name=f"({self.name} ^ {other.name})"
111
+ )
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)
116
+ return Constraint(
117
+ run_f=invert_run,
118
+ name=f"!({self.name})"
119
+ )
120
+
121
+ @classmethod
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)
167
+
168
+ def exists(f: Callable[..., bool], name: Optional[str] = None) -> Constraint:
169
+ return Constraint.predicate(f, name=name, quant=Quantifier.EXISTS)
170
+
171
+
@@ -2,17 +2,22 @@ from __future__ import annotations
2
2
 
3
3
  from typing import (
4
4
  Optional, Any, TypeVar, Generic, Callable, Tuple, cast,
5
- Type, Literal, List
5
+ Type, Literal, List, overload
6
6
  )
7
7
  from dataclasses import dataclass, field, replace
8
8
  from functools import reduce
9
9
  from syncraft.algebra import Algebra, Error, Either, Right
10
- from syncraft.constraint import Variable, Bindable
10
+ from syncraft.constraint import Bindable
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
15
  from rich import print
15
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('__')))
16
21
 
17
22
  A = TypeVar('A') # Result type
18
23
  B = TypeVar('B') # Result type for mapping
@@ -249,21 +254,18 @@ class Syntax(Generic[A, S]):
249
254
  return self.optional()
250
255
 
251
256
 
252
- ######################################################################## data processing combinators #########################################################
253
- def bind(self,
254
- var: Variable,
255
- collector: Optional[Type[E]]=None) -> Syntax[A |
256
- Marked[A] |
257
- Marked[Collect[A, E]] |
258
- Collect[A, E], S]:
259
- def bind_v(v: A | Marked[A] | Marked[Collect[A, E]] | Collect[A, E],
260
- s: S)->Tuple[A | Marked[A] | Marked[Collect[A, E]] | Collect[A, E], S]:
261
- return v, s.bind(var, v)
262
- if callable(collector):
263
- ret = self.to(collector).mark(var.name).map_all(bind_v) if var.name else self.to(collector).map_all(bind_v)
264
- else:
265
- ret = self.mark(var.name).map_all(bind_v) if var.name else self.map_all(bind_v)
266
- return ret.describe(name=f'bind({var.name})', fixity='postfix', parameter=(self,))
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}"
261
+ def bind_v(v: Any, s: S)->Tuple[Any, S]:
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,))
267
269
 
268
270
  def to(self, f: Collector[E])-> Syntax[Collect[A, E], S]:
269
271
  def to_f(v: A) -> Collect[A, E]:
@@ -277,15 +279,16 @@ class Syntax(Generic[A, S]):
277
279
 
278
280
 
279
281
  def mark(self, name: str) -> Syntax[Marked[A], S]:
280
- 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]:
281
284
  if isinstance(value, Marked):
282
285
  return replace(value, name=name)
283
286
  else:
284
287
  return Marked(name=name, value=value)
285
- def ibind_s(m : Marked[A]) -> A:
288
+ def imark_s(m : Marked[A]) -> A:
286
289
  return m.value if isinstance(m, Marked) else m
287
290
 
288
- 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,))
289
292
 
290
293
 
291
294
 
@@ -326,15 +329,14 @@ def first(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
326
329
  def last(*parsers: Syntax[Any, S]) -> Syntax[Any, S]:
327
330
  return reduce(lambda a, b: a >> b, parsers) if len(parsers) > 0 else success(Nothing())
328
331
 
329
- def bound(* parsers: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]]) -> Syntax[Any, S]:
332
+ def bind(* parsers: Syntax[Any, S] | Tuple[str, Syntax[Any, S]]) -> Syntax[Any, S]:
330
333
  def is_named_parser(x: Any) -> bool:
331
- return isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], (str, Variable)) and isinstance(x[1], Syntax)
334
+ return isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], str) and isinstance(x[1], Syntax)
332
335
 
333
- def to_parser(x: Syntax[Any, S] | Tuple[str|Variable, Syntax[Any, S]])->Syntax[Any, S]:
334
- if isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], (str, Variable)) and isinstance(x[1], Syntax):
336
+ def to_parser(x: Syntax[Any, S] | Tuple[str, Syntax[Any, S]])->Syntax[Any, S]:
337
+ if isinstance(x, tuple) and len(x) == 2 and isinstance(x[0], str) and isinstance(x[1], Syntax):
335
338
  if isinstance(x[0], str):
336
- return x[1].mark(x[0])
337
- elif isinstance(x[0], Variable):
339
+
338
340
  return x[1].bind(x[0])
339
341
  else:
340
342
  raise ValueError(f"Invalid variable type(must be str | Variable): {x[0]}", x)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syncraft
3
- Version: 0.1.37
3
+ Version: 0.2.0
4
4
  Summary: Parser combinator library
5
5
  Author-email: Michael Afmokt <michael@esacca.com>
6
6
  License-Expression: MIT
@@ -21,4 +21,5 @@ tests/test_bimap.py
21
21
  tests/test_find.py
22
22
  tests/test_parse.py
23
23
  tests/test_to.py
24
- tests/test_until.py
24
+ tests/test_until.py
25
+ tests/test_var.py
File without changes
@@ -1,198 +0,0 @@
1
- from __future__ import annotations
2
- from typing import Callable, Generic, Tuple, TypeVar, Optional, Any, Self
3
- from enum import Enum
4
- from dataclasses import dataclass, field, replace
5
- import collections.abc
6
- from collections import defaultdict
7
- from itertools import product
8
-
9
- K = TypeVar('K')
10
- V = TypeVar('V')
11
- class FrozenDict(collections.abc.Mapping, Generic[K, V]):
12
- def __init__(self, *args, **kwargs):
13
- self._data = dict(*args, **kwargs)
14
- self._hash = None
15
- def __getitem__(self, key):
16
- return self._data[key]
17
-
18
- def __iter__(self):
19
- return iter(self._data)
20
-
21
- def __len__(self):
22
- return len(self._data)
23
-
24
- def __hash__(self):
25
- if self._hash is None:
26
- self._hash = hash(frozenset(self._data.items()))
27
- return self._hash
28
-
29
- def __eq__(self, other):
30
- if isinstance(other, collections.abc.Mapping):
31
- return self._data == other
32
- return NotImplemented
33
-
34
- def __repr__(self):
35
- 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
-
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
- @dataclass(frozen=True)
100
- class Binding:
101
- bindings : frozenset[Tuple[Variable, Any]] = frozenset()
102
- def bind(self, var: Variable, node: Any) -> Binding:
103
- new_binding = set(self.bindings)
104
- new_binding.add((var, node))
105
- return Binding(bindings=frozenset(new_binding))
106
-
107
- def to_dict(self)->BoundVar:
108
- ret = defaultdict(list)
109
- for var, node in self.bindings:
110
- ret[var].append(node)
111
- return FrozenDict({k: tuple(vs) for k, vs in ret.items()})
112
-
113
-
114
- A = TypeVar('A')
115
- @dataclass(frozen=True)
116
- class Bindable:
117
- binding: Binding = field(default_factory=Binding)
118
-
119
- def map(self, f: Callable[[Any], Any])->Self:
120
- return self
121
-
122
- def bind(self, var: Variable, node:Any)->Self:
123
- return replace(self, binding=self.binding.bind(var, node))
124
-
125
-
126
- class Quantifier(Enum):
127
- FORALL = "forall"
128
- EXISTS = "exists"
129
-
130
- @dataclass(frozen=True)
131
- class Constraint:
132
- run_f: Callable[[BoundVar], bool]
133
- name: str = ""
134
- def __call__(self, bound: BoundVar)->bool:
135
- return self.run_f(bound)
136
- def __and__(self, other: Constraint) -> Constraint:
137
- return Constraint(
138
- run_f=lambda bound: self(bound) and other(bound),
139
- name=f"({self.name} && {other.name})"
140
- )
141
- def __or__(self, other: Constraint) -> Constraint:
142
- return Constraint(
143
- run_f=lambda bound: self(bound) or other(bound),
144
- name=f"({self.name} || {other.name})"
145
- )
146
- def __xor__(self, other: Constraint) -> Constraint:
147
- return Constraint(
148
- run_f=lambda bound: self(bound) ^ other(bound),
149
- name=f"({self.name} ^ {other.name})"
150
- )
151
- def __invert__(self) -> Constraint:
152
- return Constraint(
153
- run_f=lambda bound: not self(bound),
154
- name=f"!({self.name})"
155
- )
156
-
157
- @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)
193
-
194
- @classmethod
195
- def exists(cls, f: Callable[..., bool], name: Optional[str] = None):
196
- return cls.predicate(f, name=name, quant=Quantifier.EXISTS)
197
-
198
-
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