Skip to content

Commit d4472f1

Browse files
committed
Add test_astlike_1.
1 parent a44650f commit d4472f1

1 file changed

Lines changed: 346 additions & 0 deletions

File tree

tests/test_astlike_1.py

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
import typing
2+
3+
from typemap.type_eval import eval_call_with_types, eval_typing
4+
from typemap.typing import (
5+
Attrs,
6+
BaseTypedDict,
7+
Bool,
8+
GetArg,
9+
GetName,
10+
GetType,
11+
IsSub,
12+
Iter,
13+
Matches,
14+
Member,
15+
NewProtocol,
16+
_BoolLiteral,
17+
)
18+
19+
20+
"""
21+
An AST like system for doing simple type checked computation.
22+
23+
Provides Constant and Variable nodes which can be used with operators
24+
(+, -, *, /, //, **, and %) to build up expression trees.
25+
26+
Calling eval on a Node with **kwargs corresponding to the Variables in
27+
the expression will compute and return the result.
28+
29+
Example usage:
30+
a = Variable[int, typing.Literal["a"]]()
31+
b = Variable[int, typing.Literal["b"]]()
32+
c = Constant(3)
33+
z = a + b * c
34+
result = eval(z, a=1, b=2)
35+
assert result is 7
36+
"""
37+
38+
39+
type VarArg[Name: str, T: type] = tuple[Name, T]
40+
41+
type VarArgName[V: VarArg] = GetArg[V, tuple, typing.Literal[0]]
42+
type VarArgType[V: VarArg] = GetArg[V, tuple, typing.Literal[1]]
43+
44+
45+
type CombineVarArgs[Ls: tuple[VarArg], Rs: tuple[VarArg]] = tuple[
46+
*[
47+
VarArg[
48+
VarArgName[x],
49+
(
50+
VarArgType[x]
51+
if not any( # Unique to Ls
52+
Matches[VarArgName[x], VarArgName[y]] for y in Iter[Rs]
53+
)
54+
else GetArg[ # Common to both Ls and Rs
55+
tuple[
56+
*[
57+
(
58+
VarArgType[x]
59+
if IsSub[VarArgType[x], VarArgType[y]]
60+
else VarArgType[y]
61+
if IsSub[VarArgType[y], VarArgType[x]]
62+
else typing.Never
63+
)
64+
for y in Iter[Rs]
65+
if Matches[VarArgName[x], VarArgName[y]]
66+
]
67+
],
68+
tuple,
69+
typing.Literal[0],
70+
]
71+
),
72+
]
73+
for x in Iter[Ls]
74+
],
75+
*[ # Unique to Rs
76+
x
77+
for x in Iter[Rs]
78+
if not any( # Unique to Rs
79+
Matches[VarArgName[x], VarArgName[y]] for y in Iter[Ls]
80+
)
81+
],
82+
]
83+
84+
85+
def test_astlike_1_combine_varargs_01():
86+
t = eval_typing(
87+
CombineVarArgs[
88+
tuple[
89+
VarArg[typing.Literal["same"], int],
90+
VarArg[typing.Literal["different"], int],
91+
VarArg[typing.Literal["left_sub"], bool],
92+
VarArg[typing.Literal["right_sub"], int],
93+
VarArg[typing.Literal["unique_left"], int],
94+
],
95+
tuple[
96+
VarArg[typing.Literal["same"], int],
97+
VarArg[typing.Literal["different"], float],
98+
VarArg[typing.Literal["left_sub"], int],
99+
VarArg[typing.Literal["right_sub"], bool],
100+
VarArg[typing.Literal["unique_right"], int],
101+
],
102+
]
103+
)
104+
assert (
105+
t
106+
== tuple[
107+
tuple[typing.Literal["same"], int],
108+
tuple[typing.Literal["different"], typing.Never],
109+
tuple[typing.Literal["left_sub"], bool],
110+
tuple[typing.Literal["right_sub"], bool],
111+
tuple[typing.Literal["unique_left"], int],
112+
tuple[typing.Literal["unique_right"], int],
113+
]
114+
)
115+
116+
117+
type IsAssignable[L, R] = (
118+
IsSub[R, L]
119+
or Bool[Matches[L, float] and Bool[IsFloat[R]]]
120+
or Bool[Matches[L, complex] and Bool[IsComplex[R]]]
121+
)
122+
type VarIsPresent[V: VarArg, K: BaseTypedDict] = any(
123+
Matches[VarArgName[V], GetName[x]]
124+
and Bool[IsAssignable[VarArgType[V], GetType[x]]]
125+
for x in Iter[Attrs[K]]
126+
)
127+
type AllVarsPresent[Vs: tuple[VarArg, ...], K: BaseTypedDict] = all(
128+
Bool[VarIsPresent[v, K]] for v in Iter[Vs]
129+
)
130+
131+
132+
def test_astlike_1_all_vars_present_01():
133+
t = eval_typing(
134+
AllVarsPresent[
135+
tuple[VarArg[typing.Literal["x"], int]],
136+
NewProtocol[Member[typing.Literal["x"], int]],
137+
]
138+
)
139+
assert t == _BoolLiteral[True]
140+
141+
142+
def test_astlike_1_all_vars_present_02():
143+
t = eval_typing(
144+
AllVarsPresent[
145+
tuple[VarArg[typing.Literal["x"], int]],
146+
NewProtocol[Member[typing.Literal["x"], bool]],
147+
]
148+
)
149+
assert t == _BoolLiteral[True]
150+
151+
152+
def test_astlike_1_all_vars_present_03():
153+
t = eval_typing(
154+
AllVarsPresent[
155+
tuple[VarArg[typing.Literal["x"], int]],
156+
NewProtocol[Member[typing.Literal["x"], float]],
157+
]
158+
)
159+
assert t == _BoolLiteral[False]
160+
161+
162+
def test_astlike_1_all_vars_present_04():
163+
t = eval_typing(
164+
AllVarsPresent[
165+
tuple[VarArg[typing.Literal["x"], int]],
166+
NewProtocol[
167+
Member[typing.Literal["x"], int],
168+
Member[typing.Literal["y"], int],
169+
],
170+
]
171+
)
172+
assert t == _BoolLiteral[True]
173+
174+
175+
def test_astlike_1_all_vars_present_05():
176+
t = eval_typing(
177+
AllVarsPresent[
178+
tuple[VarArg[typing.Literal["x"], int]],
179+
NewProtocol[Member[typing.Literal["y"], int]],
180+
]
181+
)
182+
assert t == _BoolLiteral[False]
183+
184+
185+
type IsIntegral[T] = IsSub[T, int]
186+
type IsFloat[T] = Bool[IsIntegral[T]] or IsSub[T, float]
187+
type IsComplex[T] = Bool[IsFloat[T]] or IsSub[T, complex]
188+
189+
type SimpleNumericOp[L, R] = (
190+
int
191+
if Bool[IsIntegral[L]] and Bool[IsIntegral[R]]
192+
else float
193+
if Bool[IsFloat[L]] and Bool[IsFloat[R]]
194+
else typing.Never
195+
)
196+
type ComplexNumericOp[L, R] = (
197+
SimpleNumericOp[L, R]
198+
if Bool[IsFloat[L]] and Bool[IsFloat[R]]
199+
else complex
200+
if Bool[IsComplex[L]] and Bool[IsComplex[R]]
201+
else typing.Never
202+
)
203+
204+
type Add[L, R] = (
205+
ComplexNumericOp[L, R]
206+
if Bool[IsComplex[L]] and Bool[IsComplex[R]]
207+
else typing.Never
208+
)
209+
type Sub[L, R] = (
210+
ComplexNumericOp[L, R]
211+
if Bool[IsComplex[L]] and Bool[IsComplex[R]]
212+
else typing.Never
213+
)
214+
type Mul[L, R] = (
215+
ComplexNumericOp[L, R]
216+
if Bool[IsComplex[L]] and Bool[IsComplex[R]]
217+
else typing.Never
218+
)
219+
type TrueDiv[L, R] = (
220+
float
221+
if IsSub[L, int] and IsSub[R, int]
222+
else ComplexNumericOp[L, R]
223+
if Bool[IsComplex[L]] and Bool[IsComplex[R]]
224+
else typing.Never
225+
)
226+
type FloorDiv[L, R] = ComplexNumericOp[L, R]
227+
type Pow[L, R] = ComplexNumericOp[L, R]
228+
type Mod[L, R] = (
229+
SimpleNumericOp[L, R]
230+
if Bool[IsFloat[L]] and Bool[IsFloat[R]]
231+
else typing.Never
232+
)
233+
234+
235+
class NodeMeta(type): ...
236+
237+
238+
class Node[T, Vs: tuple[VarArg, ...]](metaclass=NodeMeta):
239+
def __add__[OtherT, OtherVs: tuple[VarArg, ...]](
240+
self, other: Node[OtherT, OtherVs]
241+
) -> Node[Add[T, OtherT], CombineVarArgs[Vs, OtherVs]]: ...
242+
243+
def __sub__[OtherT, OtherVs: tuple[VarArg, ...]](
244+
self, other: Node[OtherT, OtherVs]
245+
) -> Node[Sub[T, OtherT], CombineVarArgs[Vs, OtherVs]]: ...
246+
247+
def __mul__[OtherT, OtherVs: tuple[VarArg, ...]](
248+
self, other: Node[OtherT, OtherVs]
249+
) -> Node[Mul[T, OtherT], CombineVarArgs[Vs, OtherVs]]: ...
250+
251+
def __truediv__[OtherT, OtherVs: tuple[VarArg, ...]](
252+
self, other: Node[OtherT, OtherVs]
253+
) -> Node[TrueDiv[T, OtherT], CombineVarArgs[Vs, OtherVs]]: ...
254+
255+
def __floordiv__[OtherT, OtherVs: tuple[VarArg, ...]](
256+
self, other: Node[OtherT, OtherVs]
257+
) -> Node[FloorDiv[T, OtherT], CombineVarArgs[Vs, OtherVs]]: ...
258+
259+
def __pow__[OtherT, OtherVs: tuple[VarArg, ...]](
260+
self, other: Node[OtherT, OtherVs]
261+
) -> Node[Pow[T, OtherT], CombineVarArgs[Vs, OtherVs]]: ...
262+
263+
def __mod__[OtherT, OtherVs: tuple[VarArg, ...]](
264+
self, other: Node[OtherT, OtherVs]
265+
) -> Node[Mod[T, OtherT], CombineVarArgs[Vs, OtherVs]]: ...
266+
267+
268+
class Constant[T](Node[T, tuple[()]]):
269+
value: typing.Any
270+
271+
def __init__(self, value) -> None:
272+
self.value = value
273+
274+
275+
def test_astlike_1_constant_01():
276+
t = eval_typing(Constant[int])
277+
assert t == Constant[int]
278+
279+
280+
def test_astlike_1_constant_02():
281+
t = eval_call_with_types(eval, Constant[int])
282+
assert t is int
283+
284+
t = eval_call_with_types(eval, Constant[int], x=1)
285+
assert t is int
286+
287+
288+
class Variable[T, Name: typing.Literal[str]](Node[T, tuple[VarArg[Name, T]]]):
289+
@property
290+
def name(self) -> typing.Literal[Name]:
291+
return self.__orig_class__.__args__[1].__args__[0]
292+
293+
def _eval(self, **kwargs) -> T:
294+
if self.name not in kwargs:
295+
raise ValueError(f"Expected '{self.name}' in kwargs")
296+
if not isinstance(kwargs[self.name], self.__orig_class__.__args__[0]):
297+
raise ValueError(
298+
f"Expected '{self.__orig_class__.__args__[0].__name__}', "
299+
f"got '{type(kwargs[self.name]).__name__}'"
300+
)
301+
return kwargs[self.name]
302+
303+
304+
def test_astlike_1_variable_01():
305+
n = Variable[int, typing.Literal["x"]]
306+
assert n().name == "x"
307+
308+
309+
def test_astlike_1_variable_02():
310+
t = eval_call_with_types(eval, Variable[int, typing.Literal["x"]], x=int)
311+
assert t is int
312+
t = eval_call_with_types(eval, Variable[int, typing.Literal["x"]], x=bool)
313+
assert t is int
314+
t = eval_call_with_types(eval, Variable[int, typing.Literal["x"]], x=str)
315+
assert t is typing.Never
316+
317+
318+
def eval[T, Vs: tuple[VarArg, ...], K: BaseTypedDict](
319+
self: Node[T, Vs], **kwargs: typing.Unpack[K]
320+
) -> T if Bool[AllVarsPresent[Vs, K]] else typing.Never: ...
321+
322+
323+
def test_astlike_1_eval_01():
324+
n = Node[int, tuple[VarArg[typing.Literal["x"], int]]]
325+
t = eval_call_with_types(eval, n, x=int)
326+
assert t is int
327+
t = eval_call_with_types(eval, n, x=bool)
328+
assert t is int
329+
t = eval_call_with_types(eval, n, x=str)
330+
assert t is typing.Never
331+
332+
333+
def test_astlike_1_eval_02():
334+
n = Node[
335+
complex,
336+
tuple[
337+
VarArg[typing.Literal["x"], float],
338+
VarArg[typing.Literal["y"], float],
339+
],
340+
]
341+
t = eval_call_with_types(eval, n, x=int, y=int)
342+
assert t is complex
343+
t = eval_call_with_types(eval, n, x=bool, y=float)
344+
assert t is complex
345+
t = eval_call_with_types(eval, n, x=str, y=complex)
346+
assert t is typing.Never

0 commit comments

Comments
 (0)