yuho 5.0.0__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.
- yuho/__init__.py +16 -0
- yuho/ast/__init__.py +196 -0
- yuho/ast/builder.py +926 -0
- yuho/ast/constant_folder.py +280 -0
- yuho/ast/dead_code.py +199 -0
- yuho/ast/exhaustiveness.py +503 -0
- yuho/ast/nodes.py +907 -0
- yuho/ast/overlap.py +291 -0
- yuho/ast/reachability.py +293 -0
- yuho/ast/scope_analysis.py +490 -0
- yuho/ast/transformer.py +490 -0
- yuho/ast/type_check.py +471 -0
- yuho/ast/type_inference.py +425 -0
- yuho/ast/visitor.py +239 -0
- yuho/cli/__init__.py +14 -0
- yuho/cli/commands/__init__.py +1 -0
- yuho/cli/commands/api.py +431 -0
- yuho/cli/commands/ast_viz.py +334 -0
- yuho/cli/commands/check.py +218 -0
- yuho/cli/commands/config.py +311 -0
- yuho/cli/commands/contribute.py +122 -0
- yuho/cli/commands/diff.py +487 -0
- yuho/cli/commands/explain.py +240 -0
- yuho/cli/commands/fmt.py +253 -0
- yuho/cli/commands/generate.py +316 -0
- yuho/cli/commands/graph.py +410 -0
- yuho/cli/commands/init.py +120 -0
- yuho/cli/commands/library.py +656 -0
- yuho/cli/commands/lint.py +503 -0
- yuho/cli/commands/lsp.py +36 -0
- yuho/cli/commands/preview.py +377 -0
- yuho/cli/commands/repl.py +444 -0
- yuho/cli/commands/serve.py +44 -0
- yuho/cli/commands/test.py +528 -0
- yuho/cli/commands/transpile.py +121 -0
- yuho/cli/commands/wizard.py +370 -0
- yuho/cli/completions.py +182 -0
- yuho/cli/error_formatter.py +193 -0
- yuho/cli/main.py +1064 -0
- yuho/config/__init__.py +46 -0
- yuho/config/loader.py +235 -0
- yuho/config/mask.py +194 -0
- yuho/config/schema.py +147 -0
- yuho/library/__init__.py +84 -0
- yuho/library/index.py +328 -0
- yuho/library/install.py +699 -0
- yuho/library/lockfile.py +330 -0
- yuho/library/package.py +421 -0
- yuho/library/resolver.py +791 -0
- yuho/library/signature.py +335 -0
- yuho/llm/__init__.py +45 -0
- yuho/llm/config.py +75 -0
- yuho/llm/factory.py +123 -0
- yuho/llm/prompts.py +146 -0
- yuho/llm/providers.py +383 -0
- yuho/llm/utils.py +470 -0
- yuho/lsp/__init__.py +14 -0
- yuho/lsp/code_action_handler.py +518 -0
- yuho/lsp/completion_handler.py +85 -0
- yuho/lsp/diagnostics.py +100 -0
- yuho/lsp/hover_handler.py +130 -0
- yuho/lsp/server.py +1425 -0
- yuho/mcp/__init__.py +10 -0
- yuho/mcp/server.py +1452 -0
- yuho/parser/__init__.py +8 -0
- yuho/parser/source_location.py +108 -0
- yuho/parser/wrapper.py +311 -0
- yuho/testing/__init__.py +48 -0
- yuho/testing/coverage.py +274 -0
- yuho/testing/fixtures.py +263 -0
- yuho/transpile/__init__.py +52 -0
- yuho/transpile/alloy_transpiler.py +546 -0
- yuho/transpile/base.py +100 -0
- yuho/transpile/blocks_transpiler.py +338 -0
- yuho/transpile/english_transpiler.py +470 -0
- yuho/transpile/graphql_transpiler.py +404 -0
- yuho/transpile/json_transpiler.py +217 -0
- yuho/transpile/jsonld_transpiler.py +250 -0
- yuho/transpile/latex_preamble.py +161 -0
- yuho/transpile/latex_transpiler.py +406 -0
- yuho/transpile/latex_utils.py +206 -0
- yuho/transpile/mermaid_transpiler.py +357 -0
- yuho/transpile/registry.py +275 -0
- yuho/verify/__init__.py +43 -0
- yuho/verify/alloy.py +352 -0
- yuho/verify/combined.py +218 -0
- yuho/verify/z3_solver.py +1155 -0
- yuho-5.0.0.dist-info/METADATA +186 -0
- yuho-5.0.0.dist-info/RECORD +91 -0
- yuho-5.0.0.dist-info/WHEEL +4 -0
- yuho-5.0.0.dist-info/entry_points.txt +2 -0
yuho/ast/transformer.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Transformer base class for immutable AST transformation.
|
|
3
|
+
|
|
4
|
+
Unlike Visitor which traverses without returning new nodes,
|
|
5
|
+
Transformer returns new AST nodes allowing immutable transformations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Optional, Tuple, List
|
|
9
|
+
|
|
10
|
+
from yuho.ast import nodes
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Transformer:
|
|
14
|
+
"""
|
|
15
|
+
Base transformer class for immutable AST transformation.
|
|
16
|
+
|
|
17
|
+
Each transform_* method returns a new node (or the same node if unchanged).
|
|
18
|
+
The default implementation recursively transforms children and reconstructs
|
|
19
|
+
the node only if children changed.
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
class ConstantFolder(Transformer):
|
|
23
|
+
def transform_binary_expr(self, node):
|
|
24
|
+
node = super().transform_binary_expr(node)
|
|
25
|
+
if isinstance(node.left, nodes.IntLit) and isinstance(node.right, nodes.IntLit):
|
|
26
|
+
if node.operator == '+':
|
|
27
|
+
return nodes.IntLit(
|
|
28
|
+
value=node.left.value + node.right.value,
|
|
29
|
+
source_location=node.source_location
|
|
30
|
+
)
|
|
31
|
+
return node
|
|
32
|
+
|
|
33
|
+
folder = ConstantFolder()
|
|
34
|
+
new_ast = folder.transform(ast)
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def transform(self, node: nodes.ASTNode) -> nodes.ASTNode:
|
|
38
|
+
"""
|
|
39
|
+
Dispatch to the appropriate transform_* method.
|
|
40
|
+
|
|
41
|
+
This is the main entry point for transforming a node.
|
|
42
|
+
"""
|
|
43
|
+
return node.accept(self)
|
|
44
|
+
|
|
45
|
+
def _transform_children(self, children: List[nodes.ASTNode]) -> Tuple[Tuple[nodes.ASTNode, ...], bool]:
|
|
46
|
+
"""
|
|
47
|
+
Transform a list of children and return whether any changed.
|
|
48
|
+
|
|
49
|
+
Returns (transformed_tuple, changed).
|
|
50
|
+
"""
|
|
51
|
+
transformed = []
|
|
52
|
+
changed = False
|
|
53
|
+
for child in children:
|
|
54
|
+
new_child = self.transform(child)
|
|
55
|
+
if new_child is not child:
|
|
56
|
+
changed = True
|
|
57
|
+
transformed.append(new_child)
|
|
58
|
+
return tuple(transformed), changed
|
|
59
|
+
|
|
60
|
+
# =========================================================================
|
|
61
|
+
# Type nodes
|
|
62
|
+
# =========================================================================
|
|
63
|
+
|
|
64
|
+
def transform_type(self, node: nodes.TypeNode) -> nodes.TypeNode:
|
|
65
|
+
return node
|
|
66
|
+
|
|
67
|
+
def transform_builtin_type(self, node: nodes.BuiltinType) -> nodes.BuiltinType:
|
|
68
|
+
return node
|
|
69
|
+
|
|
70
|
+
def transform_named_type(self, node: nodes.NamedType) -> nodes.NamedType:
|
|
71
|
+
return node
|
|
72
|
+
|
|
73
|
+
def transform_generic_type(self, node: nodes.GenericType) -> nodes.GenericType:
|
|
74
|
+
new_args, changed = self._transform_children(list(node.type_args))
|
|
75
|
+
if changed:
|
|
76
|
+
return nodes.GenericType(
|
|
77
|
+
base=node.base,
|
|
78
|
+
type_args=new_args,
|
|
79
|
+
source_location=node.source_location,
|
|
80
|
+
)
|
|
81
|
+
return node
|
|
82
|
+
|
|
83
|
+
def transform_optional_type(self, node: nodes.OptionalType) -> nodes.OptionalType:
|
|
84
|
+
new_inner = self.transform(node.inner)
|
|
85
|
+
if new_inner is not node.inner:
|
|
86
|
+
return nodes.OptionalType(
|
|
87
|
+
inner=new_inner,
|
|
88
|
+
source_location=node.source_location,
|
|
89
|
+
)
|
|
90
|
+
return node
|
|
91
|
+
|
|
92
|
+
def transform_array_type(self, node: nodes.ArrayType) -> nodes.ArrayType:
|
|
93
|
+
new_elem = self.transform(node.element_type)
|
|
94
|
+
if new_elem is not node.element_type:
|
|
95
|
+
return nodes.ArrayType(
|
|
96
|
+
element_type=new_elem,
|
|
97
|
+
source_location=node.source_location,
|
|
98
|
+
)
|
|
99
|
+
return node
|
|
100
|
+
|
|
101
|
+
# =========================================================================
|
|
102
|
+
# Literal nodes (typically unchanged)
|
|
103
|
+
# =========================================================================
|
|
104
|
+
|
|
105
|
+
def transform_int_lit(self, node: nodes.IntLit) -> nodes.IntLit:
|
|
106
|
+
return node
|
|
107
|
+
|
|
108
|
+
def transform_float_lit(self, node: nodes.FloatLit) -> nodes.FloatLit:
|
|
109
|
+
return node
|
|
110
|
+
|
|
111
|
+
def transform_bool_lit(self, node: nodes.BoolLit) -> nodes.BoolLit:
|
|
112
|
+
return node
|
|
113
|
+
|
|
114
|
+
def transform_string_lit(self, node: nodes.StringLit) -> nodes.StringLit:
|
|
115
|
+
return node
|
|
116
|
+
|
|
117
|
+
def transform_money(self, node: nodes.MoneyNode) -> nodes.MoneyNode:
|
|
118
|
+
return node
|
|
119
|
+
|
|
120
|
+
def transform_percent(self, node: nodes.PercentNode) -> nodes.PercentNode:
|
|
121
|
+
return node
|
|
122
|
+
|
|
123
|
+
def transform_date(self, node: nodes.DateNode) -> nodes.DateNode:
|
|
124
|
+
return node
|
|
125
|
+
|
|
126
|
+
def transform_duration(self, node: nodes.DurationNode) -> nodes.DurationNode:
|
|
127
|
+
return node
|
|
128
|
+
|
|
129
|
+
# =========================================================================
|
|
130
|
+
# Expression nodes
|
|
131
|
+
# =========================================================================
|
|
132
|
+
|
|
133
|
+
def transform_identifier(self, node: nodes.IdentifierNode) -> nodes.IdentifierNode:
|
|
134
|
+
return node
|
|
135
|
+
|
|
136
|
+
def transform_field_access(self, node: nodes.FieldAccessNode) -> nodes.ASTNode:
|
|
137
|
+
new_base = self.transform(node.base)
|
|
138
|
+
if new_base is not node.base:
|
|
139
|
+
return nodes.FieldAccessNode(
|
|
140
|
+
base=new_base,
|
|
141
|
+
field_name=node.field_name,
|
|
142
|
+
source_location=node.source_location,
|
|
143
|
+
)
|
|
144
|
+
return node
|
|
145
|
+
|
|
146
|
+
def transform_index_access(self, node: nodes.IndexAccessNode) -> nodes.ASTNode:
|
|
147
|
+
new_base = self.transform(node.base)
|
|
148
|
+
new_index = self.transform(node.index)
|
|
149
|
+
if new_base is not node.base or new_index is not node.index:
|
|
150
|
+
return nodes.IndexAccessNode(
|
|
151
|
+
base=new_base,
|
|
152
|
+
index=new_index,
|
|
153
|
+
source_location=node.source_location,
|
|
154
|
+
)
|
|
155
|
+
return node
|
|
156
|
+
|
|
157
|
+
def transform_function_call(self, node: nodes.FunctionCallNode) -> nodes.ASTNode:
|
|
158
|
+
new_callee = self.transform(node.callee)
|
|
159
|
+
new_args, args_changed = self._transform_children(list(node.args))
|
|
160
|
+
if new_callee is not node.callee or args_changed:
|
|
161
|
+
return nodes.FunctionCallNode(
|
|
162
|
+
callee=new_callee,
|
|
163
|
+
args=new_args,
|
|
164
|
+
source_location=node.source_location,
|
|
165
|
+
)
|
|
166
|
+
return node
|
|
167
|
+
|
|
168
|
+
def transform_binary_expr(self, node: nodes.BinaryExprNode) -> nodes.ASTNode:
|
|
169
|
+
new_left = self.transform(node.left)
|
|
170
|
+
new_right = self.transform(node.right)
|
|
171
|
+
if new_left is not node.left or new_right is not node.right:
|
|
172
|
+
return nodes.BinaryExprNode(
|
|
173
|
+
left=new_left,
|
|
174
|
+
operator=node.operator,
|
|
175
|
+
right=new_right,
|
|
176
|
+
source_location=node.source_location,
|
|
177
|
+
)
|
|
178
|
+
return node
|
|
179
|
+
|
|
180
|
+
def transform_unary_expr(self, node: nodes.UnaryExprNode) -> nodes.ASTNode:
|
|
181
|
+
new_operand = self.transform(node.operand)
|
|
182
|
+
if new_operand is not node.operand:
|
|
183
|
+
return nodes.UnaryExprNode(
|
|
184
|
+
operator=node.operator,
|
|
185
|
+
operand=new_operand,
|
|
186
|
+
source_location=node.source_location,
|
|
187
|
+
)
|
|
188
|
+
return node
|
|
189
|
+
|
|
190
|
+
def transform_pass_expr(self, node: nodes.PassExprNode) -> nodes.PassExprNode:
|
|
191
|
+
return node
|
|
192
|
+
|
|
193
|
+
# =========================================================================
|
|
194
|
+
# Pattern nodes
|
|
195
|
+
# =========================================================================
|
|
196
|
+
|
|
197
|
+
def transform_pattern(self, node: nodes.PatternNode) -> nodes.PatternNode:
|
|
198
|
+
return node
|
|
199
|
+
|
|
200
|
+
def transform_wildcard_pattern(self, node: nodes.WildcardPattern) -> nodes.WildcardPattern:
|
|
201
|
+
return node
|
|
202
|
+
|
|
203
|
+
def transform_literal_pattern(self, node: nodes.LiteralPattern) -> nodes.LiteralPattern:
|
|
204
|
+
new_lit = self.transform(node.literal)
|
|
205
|
+
if new_lit is not node.literal:
|
|
206
|
+
return nodes.LiteralPattern(
|
|
207
|
+
literal=new_lit,
|
|
208
|
+
source_location=node.source_location,
|
|
209
|
+
)
|
|
210
|
+
return node
|
|
211
|
+
|
|
212
|
+
def transform_binding_pattern(self, node: nodes.BindingPattern) -> nodes.BindingPattern:
|
|
213
|
+
return node
|
|
214
|
+
|
|
215
|
+
def transform_field_pattern(self, node: nodes.FieldPattern) -> nodes.FieldPattern:
|
|
216
|
+
if node.pattern:
|
|
217
|
+
new_pattern = self.transform(node.pattern)
|
|
218
|
+
if new_pattern is not node.pattern:
|
|
219
|
+
return nodes.FieldPattern(
|
|
220
|
+
name=node.name,
|
|
221
|
+
pattern=new_pattern,
|
|
222
|
+
source_location=node.source_location,
|
|
223
|
+
)
|
|
224
|
+
return node
|
|
225
|
+
|
|
226
|
+
def transform_struct_pattern(self, node: nodes.StructPattern) -> nodes.StructPattern:
|
|
227
|
+
new_fields, changed = self._transform_children(list(node.fields))
|
|
228
|
+
if changed:
|
|
229
|
+
return nodes.StructPattern(
|
|
230
|
+
type_name=node.type_name,
|
|
231
|
+
fields=new_fields,
|
|
232
|
+
source_location=node.source_location,
|
|
233
|
+
)
|
|
234
|
+
return node
|
|
235
|
+
|
|
236
|
+
# =========================================================================
|
|
237
|
+
# Match expression
|
|
238
|
+
# =========================================================================
|
|
239
|
+
|
|
240
|
+
def transform_match_arm(self, node: nodes.MatchArm) -> nodes.MatchArm:
|
|
241
|
+
new_pattern = self.transform(node.pattern)
|
|
242
|
+
new_guard = self.transform(node.guard) if node.guard else None
|
|
243
|
+
new_body = self.transform(node.body)
|
|
244
|
+
|
|
245
|
+
if (new_pattern is not node.pattern or
|
|
246
|
+
new_guard is not node.guard or
|
|
247
|
+
new_body is not node.body):
|
|
248
|
+
return nodes.MatchArm(
|
|
249
|
+
pattern=new_pattern,
|
|
250
|
+
guard=new_guard,
|
|
251
|
+
body=new_body,
|
|
252
|
+
source_location=node.source_location,
|
|
253
|
+
)
|
|
254
|
+
return node
|
|
255
|
+
|
|
256
|
+
def transform_match_expr(self, node: nodes.MatchExprNode) -> nodes.ASTNode:
|
|
257
|
+
new_scrutinee = self.transform(node.scrutinee) if node.scrutinee else None
|
|
258
|
+
new_arms, changed = self._transform_children(list(node.arms))
|
|
259
|
+
|
|
260
|
+
if new_scrutinee is not node.scrutinee or changed:
|
|
261
|
+
return nodes.MatchExprNode(
|
|
262
|
+
scrutinee=new_scrutinee,
|
|
263
|
+
arms=new_arms,
|
|
264
|
+
ensure_exhaustiveness=node.ensure_exhaustiveness,
|
|
265
|
+
source_location=node.source_location,
|
|
266
|
+
)
|
|
267
|
+
return node
|
|
268
|
+
|
|
269
|
+
# =========================================================================
|
|
270
|
+
# Struct definition and literal
|
|
271
|
+
# =========================================================================
|
|
272
|
+
|
|
273
|
+
def transform_field_def(self, node: nodes.FieldDef) -> nodes.FieldDef:
|
|
274
|
+
new_type = self.transform(node.type_annotation)
|
|
275
|
+
if new_type is not node.type_annotation:
|
|
276
|
+
return nodes.FieldDef(
|
|
277
|
+
type_annotation=new_type,
|
|
278
|
+
name=node.name,
|
|
279
|
+
source_location=node.source_location,
|
|
280
|
+
)
|
|
281
|
+
return node
|
|
282
|
+
|
|
283
|
+
def transform_struct_def(self, node: nodes.StructDefNode) -> nodes.StructDefNode:
|
|
284
|
+
new_fields, changed = self._transform_children(list(node.fields))
|
|
285
|
+
if changed:
|
|
286
|
+
return nodes.StructDefNode(
|
|
287
|
+
name=node.name,
|
|
288
|
+
fields=new_fields,
|
|
289
|
+
type_params=node.type_params,
|
|
290
|
+
source_location=node.source_location,
|
|
291
|
+
)
|
|
292
|
+
return node
|
|
293
|
+
|
|
294
|
+
def transform_field_assignment(self, node: nodes.FieldAssignment) -> nodes.FieldAssignment:
|
|
295
|
+
new_value = self.transform(node.value)
|
|
296
|
+
if new_value is not node.value:
|
|
297
|
+
return nodes.FieldAssignment(
|
|
298
|
+
name=node.name,
|
|
299
|
+
value=new_value,
|
|
300
|
+
source_location=node.source_location,
|
|
301
|
+
)
|
|
302
|
+
return node
|
|
303
|
+
|
|
304
|
+
def transform_struct_literal(self, node: nodes.StructLiteralNode) -> nodes.ASTNode:
|
|
305
|
+
new_fields, changed = self._transform_children(list(node.field_values))
|
|
306
|
+
if changed:
|
|
307
|
+
return nodes.StructLiteralNode(
|
|
308
|
+
struct_name=node.struct_name,
|
|
309
|
+
field_values=new_fields,
|
|
310
|
+
source_location=node.source_location,
|
|
311
|
+
)
|
|
312
|
+
return node
|
|
313
|
+
|
|
314
|
+
# =========================================================================
|
|
315
|
+
# Function definition
|
|
316
|
+
# =========================================================================
|
|
317
|
+
|
|
318
|
+
def transform_param_def(self, node: nodes.ParamDef) -> nodes.ParamDef:
|
|
319
|
+
new_type = self.transform(node.type_annotation)
|
|
320
|
+
if new_type is not node.type_annotation:
|
|
321
|
+
return nodes.ParamDef(
|
|
322
|
+
type_annotation=new_type,
|
|
323
|
+
name=node.name,
|
|
324
|
+
source_location=node.source_location,
|
|
325
|
+
)
|
|
326
|
+
return node
|
|
327
|
+
|
|
328
|
+
def transform_block(self, node: nodes.Block) -> nodes.Block:
|
|
329
|
+
new_stmts, changed = self._transform_children(list(node.statements))
|
|
330
|
+
if changed:
|
|
331
|
+
return nodes.Block(
|
|
332
|
+
statements=new_stmts,
|
|
333
|
+
source_location=node.source_location,
|
|
334
|
+
)
|
|
335
|
+
return node
|
|
336
|
+
|
|
337
|
+
def transform_function_def(self, node: nodes.FunctionDefNode) -> nodes.FunctionDefNode:
|
|
338
|
+
new_params, params_changed = self._transform_children(list(node.params))
|
|
339
|
+
new_return_type = self.transform(node.return_type) if node.return_type else None
|
|
340
|
+
new_body = self.transform(node.body)
|
|
341
|
+
|
|
342
|
+
if (params_changed or
|
|
343
|
+
new_return_type is not node.return_type or
|
|
344
|
+
new_body is not node.body):
|
|
345
|
+
return nodes.FunctionDefNode(
|
|
346
|
+
name=node.name,
|
|
347
|
+
params=new_params,
|
|
348
|
+
return_type=new_return_type,
|
|
349
|
+
body=new_body,
|
|
350
|
+
source_location=node.source_location,
|
|
351
|
+
)
|
|
352
|
+
return node
|
|
353
|
+
|
|
354
|
+
# =========================================================================
|
|
355
|
+
# Statements
|
|
356
|
+
# =========================================================================
|
|
357
|
+
|
|
358
|
+
def transform_variable_decl(self, node: nodes.VariableDecl) -> nodes.VariableDecl:
|
|
359
|
+
new_type = self.transform(node.type_annotation)
|
|
360
|
+
new_value = self.transform(node.value) if node.value else None
|
|
361
|
+
|
|
362
|
+
if new_type is not node.type_annotation or new_value is not node.value:
|
|
363
|
+
return nodes.VariableDecl(
|
|
364
|
+
type_annotation=new_type,
|
|
365
|
+
name=node.name,
|
|
366
|
+
value=new_value,
|
|
367
|
+
source_location=node.source_location,
|
|
368
|
+
)
|
|
369
|
+
return node
|
|
370
|
+
|
|
371
|
+
def transform_assignment_stmt(self, node: nodes.AssignmentStmt) -> nodes.AssignmentStmt:
|
|
372
|
+
new_target = self.transform(node.target)
|
|
373
|
+
new_value = self.transform(node.value)
|
|
374
|
+
|
|
375
|
+
if new_target is not node.target or new_value is not node.value:
|
|
376
|
+
return nodes.AssignmentStmt(
|
|
377
|
+
target=new_target,
|
|
378
|
+
value=new_value,
|
|
379
|
+
source_location=node.source_location,
|
|
380
|
+
)
|
|
381
|
+
return node
|
|
382
|
+
|
|
383
|
+
def transform_return_stmt(self, node: nodes.ReturnStmt) -> nodes.ReturnStmt:
|
|
384
|
+
new_value = self.transform(node.value) if node.value else None
|
|
385
|
+
if new_value is not node.value:
|
|
386
|
+
return nodes.ReturnStmt(
|
|
387
|
+
value=new_value,
|
|
388
|
+
source_location=node.source_location,
|
|
389
|
+
)
|
|
390
|
+
return node
|
|
391
|
+
|
|
392
|
+
def transform_pass_stmt(self, node: nodes.PassStmt) -> nodes.PassStmt:
|
|
393
|
+
return node
|
|
394
|
+
|
|
395
|
+
def transform_expression_stmt(self, node: nodes.ExpressionStmt) -> nodes.ExpressionStmt:
|
|
396
|
+
new_expr = self.transform(node.expression)
|
|
397
|
+
if new_expr is not node.expression:
|
|
398
|
+
return nodes.ExpressionStmt(
|
|
399
|
+
expression=new_expr,
|
|
400
|
+
source_location=node.source_location,
|
|
401
|
+
)
|
|
402
|
+
return node
|
|
403
|
+
|
|
404
|
+
# =========================================================================
|
|
405
|
+
# Statute-specific nodes
|
|
406
|
+
# =========================================================================
|
|
407
|
+
|
|
408
|
+
def transform_definition_entry(self, node: nodes.DefinitionEntry) -> nodes.DefinitionEntry:
|
|
409
|
+
new_def = self.transform(node.definition)
|
|
410
|
+
if new_def is not node.definition:
|
|
411
|
+
return nodes.DefinitionEntry(
|
|
412
|
+
term=node.term,
|
|
413
|
+
definition=new_def,
|
|
414
|
+
source_location=node.source_location,
|
|
415
|
+
)
|
|
416
|
+
return node
|
|
417
|
+
|
|
418
|
+
def transform_element(self, node: nodes.ElementNode) -> nodes.ElementNode:
|
|
419
|
+
new_desc = self.transform(node.description)
|
|
420
|
+
if new_desc is not node.description:
|
|
421
|
+
return nodes.ElementNode(
|
|
422
|
+
element_type=node.element_type,
|
|
423
|
+
name=node.name,
|
|
424
|
+
description=new_desc,
|
|
425
|
+
source_location=node.source_location,
|
|
426
|
+
)
|
|
427
|
+
return node
|
|
428
|
+
|
|
429
|
+
def transform_penalty(self, node: nodes.PenaltyNode) -> nodes.PenaltyNode:
|
|
430
|
+
# Penalties contain immutable duration/money nodes, typically unchanged
|
|
431
|
+
return node
|
|
432
|
+
|
|
433
|
+
def transform_illustration(self, node: nodes.IllustrationNode) -> nodes.IllustrationNode:
|
|
434
|
+
new_desc = self.transform(node.description)
|
|
435
|
+
if new_desc is not node.description:
|
|
436
|
+
return nodes.IllustrationNode(
|
|
437
|
+
label=node.label,
|
|
438
|
+
description=new_desc,
|
|
439
|
+
source_location=node.source_location,
|
|
440
|
+
)
|
|
441
|
+
return node
|
|
442
|
+
|
|
443
|
+
def transform_statute(self, node: nodes.StatuteNode) -> nodes.StatuteNode:
|
|
444
|
+
new_title = self.transform(node.title) if node.title else None
|
|
445
|
+
new_defs, defs_changed = self._transform_children(list(node.definitions))
|
|
446
|
+
new_elems, elems_changed = self._transform_children(list(node.elements))
|
|
447
|
+
new_penalty = self.transform(node.penalty) if node.penalty else None
|
|
448
|
+
new_illus, illus_changed = self._transform_children(list(node.illustrations))
|
|
449
|
+
|
|
450
|
+
if (new_title is not node.title or
|
|
451
|
+
defs_changed or
|
|
452
|
+
elems_changed or
|
|
453
|
+
new_penalty is not node.penalty or
|
|
454
|
+
illus_changed):
|
|
455
|
+
return nodes.StatuteNode(
|
|
456
|
+
section_number=node.section_number,
|
|
457
|
+
title=new_title,
|
|
458
|
+
definitions=new_defs,
|
|
459
|
+
elements=new_elems,
|
|
460
|
+
penalty=new_penalty,
|
|
461
|
+
illustrations=new_illus,
|
|
462
|
+
source_location=node.source_location,
|
|
463
|
+
)
|
|
464
|
+
return node
|
|
465
|
+
|
|
466
|
+
# =========================================================================
|
|
467
|
+
# Import and module
|
|
468
|
+
# =========================================================================
|
|
469
|
+
|
|
470
|
+
def transform_import(self, node: nodes.ImportNode) -> nodes.ImportNode:
|
|
471
|
+
return node
|
|
472
|
+
|
|
473
|
+
def transform_module(self, node: nodes.ModuleNode) -> nodes.ModuleNode:
|
|
474
|
+
new_imports, imports_changed = self._transform_children(list(node.imports))
|
|
475
|
+
new_types, types_changed = self._transform_children(list(node.type_defs))
|
|
476
|
+
new_funcs, funcs_changed = self._transform_children(list(node.function_defs))
|
|
477
|
+
new_statutes, statutes_changed = self._transform_children(list(node.statutes))
|
|
478
|
+
new_vars, vars_changed = self._transform_children(list(node.variables))
|
|
479
|
+
|
|
480
|
+
if (imports_changed or types_changed or funcs_changed or
|
|
481
|
+
statutes_changed or vars_changed):
|
|
482
|
+
return nodes.ModuleNode(
|
|
483
|
+
imports=new_imports,
|
|
484
|
+
type_defs=new_types,
|
|
485
|
+
function_defs=new_funcs,
|
|
486
|
+
statutes=new_statutes,
|
|
487
|
+
variables=new_vars,
|
|
488
|
+
source_location=node.source_location,
|
|
489
|
+
)
|
|
490
|
+
return node
|