ssc_codegen 0.29.6__tar.gz → 0.30.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.
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/PKG-INFO +3 -3
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/pyproject.toml +3 -5
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/__init__.py +3 -3
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/contexts.py +197 -235
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/expressions.py +4 -13
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/format.py +361 -361
- ssc_codegen-0.30.0/ssc_codegen/core/linter.py +1491 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/module_handler.py +9 -49
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/predicates.py +2 -2
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/reader.py +116 -164
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/struct_parser.py +17 -41
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/core/type_checking.py +401 -401
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/__init__.py +8 -5
- ssc_codegen-0.29.6/ssc_codegen/core/adapter.py +0 -25
- ssc_codegen-0.29.6/ssc_codegen/core/linting.py +0 -1179
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/.gitignore +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/LICENSE +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/README.md +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/__init__.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/_logging.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/__init__.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/array.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/base.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/cast.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/control.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/extract.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/helpers.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/jsondef.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/module.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/predicate_containers.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/predicate_ops.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/regex.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/selectors.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/string.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/struct.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/transform.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/typedef.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/ast/types.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/base.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/helpers.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/js_pure.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_bs4.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_lxml.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_parsel.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_render.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/py_slax.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/converters/request_spec.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/document_utils.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/exceptions.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/health.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/dict_reader.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/parser.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/kdl/reader.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/main.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/parsers/__init__.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/parsers/curl.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/parsers/http.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/pseudo_selectors.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/regex_utils.py +0 -0
- {ssc_codegen-0.29.6 → ssc_codegen-0.30.0}/ssc_codegen/selector_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssc_codegen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Python-dsl code converter to html parser for web scraping
|
|
5
5
|
Project-URL: Documentation, https://github.com/vypivshiy/selector_schema_codegen#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/vypivshiy/selector_schema_codegen/issues
|
|
@@ -20,13 +20,13 @@ Classifier: Topic :: Text Processing :: Markup :: HTML
|
|
|
20
20
|
Classifier: Topic :: Utilities
|
|
21
21
|
Requires-Python: >=3.10
|
|
22
22
|
Requires-Dist: bs4>=0.0.2
|
|
23
|
-
Requires-Dist: click<8.2.0
|
|
24
23
|
Requires-Dist: colorama>=0.4.6; sys_platform == 'win32'
|
|
25
24
|
Requires-Dist: cssselect>=1.2.0
|
|
25
|
+
Requires-Dist: kdlquery>=1.1.2
|
|
26
26
|
Requires-Dist: lxml>=5.3.0
|
|
27
27
|
Requires-Dist: pyyaml>=6.0
|
|
28
28
|
Requires-Dist: soupsieve>=2.6
|
|
29
|
-
Requires-Dist: typer
|
|
29
|
+
Requires-Dist: typer>0.26
|
|
30
30
|
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
31
31
|
Description-Content-Type: text/markdown
|
|
32
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ssc_codegen"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.30.0"
|
|
4
4
|
description = "Python-dsl code converter to html parser for web scraping "
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -10,12 +10,10 @@ dependencies = [
|
|
|
10
10
|
"cssselect>=1.2.0",
|
|
11
11
|
"lxml>=5.3.0",
|
|
12
12
|
"soupsieve>=2.6",
|
|
13
|
-
"typer
|
|
13
|
+
"typer>0.26",
|
|
14
14
|
"typing_extensions;python_version<'3.11'",
|
|
15
|
-
# https://github.com/fastapi/typer/discussions/1215
|
|
16
|
-
# https://github.com/fastapi/typer/pull/1145
|
|
17
|
-
"click<8.2.0",
|
|
18
15
|
"pyyaml>=6.0",
|
|
16
|
+
"kdlquery>=1.1.2",
|
|
19
17
|
]
|
|
20
18
|
|
|
21
19
|
classifiers = [
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""ssc_codegen.core — unified KDL -> Module AST reader with integrated linting."""
|
|
2
|
-
from ssc_codegen.core.reader import parse_module
|
|
2
|
+
from ssc_codegen.core.reader import parse_module
|
|
3
3
|
from ssc_codegen.core.format import format_diagnostics, format_diagnostic
|
|
4
|
+
from kdlquery import ReadDiagnostic
|
|
4
5
|
__all__ = [
|
|
5
6
|
"parse_module",
|
|
6
|
-
"SscReader",
|
|
7
7
|
"ReadDiagnostic",
|
|
8
8
|
"format_diagnostics",
|
|
9
9
|
"format_diagnostic"
|
|
10
|
-
]
|
|
10
|
+
]
|
|
@@ -1,235 +1,197 @@
|
|
|
1
|
-
"""Parse/lint contexts, error codes, and supporting data classes."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from dataclasses import dataclass, field
|
|
6
|
-
from enum import Enum, auto
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
from ssc_codegen.ast import VariableType
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
# ── Walk context enum ──────────────────────────────────────────────────────────
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class WalkCtx(Enum):
|
|
18
|
-
MODULE = auto()
|
|
19
|
-
STRUCT_BODY = auto()
|
|
20
|
-
INIT_BLOCK = auto()
|
|
21
|
-
PIPELINE = auto()
|
|
22
|
-
JSON_TYPEDEF = auto()
|
|
23
|
-
SPECIAL_FIELD = auto()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# ── Error codes ────────────────────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class ErrorCode(Enum):
|
|
30
|
-
INVALID_SYNTAX = "E000"
|
|
31
|
-
MISSING_ARGUMENT = "E001"
|
|
32
|
-
INVALID_ARGUMENT = "E002"
|
|
33
|
-
EMPTY_BLOCK = "E003"
|
|
34
|
-
UNEXPECTED_CHILDREN = "E004"
|
|
35
|
-
TYPE_MISMATCH = "E100"
|
|
36
|
-
INCOMPATIBLE_OPERATION = "E101"
|
|
37
|
-
UNKNOWN_OPERATION = "E200"
|
|
38
|
-
UNKNOWN_FIELD = "E201"
|
|
39
|
-
MISSING_REQUIRED_FIELD = "E202"
|
|
40
|
-
INVALID_FIELD_FOR_TYPE = "E203"
|
|
41
|
-
UNDEFINED_REFERENCE = "E300"
|
|
42
|
-
INIT_FIELD_NOT_FOUND = "E301"
|
|
43
|
-
DEFINE_NOT_FOUND = "E302"
|
|
44
|
-
INVALID_STRUCT_TYPE = "E400"
|
|
45
|
-
MISSING_SPECIAL_FIELD = "E401"
|
|
46
|
-
DEPRECATED_SYNTAX = "W001"
|
|
47
|
-
UNUSED_FIELD = "W002"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class DefineKind(Enum):
|
|
51
|
-
SCALAR = auto()
|
|
52
|
-
BLOCK = auto()
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@dataclass
|
|
56
|
-
class RawArg:
|
|
57
|
-
value: str
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
set(self.
|
|
96
|
-
| set(self.
|
|
97
|
-
| set(self.
|
|
98
|
-
| set(self.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def
|
|
185
|
-
return
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
def get_arg(self, node: KdlNode, index: int) -> str | None:
|
|
199
|
-
args = self.get_args(node)
|
|
200
|
-
return args[index] if index < len(args) else None
|
|
201
|
-
|
|
202
|
-
def get_prop(self, node: KdlNode, key: str) -> str | None:
|
|
203
|
-
entry = node.properties.get(key)
|
|
204
|
-
return str(entry.value) if entry else None
|
|
205
|
-
|
|
206
|
-
def get_children_nodes(self, node: KdlNode) -> list[KdlNode]:
|
|
207
|
-
return list(node.children)
|
|
208
|
-
|
|
209
|
-
def has_empty_block(self, node: KdlNode) -> bool:
|
|
210
|
-
return len(node.children) == 0
|
|
211
|
-
|
|
212
|
-
def is_define_ref(self, arg: str) -> bool:
|
|
213
|
-
return arg in self.defines
|
|
214
|
-
|
|
215
|
-
def resolve_scalar_arg(self, arg: str) -> str | None:
|
|
216
|
-
info = self.defines.get(arg)
|
|
217
|
-
if info is not None and info.kind == DefineKind.SCALAR:
|
|
218
|
-
return info.value
|
|
219
|
-
return None
|
|
220
|
-
|
|
221
|
-
@property
|
|
222
|
-
def in_predicate(self) -> bool:
|
|
223
|
-
return self._predicate_depth > 0
|
|
224
|
-
|
|
225
|
-
@property
|
|
226
|
-
def in_assert(self) -> bool:
|
|
227
|
-
return self._predicate_depth > 0 and self._predicate_context == "assert"
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
# lazy imports for forward refs
|
|
231
|
-
from ssc_codegen.ast import ( # noqa: E402
|
|
232
|
-
JsonDef,
|
|
233
|
-
Struct,
|
|
234
|
-
TransformDef,
|
|
235
|
-
)
|
|
1
|
+
"""Parse/lint contexts, error codes, and supporting data classes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum, auto
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ssc_codegen.ast import VariableType
|
|
10
|
+
from kdlquery import KdlNode, ReadDiagnostic, Severity
|
|
11
|
+
from kdlquery.types import Span
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# ── Walk context enum ──────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WalkCtx(Enum):
|
|
18
|
+
MODULE = auto()
|
|
19
|
+
STRUCT_BODY = auto()
|
|
20
|
+
INIT_BLOCK = auto()
|
|
21
|
+
PIPELINE = auto()
|
|
22
|
+
JSON_TYPEDEF = auto()
|
|
23
|
+
SPECIAL_FIELD = auto()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ── Error codes ────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ErrorCode(Enum):
|
|
30
|
+
INVALID_SYNTAX = "E000"
|
|
31
|
+
MISSING_ARGUMENT = "E001"
|
|
32
|
+
INVALID_ARGUMENT = "E002"
|
|
33
|
+
EMPTY_BLOCK = "E003"
|
|
34
|
+
UNEXPECTED_CHILDREN = "E004"
|
|
35
|
+
TYPE_MISMATCH = "E100"
|
|
36
|
+
INCOMPATIBLE_OPERATION = "E101"
|
|
37
|
+
UNKNOWN_OPERATION = "E200"
|
|
38
|
+
UNKNOWN_FIELD = "E201"
|
|
39
|
+
MISSING_REQUIRED_FIELD = "E202"
|
|
40
|
+
INVALID_FIELD_FOR_TYPE = "E203"
|
|
41
|
+
UNDEFINED_REFERENCE = "E300"
|
|
42
|
+
INIT_FIELD_NOT_FOUND = "E301"
|
|
43
|
+
DEFINE_NOT_FOUND = "E302"
|
|
44
|
+
INVALID_STRUCT_TYPE = "E400"
|
|
45
|
+
MISSING_SPECIAL_FIELD = "E401"
|
|
46
|
+
DEPRECATED_SYNTAX = "W001"
|
|
47
|
+
UNUSED_FIELD = "W002"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DefineKind(Enum):
|
|
51
|
+
SCALAR = auto()
|
|
52
|
+
BLOCK = auto()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class RawArg:
|
|
57
|
+
value: str
|
|
58
|
+
span: Span
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass
|
|
62
|
+
class DefineInfo:
|
|
63
|
+
name: str
|
|
64
|
+
kind: DefineKind
|
|
65
|
+
value: str | None
|
|
66
|
+
node: KdlNode
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class TransformInfo:
|
|
71
|
+
name: str
|
|
72
|
+
accept: str
|
|
73
|
+
ret: str
|
|
74
|
+
langs: list[str]
|
|
75
|
+
node: KdlNode
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ── Parse context ───────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@dataclass
|
|
82
|
+
class ParseContext:
|
|
83
|
+
property_defines: dict[str, str | int | float | bool] = field(
|
|
84
|
+
default_factory=dict
|
|
85
|
+
)
|
|
86
|
+
children_defines: dict[str, list[KdlNode]] = field(default_factory=dict)
|
|
87
|
+
transforms: dict[str, TransformDef] = field(default_factory=dict)
|
|
88
|
+
structs: dict[str, Struct] = field(default_factory=dict)
|
|
89
|
+
json_defs: dict[str, JsonDef] = field(default_factory=dict)
|
|
90
|
+
source_path: Path | None = None
|
|
91
|
+
|
|
92
|
+
def all_names(self) -> set[str]:
|
|
93
|
+
return (
|
|
94
|
+
set(self.property_defines)
|
|
95
|
+
| set(self.children_defines)
|
|
96
|
+
| set(self.transforms)
|
|
97
|
+
| set(self.structs)
|
|
98
|
+
| set(self.json_defs)
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# ── Lint context ───────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class LintContext:
|
|
107
|
+
"""Lint state for reader-side validation (type inference, per-op checks)."""
|
|
108
|
+
|
|
109
|
+
defines: dict[str, DefineInfo] = field(default_factory=dict)
|
|
110
|
+
transforms: dict[str, TransformInfo] = field(default_factory=dict)
|
|
111
|
+
init_fields: set[str] = field(default_factory=set)
|
|
112
|
+
walk_context: WalkCtx = WalkCtx.MODULE
|
|
113
|
+
_path_segments: list[str] = field(default_factory=list)
|
|
114
|
+
diagnostics: list[ReadDiagnostic] = field(default_factory=list)
|
|
115
|
+
inferred_define_types: dict[str, tuple[VariableType, VariableType]] = field(
|
|
116
|
+
default_factory=dict
|
|
117
|
+
)
|
|
118
|
+
_predicate_depth: int = field(default=0)
|
|
119
|
+
_predicate_context: str = ""
|
|
120
|
+
|
|
121
|
+
def error(
|
|
122
|
+
self,
|
|
123
|
+
node: KdlNode,
|
|
124
|
+
*,
|
|
125
|
+
message: str,
|
|
126
|
+
code: str = "",
|
|
127
|
+
hint: str = "",
|
|
128
|
+
label: str | None = None,
|
|
129
|
+
notes: list[str] | None = None,
|
|
130
|
+
) -> None:
|
|
131
|
+
self.diagnostics.append(
|
|
132
|
+
ReadDiagnostic(
|
|
133
|
+
message=message,
|
|
134
|
+
severity=Severity.ERROR,
|
|
135
|
+
span=node.span,
|
|
136
|
+
path=self.path,
|
|
137
|
+
hint=hint,
|
|
138
|
+
code=code,
|
|
139
|
+
label=label,
|
|
140
|
+
notes=tuple(notes) if notes else (),
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def warning(
|
|
145
|
+
self,
|
|
146
|
+
node: KdlNode,
|
|
147
|
+
*,
|
|
148
|
+
message: str,
|
|
149
|
+
code: str = "",
|
|
150
|
+
hint: str = "",
|
|
151
|
+
label: str | None = None,
|
|
152
|
+
notes: list[str] | None = None,
|
|
153
|
+
) -> None:
|
|
154
|
+
self.diagnostics.append(
|
|
155
|
+
ReadDiagnostic(
|
|
156
|
+
message=message,
|
|
157
|
+
severity=Severity.WARNING,
|
|
158
|
+
span=node.span,
|
|
159
|
+
path=self.path,
|
|
160
|
+
hint=hint,
|
|
161
|
+
code=code,
|
|
162
|
+
label=label,
|
|
163
|
+
notes=tuple(notes) if notes else (),
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def path(self) -> str:
|
|
169
|
+
return "/".join(self._path_segments)
|
|
170
|
+
|
|
171
|
+
def push(self, segment: str) -> None:
|
|
172
|
+
self._path_segments.append(segment)
|
|
173
|
+
|
|
174
|
+
def pop(self) -> None:
|
|
175
|
+
self._path_segments.pop()
|
|
176
|
+
|
|
177
|
+
def resolve_scalar_arg(self, arg: str) -> str | None:
|
|
178
|
+
info = self.defines.get(arg)
|
|
179
|
+
if info is not None and info.kind == DefineKind.SCALAR:
|
|
180
|
+
return info.value
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def in_predicate(self) -> bool:
|
|
185
|
+
return self._predicate_depth > 0
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def in_assert(self) -> bool:
|
|
189
|
+
return self._predicate_depth > 0 and self._predicate_context == "assert"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# lazy imports for forward refs
|
|
193
|
+
from ssc_codegen.ast import ( # noqa: E402
|
|
194
|
+
JsonDef,
|
|
195
|
+
Struct,
|
|
196
|
+
TransformDef,
|
|
197
|
+
)
|
|
@@ -76,12 +76,12 @@ from ssc_codegen.ast import (
|
|
|
76
76
|
XpathSelectAll,
|
|
77
77
|
)
|
|
78
78
|
from ssc_codegen.exceptions import BuildTimeError
|
|
79
|
-
from
|
|
79
|
+
from kdlquery import KdlNode
|
|
80
80
|
from ssc_codegen.regex_utils import normalize_regex_pattern
|
|
81
81
|
from typing import cast
|
|
82
82
|
|
|
83
83
|
from ssc_codegen.core.contexts import LintContext, ParseContext, WalkCtx
|
|
84
|
-
from ssc_codegen.core.
|
|
84
|
+
from ssc_codegen.core.linter import (
|
|
85
85
|
lint_pipeline_op,
|
|
86
86
|
lint_validate_css,
|
|
87
87
|
lint_validate_xpath,
|
|
@@ -866,12 +866,7 @@ def _expr_len(
|
|
|
866
866
|
def _expr_unique(
|
|
867
867
|
node: KdlNode, parent: FieldLikeNode, ctx: ParseContext, lint: LintContext
|
|
868
868
|
):
|
|
869
|
-
keep_order = bool(
|
|
870
|
-
node.properties.get(
|
|
871
|
-
"keep-order",
|
|
872
|
-
KdlArg(value=False, span=node.span, is_identifier=False),
|
|
873
|
-
).value
|
|
874
|
-
)
|
|
869
|
+
keep_order = bool(node.get_prop("keep-order") or False)
|
|
875
870
|
return Unique(parent=parent, keep_order=keep_order)
|
|
876
871
|
|
|
877
872
|
|
|
@@ -917,11 +912,7 @@ def _expr_jsonify(
|
|
|
917
912
|
node: KdlNode, parent: FieldLikeNode, ctx: ParseContext, lint: LintContext
|
|
918
913
|
):
|
|
919
914
|
schema_name = str(node.args[0].value)
|
|
920
|
-
path =
|
|
921
|
-
node.properties.get(
|
|
922
|
-
"path", KdlArg(value="", span=node.span, is_identifier=False)
|
|
923
|
-
).value
|
|
924
|
-
)
|
|
915
|
+
path = node.get_prop("path") or ""
|
|
925
916
|
json_def = ctx.json_defs.get(schema_name)
|
|
926
917
|
if json_def is None:
|
|
927
918
|
lint.error(
|