omlish 0.0.0.dev70__py3-none-any.whl → 0.0.0.dev71__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.
- omlish/__about__.py +3 -3
- omlish/dataclasses/impl/internals.py +1 -0
- omlish/dataclasses/impl/metaclass.py +22 -5
- omlish/dataclasses/impl/params.py +4 -2
- omlish/diag/_pycharm/runhack.py +9 -4
- omlish/http/__init__.py +17 -0
- omlish/http/client.py +142 -0
- omlish/http/headers.py +146 -0
- omlish/sql/queries/__init__.py +79 -0
- omlish/sql/queries/base.py +21 -0
- omlish/sql/queries/binary.py +48 -0
- omlish/sql/queries/exprs.py +54 -0
- omlish/sql/queries/idents.py +29 -0
- omlish/sql/queries/multi.py +41 -0
- omlish/sql/queries/names.py +34 -0
- omlish/sql/queries/relations.py +39 -0
- omlish/sql/queries/selects.py +50 -0
- omlish/sql/queries/std.py +29 -0
- omlish/sql/queries/stmts.py +28 -0
- omlish/sql/queries/unary.py +29 -0
- omlish/sql/tabledefs/__init__.py +11 -0
- omlish/sql/tabledefs/alchemy.py +26 -0
- omlish/sql/tabledefs/dtypes.py +22 -0
- omlish/sql/tabledefs/elements.py +88 -0
- omlish/sql/tabledefs/lower.py +49 -0
- omlish/sql/tabledefs/marshal.py +19 -0
- omlish/sql/tabledefs/tabledefs.py +52 -0
- {omlish-0.0.0.dev70.dist-info → omlish-0.0.0.dev71.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev70.dist-info → omlish-0.0.0.dev71.dist-info}/RECORD +33 -12
- {omlish-0.0.0.dev70.dist-info → omlish-0.0.0.dev71.dist-info}/LICENSE +0 -0
- {omlish-0.0.0.dev70.dist-info → omlish-0.0.0.dev71.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev70.dist-info → omlish-0.0.0.dev71.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev70.dist-info → omlish-0.0.0.dev71.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
__version__ = '0.0.0.
|
2
|
-
__revision__ = '
|
1
|
+
__version__ = '0.0.0.dev71'
|
2
|
+
__revision__ = '293695b11eeed255848dcf6beee9c4b4383d5c91'
|
3
3
|
|
4
4
|
|
5
5
|
#
|
@@ -37,7 +37,7 @@ class Project(ProjectBase):
|
|
37
37
|
|
38
38
|
'greenlet ~= 3.1',
|
39
39
|
|
40
|
-
'trio ~= 0.
|
40
|
+
'trio ~= 0.27',
|
41
41
|
'trio-asyncio ~= 0.15',
|
42
42
|
],
|
43
43
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
TODO:
|
3
|
-
-
|
3
|
+
- Rewrite lol
|
4
|
+
- Enum - enforce Abstract or Final
|
4
5
|
"""
|
5
6
|
import abc
|
6
7
|
import collections
|
@@ -13,6 +14,7 @@ from .api import field # noqa
|
|
13
14
|
from .params import MetaclassParams
|
14
15
|
from .params import get_metaclass_params
|
15
16
|
from .params import get_params
|
17
|
+
from .params import get_params_extras
|
16
18
|
|
17
19
|
|
18
20
|
T = ta.TypeVar('T')
|
@@ -34,22 +36,35 @@ def confer_kwargs(
|
|
34
36
|
for base in bases:
|
35
37
|
if not dc.is_dataclass(base):
|
36
38
|
continue
|
39
|
+
|
37
40
|
if not (bmp := get_metaclass_params(base)).confer:
|
38
41
|
continue
|
42
|
+
|
39
43
|
for ck in bmp.confer:
|
40
44
|
if ck in kwargs:
|
41
45
|
continue
|
46
|
+
|
42
47
|
if ck in (
|
43
48
|
'frozen',
|
44
|
-
'generic_init',
|
45
49
|
'kw_only',
|
50
|
+
):
|
51
|
+
confer_kwarg(out, ck, getattr(get_params(base), ck))
|
52
|
+
|
53
|
+
elif ck in (
|
54
|
+
'cache_hash',
|
55
|
+
'generic_init',
|
46
56
|
'reorder',
|
47
57
|
):
|
48
|
-
confer_kwarg(out, ck,
|
49
|
-
|
50
|
-
|
58
|
+
confer_kwarg(out, ck, getattr(get_params_extras(base), ck))
|
59
|
+
|
60
|
+
elif ck in (
|
61
|
+
'confer',
|
62
|
+
):
|
63
|
+
confer_kwarg(out, ck, getattr(bmp, ck))
|
64
|
+
|
51
65
|
else:
|
52
66
|
raise KeyError(ck)
|
67
|
+
|
53
68
|
return out
|
54
69
|
|
55
70
|
|
@@ -112,6 +127,7 @@ class Frozen(
|
|
112
127
|
frozen=True,
|
113
128
|
confer=frozenset([
|
114
129
|
'frozen',
|
130
|
+
'cache_hash',
|
115
131
|
'confer',
|
116
132
|
]),
|
117
133
|
):
|
@@ -124,6 +140,7 @@ class Box(
|
|
124
140
|
generic_init=True,
|
125
141
|
confer=frozenset([
|
126
142
|
'frozen',
|
143
|
+
'cache_hash',
|
127
144
|
'generic_init',
|
128
145
|
'confer',
|
129
146
|
]),
|
@@ -1,5 +1,6 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
@dc.dataclass(frozen=True)
|
3
|
+
class Field_:
|
3
4
|
name: str | None = None
|
4
5
|
type: Any = None
|
5
6
|
default: Any | MISSING = MISSING
|
@@ -14,7 +15,8 @@ Field:
|
|
14
15
|
_field_type: Any = None
|
15
16
|
|
16
17
|
|
17
|
-
|
18
|
+
@dc.dataclass(frozen=True)
|
19
|
+
class Params_:
|
18
20
|
init: bool = True
|
19
21
|
repr: bool = True
|
20
22
|
eq: bool = True
|
omlish/diag/_pycharm/runhack.py
CHANGED
@@ -1038,11 +1038,13 @@ class ExecDecider:
|
|
1038
1038
|
if not isinstance(tgt, FileTarget):
|
1039
1039
|
return None
|
1040
1040
|
|
1041
|
-
|
1041
|
+
abs_file = os.path.abspath(tgt.file)
|
1042
|
+
if os.path.commonpath([abs_file, self._root_dir]) != self._root_dir:
|
1043
|
+
return None
|
1042
1044
|
|
1043
1045
|
return ExecDecision(
|
1044
1046
|
tgt.replace(
|
1045
|
-
file=
|
1047
|
+
file=abs_file,
|
1046
1048
|
),
|
1047
1049
|
cwd=self._root_dir,
|
1048
1050
|
)
|
@@ -1070,8 +1072,11 @@ class ExecDecider:
|
|
1070
1072
|
if not (isinstance(dt, FileTarget) and dt.file.endswith('.py')):
|
1071
1073
|
return None
|
1072
1074
|
|
1073
|
-
|
1074
|
-
|
1075
|
+
abs_file = os.path.abspath(dt.file)
|
1076
|
+
if os.path.commonpath([abs_file, self._root_dir]) != self._root_dir:
|
1077
|
+
return None
|
1078
|
+
|
1079
|
+
rp = os.path.relpath(abs_file, self._root_dir).split(os.path.sep)
|
1075
1080
|
mod = '.'.join([*rp[:-1], rp[-1][:-3]])
|
1076
1081
|
new_dt = ModuleTarget(
|
1077
1082
|
mod,
|
omlish/http/__init__.py
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
from . import consts # noqa
|
2
2
|
|
3
|
+
from .client import ( # noqa
|
4
|
+
HttpClient,
|
5
|
+
HttpClientError,
|
6
|
+
HttpRequest,
|
7
|
+
HttpResponse,
|
8
|
+
HttpxHttpClient,
|
9
|
+
UrllibHttpClient,
|
10
|
+
client,
|
11
|
+
request,
|
12
|
+
)
|
13
|
+
|
3
14
|
from .cookies import ( # noqa
|
4
15
|
CookieTooBigError,
|
5
16
|
dump_cookie,
|
@@ -16,6 +27,12 @@ from .encodings import ( # noqa
|
|
16
27
|
latin1_encode,
|
17
28
|
)
|
18
29
|
|
30
|
+
from .headers import ( # noqa
|
31
|
+
CanHttpHeaders,
|
32
|
+
HttpHeaders,
|
33
|
+
headers,
|
34
|
+
)
|
35
|
+
|
19
36
|
from .json import ( # noqa
|
20
37
|
JSON_TAGGER,
|
21
38
|
JsonTag,
|
omlish/http/client.py
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
"""
|
2
|
+
TODO:
|
3
|
+
- return non-200 HttpResponses
|
4
|
+
- async
|
5
|
+
- stream
|
6
|
+
"""
|
7
|
+
import abc
|
8
|
+
import http.client
|
9
|
+
import typing as ta
|
10
|
+
import urllib.error
|
11
|
+
import urllib.request
|
12
|
+
|
13
|
+
from .. import cached
|
14
|
+
from .. import dataclasses as dc
|
15
|
+
from .. import lang
|
16
|
+
from .headers import CanHttpHeaders
|
17
|
+
from .headers import HttpHeaders
|
18
|
+
|
19
|
+
|
20
|
+
if ta.TYPE_CHECKING:
|
21
|
+
import httpx
|
22
|
+
else:
|
23
|
+
httpx = lang.proxy_import('httpx')
|
24
|
+
|
25
|
+
|
26
|
+
@dc.dataclass(frozen=True)
|
27
|
+
class HttpRequest(lang.Final):
|
28
|
+
url: str
|
29
|
+
method: str = 'GET' # noqa
|
30
|
+
|
31
|
+
_: dc.KW_ONLY
|
32
|
+
|
33
|
+
headers: CanHttpHeaders | None = dc.xfield(None, repr=dc.truthy_repr)
|
34
|
+
data: bytes | None = dc.xfield(None, repr_fn=lambda v: '...' if v is not None else None)
|
35
|
+
|
36
|
+
timeout_s: float | None = None
|
37
|
+
|
38
|
+
@cached.property
|
39
|
+
def headers_(self) -> HttpHeaders | None:
|
40
|
+
return HttpHeaders(self.headers) if self.headers is not None else None
|
41
|
+
|
42
|
+
|
43
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
44
|
+
class HttpResponse(lang.Final):
|
45
|
+
code: int
|
46
|
+
|
47
|
+
headers: HttpHeaders | None = dc.xfield(None, repr=dc.truthy_repr)
|
48
|
+
data: bytes | None = dc.xfield(None, repr_fn=lambda v: '...' if v is not None else None)
|
49
|
+
|
50
|
+
request: HttpRequest
|
51
|
+
underlying: ta.Any = dc.field(default=None, repr=False)
|
52
|
+
|
53
|
+
|
54
|
+
class HttpClientError(Exception):
|
55
|
+
pass
|
56
|
+
|
57
|
+
|
58
|
+
class HttpClient(lang.Abstract):
|
59
|
+
def __enter__(self) -> ta.Self:
|
60
|
+
return self
|
61
|
+
|
62
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
63
|
+
pass
|
64
|
+
|
65
|
+
@abc.abstractmethod
|
66
|
+
def request(self, req: HttpRequest) -> HttpResponse:
|
67
|
+
raise NotImplementedError
|
68
|
+
|
69
|
+
|
70
|
+
class UrllibHttpClient(HttpClient):
|
71
|
+
def request(self, req: HttpRequest) -> HttpResponse:
|
72
|
+
try:
|
73
|
+
with urllib.request.urlopen( # noqa
|
74
|
+
urllib.request.Request( # noqa
|
75
|
+
req.url,
|
76
|
+
method=req.method,
|
77
|
+
headers=req.headers_ or {}, # type: ignore
|
78
|
+
data=req.data,
|
79
|
+
),
|
80
|
+
timeout=req.timeout_s,
|
81
|
+
) as resp:
|
82
|
+
return HttpResponse(
|
83
|
+
code=resp.status,
|
84
|
+
headers=HttpHeaders(resp.headers.items()),
|
85
|
+
data=resp.read(),
|
86
|
+
request=req,
|
87
|
+
underlying=resp,
|
88
|
+
)
|
89
|
+
except (urllib.error.URLError, http.client.HTTPException) as e:
|
90
|
+
raise HttpClientError from e
|
91
|
+
|
92
|
+
|
93
|
+
class HttpxHttpClient(HttpClient):
|
94
|
+
def request(self, req: HttpRequest) -> HttpResponse:
|
95
|
+
try:
|
96
|
+
response = httpx.request(
|
97
|
+
method=req.method,
|
98
|
+
url=req.url,
|
99
|
+
headers=req.headers_ or None, # type: ignore
|
100
|
+
content=req.data,
|
101
|
+
timeout=req.timeout_s,
|
102
|
+
)
|
103
|
+
return HttpResponse(
|
104
|
+
code=response.status_code,
|
105
|
+
headers=HttpHeaders(response.headers.raw),
|
106
|
+
data=response.content,
|
107
|
+
request=req,
|
108
|
+
underlying=response,
|
109
|
+
)
|
110
|
+
except httpx.HTTPError as e:
|
111
|
+
raise HttpClientError from e
|
112
|
+
|
113
|
+
|
114
|
+
def client() -> HttpClient:
|
115
|
+
return UrllibHttpClient()
|
116
|
+
|
117
|
+
|
118
|
+
def request(
|
119
|
+
url: str,
|
120
|
+
method: str = 'GET',
|
121
|
+
*,
|
122
|
+
headers: CanHttpHeaders | None = None,
|
123
|
+
data: bytes | None = None,
|
124
|
+
|
125
|
+
timeout_s: float | None = None,
|
126
|
+
|
127
|
+
**kwargs: ta.Any,
|
128
|
+
) -> HttpResponse:
|
129
|
+
req = HttpRequest(
|
130
|
+
url,
|
131
|
+
method=method,
|
132
|
+
|
133
|
+
headers=headers,
|
134
|
+
data=data,
|
135
|
+
|
136
|
+
timeout_s=timeout_s,
|
137
|
+
|
138
|
+
**kwargs,
|
139
|
+
)
|
140
|
+
|
141
|
+
with client() as cli:
|
142
|
+
return cli.request(req)
|
omlish/http/headers.py
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from .. import cached
|
4
|
+
from .. import check
|
5
|
+
from .. import collections as col
|
6
|
+
|
7
|
+
|
8
|
+
StrOrBytes: ta.TypeAlias = str | bytes
|
9
|
+
|
10
|
+
CanHttpHeaders: ta.TypeAlias = ta.Union[
|
11
|
+
'HttpHeaders',
|
12
|
+
ta.Mapping[StrOrBytes, StrOrBytes],
|
13
|
+
ta.Mapping[StrOrBytes, ta.Sequence[StrOrBytes]],
|
14
|
+
ta.Mapping[StrOrBytes, StrOrBytes | ta.Sequence[StrOrBytes]],
|
15
|
+
ta.Sequence[tuple[StrOrBytes, StrOrBytes]],
|
16
|
+
]
|
17
|
+
|
18
|
+
|
19
|
+
class HttpHeaders:
|
20
|
+
def __init__(self, src: CanHttpHeaders) -> None:
|
21
|
+
super().__init__()
|
22
|
+
|
23
|
+
if isinstance(src, HttpHeaders):
|
24
|
+
check.is_(src, self)
|
25
|
+
return
|
26
|
+
|
27
|
+
# TODO: optimized storage, 'use-whats-given'
|
28
|
+
lst: list[tuple[bytes, bytes]] = []
|
29
|
+
if isinstance(src, ta.Mapping):
|
30
|
+
for k, v in src.items():
|
31
|
+
if isinstance(v, (str, bytes)):
|
32
|
+
lst.append((self._as_bytes(k), self._as_bytes(v)))
|
33
|
+
else:
|
34
|
+
for e in v:
|
35
|
+
lst.append((self._as_bytes(k), self._as_bytes(e)))
|
36
|
+
|
37
|
+
elif isinstance(src, (str, bytes)): # type: ignore
|
38
|
+
raise TypeError(src)
|
39
|
+
|
40
|
+
elif isinstance(src, ta.Sequence):
|
41
|
+
for k, v in src:
|
42
|
+
lst.append((self._as_bytes(k), self._as_bytes(v)))
|
43
|
+
|
44
|
+
else:
|
45
|
+
raise TypeError(src)
|
46
|
+
|
47
|
+
self._lst = lst
|
48
|
+
|
49
|
+
def __new__(cls, obj: CanHttpHeaders) -> 'HttpHeaders':
|
50
|
+
if isinstance(obj, HttpHeaders):
|
51
|
+
return obj
|
52
|
+
|
53
|
+
return super().__new__(cls)
|
54
|
+
|
55
|
+
#
|
56
|
+
|
57
|
+
# https://github.com/pgjones/hypercorn/commit/13f385be7277f407a9a361c958820515e16e217e
|
58
|
+
ENCODING: ta.ClassVar[str] = 'latin1'
|
59
|
+
|
60
|
+
@classmethod
|
61
|
+
def _as_bytes(cls, o: StrOrBytes) -> bytes:
|
62
|
+
if isinstance(o, bytes):
|
63
|
+
return o
|
64
|
+
elif isinstance(o, str):
|
65
|
+
return o.encode(cls.ENCODING)
|
66
|
+
else:
|
67
|
+
raise TypeError(o)
|
68
|
+
|
69
|
+
#
|
70
|
+
|
71
|
+
@cached.function
|
72
|
+
def __repr__(self) -> str:
|
73
|
+
return f'{self.__class__.__name__}({{{", ".join(repr(k) for k in self.single_str_dct)}}})'
|
74
|
+
|
75
|
+
#
|
76
|
+
|
77
|
+
@cached.property
|
78
|
+
def multi_dct(self) -> ta.Mapping[bytes, ta.Sequence[bytes]]:
|
79
|
+
return col.multi_map(self._lst)
|
80
|
+
|
81
|
+
@cached.property
|
82
|
+
def single_dct(self) -> ta.Mapping[bytes, bytes]:
|
83
|
+
return {k: v[0] for k, v in self.multi_dct.items() if len(v) == 1}
|
84
|
+
|
85
|
+
@cached.property
|
86
|
+
def strict_dct(self) -> ta.Mapping[bytes, bytes]:
|
87
|
+
return col.make_map(self._lst, strict=True)
|
88
|
+
|
89
|
+
#
|
90
|
+
|
91
|
+
@cached.property
|
92
|
+
def strs(self) -> ta.Sequence[tuple[str, str]]:
|
93
|
+
return tuple((k.decode(self.ENCODING), v.decode(self.ENCODING)) for k, v in self._lst)
|
94
|
+
|
95
|
+
@cached.property
|
96
|
+
def multi_str_dct(self) -> ta.Mapping[str, ta.Sequence[str]]:
|
97
|
+
return col.multi_map(self.strs)
|
98
|
+
|
99
|
+
@cached.property
|
100
|
+
def single_str_dct(self) -> ta.Mapping[str, str]:
|
101
|
+
return {k: v[0] for k, v in self.multi_str_dct.items() if len(v) == 1}
|
102
|
+
|
103
|
+
@cached.property
|
104
|
+
def strict_str_dct(self) -> ta.Mapping[str, str]:
|
105
|
+
return col.make_map(self.strs, strict=True)
|
106
|
+
|
107
|
+
#
|
108
|
+
|
109
|
+
def __bool__(self) -> bool:
|
110
|
+
return bool(self._lst)
|
111
|
+
|
112
|
+
def __len__(self) -> int:
|
113
|
+
return len(self._lst)
|
114
|
+
|
115
|
+
def __iter__(self) -> ta.Iterator[tuple[bytes, bytes]]:
|
116
|
+
return iter(self._lst)
|
117
|
+
|
118
|
+
@ta.overload
|
119
|
+
def __getitem__(self, item: StrOrBytes) -> ta.Sequence[StrOrBytes]:
|
120
|
+
...
|
121
|
+
|
122
|
+
@ta.overload
|
123
|
+
def __getitem__(self, item: int) -> StrOrBytes:
|
124
|
+
...
|
125
|
+
|
126
|
+
@ta.overload
|
127
|
+
def __getitem__(self, item: slice) -> ta.Sequence[StrOrBytes]:
|
128
|
+
...
|
129
|
+
|
130
|
+
def __getitem__(self, item):
|
131
|
+
if isinstance(item, (int, slice)):
|
132
|
+
return self._lst[item]
|
133
|
+
elif isinstance(item, (str, bytes)):
|
134
|
+
return self.multi_dct[self._as_bytes(item)]
|
135
|
+
else:
|
136
|
+
raise TypeError(item)
|
137
|
+
|
138
|
+
def keys(self) -> ta.Iterable[bytes]:
|
139
|
+
return self.multi_dct.keys()
|
140
|
+
|
141
|
+
def items(self) -> ta.Iterable[tuple[bytes, bytes]]:
|
142
|
+
return self._lst
|
143
|
+
|
144
|
+
|
145
|
+
def headers(src: CanHttpHeaders) -> HttpHeaders:
|
146
|
+
return HttpHeaders(src)
|
@@ -0,0 +1,79 @@
|
|
1
|
+
from .base import ( # noqa
|
2
|
+
Builder,
|
3
|
+
Node,
|
4
|
+
Value,
|
5
|
+
)
|
6
|
+
|
7
|
+
from .binary import ( # noqa
|
8
|
+
Binary,
|
9
|
+
BinaryBuilder,
|
10
|
+
BinaryOp,
|
11
|
+
BinaryOps,
|
12
|
+
)
|
13
|
+
|
14
|
+
from .exprs import ( # noqa
|
15
|
+
CanExpr,
|
16
|
+
CanLiteral,
|
17
|
+
Expr,
|
18
|
+
ExprBuilder,
|
19
|
+
Literal,
|
20
|
+
NameExpr,
|
21
|
+
)
|
22
|
+
|
23
|
+
from .idents import ( # noqa
|
24
|
+
CanIdent,
|
25
|
+
Ident,
|
26
|
+
IdentBuilder,
|
27
|
+
)
|
28
|
+
|
29
|
+
from .multi import ( # noqa
|
30
|
+
Multi,
|
31
|
+
MultiBuilder,
|
32
|
+
MultiOp,
|
33
|
+
MultiOps,
|
34
|
+
)
|
35
|
+
|
36
|
+
from .names import ( # noqa
|
37
|
+
CanName,
|
38
|
+
Name,
|
39
|
+
NameBuilder,
|
40
|
+
)
|
41
|
+
|
42
|
+
from .relations import ( # noqa
|
43
|
+
CanRelation,
|
44
|
+
CanTable,
|
45
|
+
Relation,
|
46
|
+
RelationBuilder,
|
47
|
+
Table,
|
48
|
+
)
|
49
|
+
|
50
|
+
from .selects import ( # noqa
|
51
|
+
CanRelation,
|
52
|
+
Select,
|
53
|
+
SelectBuilder,
|
54
|
+
SelectItem,
|
55
|
+
)
|
56
|
+
|
57
|
+
from .std import ( # noqa
|
58
|
+
StdBuilder,
|
59
|
+
)
|
60
|
+
|
61
|
+
from .stmts import ( # noqa
|
62
|
+
CanExpr,
|
63
|
+
ExprStmt,
|
64
|
+
Stmt,
|
65
|
+
StmtBuilder,
|
66
|
+
)
|
67
|
+
|
68
|
+
from .unary import ( # noqa
|
69
|
+
Unary,
|
70
|
+
UnaryBuilder,
|
71
|
+
UnaryOp,
|
72
|
+
UnaryOps,
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
##
|
77
|
+
|
78
|
+
|
79
|
+
Q = StdBuilder()
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from ... import dataclasses as dc
|
4
|
+
from ... import lang
|
5
|
+
|
6
|
+
|
7
|
+
##
|
8
|
+
|
9
|
+
|
10
|
+
Value: ta.TypeAlias = ta.Any
|
11
|
+
|
12
|
+
|
13
|
+
##
|
14
|
+
|
15
|
+
|
16
|
+
class Node(dc.Frozen, lang.Abstract, cache_hash=True):
|
17
|
+
pass
|
18
|
+
|
19
|
+
|
20
|
+
class Builder(lang.Abstract):
|
21
|
+
pass
|
@@ -0,0 +1,48 @@
|
|
1
|
+
from ... import check
|
2
|
+
from ... import lang
|
3
|
+
from .base import Node
|
4
|
+
from .exprs import CanExpr
|
5
|
+
from .exprs import Expr
|
6
|
+
from .exprs import ExprBuilder
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
class BinaryOp(Node, lang.Final):
|
13
|
+
name: str
|
14
|
+
|
15
|
+
|
16
|
+
class BinaryOps(lang.Namespace):
|
17
|
+
ADD = BinaryOp('add')
|
18
|
+
SUB = BinaryOp('sub')
|
19
|
+
|
20
|
+
EQ = BinaryOp('eq')
|
21
|
+
NE = BinaryOp('ne')
|
22
|
+
|
23
|
+
|
24
|
+
class Binary(Expr, lang.Final):
|
25
|
+
op: BinaryOp
|
26
|
+
l: Expr
|
27
|
+
r: Expr
|
28
|
+
|
29
|
+
|
30
|
+
class BinaryBuilder(ExprBuilder):
|
31
|
+
def binary(self, op: BinaryOp, *es: CanExpr) -> Expr:
|
32
|
+
check.not_empty(es)
|
33
|
+
l = self.expr(es[0])
|
34
|
+
for r in es[1:]:
|
35
|
+
l = Binary(op, l, self.expr(r))
|
36
|
+
return l
|
37
|
+
|
38
|
+
def add(self, *es: CanExpr) -> Expr:
|
39
|
+
return self.binary(BinaryOps.ADD, *es)
|
40
|
+
|
41
|
+
def sub(self, *es: CanExpr) -> Expr:
|
42
|
+
return self.binary(BinaryOps.SUB, *es)
|
43
|
+
|
44
|
+
def eq(self, *es: CanExpr) -> Expr:
|
45
|
+
return self.binary(BinaryOps.EQ, *es)
|
46
|
+
|
47
|
+
def ne(self, *es: CanExpr) -> Expr:
|
48
|
+
return self.binary(BinaryOps.NE, *es)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from ... import lang
|
4
|
+
from .base import Node
|
5
|
+
from .base import Value
|
6
|
+
from .idents import Ident
|
7
|
+
from .names import CanName
|
8
|
+
from .names import Name
|
9
|
+
from .names import NameBuilder
|
10
|
+
|
11
|
+
|
12
|
+
##
|
13
|
+
|
14
|
+
|
15
|
+
class Expr(Node, lang.Abstract):
|
16
|
+
pass
|
17
|
+
|
18
|
+
|
19
|
+
class Literal(Expr, lang.Final):
|
20
|
+
v: Value
|
21
|
+
|
22
|
+
|
23
|
+
class NameExpr(Expr, lang.Final):
|
24
|
+
n: Name
|
25
|
+
|
26
|
+
|
27
|
+
CanLiteral: ta.TypeAlias = Literal | Value
|
28
|
+
CanExpr: ta.TypeAlias = Expr | CanName | CanLiteral
|
29
|
+
|
30
|
+
|
31
|
+
class ExprBuilder(NameBuilder):
|
32
|
+
def literal(self, o: CanLiteral) -> Literal:
|
33
|
+
if isinstance(o, Literal):
|
34
|
+
return o
|
35
|
+
elif isinstance(o, Node):
|
36
|
+
raise TypeError(o)
|
37
|
+
else:
|
38
|
+
return Literal(o)
|
39
|
+
|
40
|
+
@ta.final
|
41
|
+
def l(self, o: CanLiteral) -> Literal: # noqa
|
42
|
+
return self.literal(o)
|
43
|
+
|
44
|
+
def expr(self, o: CanExpr) -> Expr:
|
45
|
+
if isinstance(o, Expr):
|
46
|
+
return o
|
47
|
+
elif isinstance(o, (Name, Ident)):
|
48
|
+
return NameExpr(self.name(o))
|
49
|
+
else:
|
50
|
+
return self.literal(o)
|
51
|
+
|
52
|
+
@ta.final
|
53
|
+
def e(self, o: CanExpr) -> Expr:
|
54
|
+
return self.expr(o)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import typing as ta
|
2
|
+
|
3
|
+
from ... import lang
|
4
|
+
from .base import Builder
|
5
|
+
from .base import Node
|
6
|
+
|
7
|
+
|
8
|
+
##
|
9
|
+
|
10
|
+
|
11
|
+
class Ident(Node, lang.Final):
|
12
|
+
s: str
|
13
|
+
|
14
|
+
|
15
|
+
CanIdent: ta.TypeAlias = Ident | str
|
16
|
+
|
17
|
+
|
18
|
+
class IdentBuilder(Builder):
|
19
|
+
def ident(self, o: CanIdent) -> Ident:
|
20
|
+
if isinstance(o, Ident):
|
21
|
+
return o
|
22
|
+
elif isinstance(o, str):
|
23
|
+
return Ident(o)
|
24
|
+
else:
|
25
|
+
raise TypeError(o)
|
26
|
+
|
27
|
+
@ta.final
|
28
|
+
def i(self, o: CanIdent) -> Ident:
|
29
|
+
return self.ident(o)
|