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.
Files changed (91) hide show
  1. yuho/__init__.py +16 -0
  2. yuho/ast/__init__.py +196 -0
  3. yuho/ast/builder.py +926 -0
  4. yuho/ast/constant_folder.py +280 -0
  5. yuho/ast/dead_code.py +199 -0
  6. yuho/ast/exhaustiveness.py +503 -0
  7. yuho/ast/nodes.py +907 -0
  8. yuho/ast/overlap.py +291 -0
  9. yuho/ast/reachability.py +293 -0
  10. yuho/ast/scope_analysis.py +490 -0
  11. yuho/ast/transformer.py +490 -0
  12. yuho/ast/type_check.py +471 -0
  13. yuho/ast/type_inference.py +425 -0
  14. yuho/ast/visitor.py +239 -0
  15. yuho/cli/__init__.py +14 -0
  16. yuho/cli/commands/__init__.py +1 -0
  17. yuho/cli/commands/api.py +431 -0
  18. yuho/cli/commands/ast_viz.py +334 -0
  19. yuho/cli/commands/check.py +218 -0
  20. yuho/cli/commands/config.py +311 -0
  21. yuho/cli/commands/contribute.py +122 -0
  22. yuho/cli/commands/diff.py +487 -0
  23. yuho/cli/commands/explain.py +240 -0
  24. yuho/cli/commands/fmt.py +253 -0
  25. yuho/cli/commands/generate.py +316 -0
  26. yuho/cli/commands/graph.py +410 -0
  27. yuho/cli/commands/init.py +120 -0
  28. yuho/cli/commands/library.py +656 -0
  29. yuho/cli/commands/lint.py +503 -0
  30. yuho/cli/commands/lsp.py +36 -0
  31. yuho/cli/commands/preview.py +377 -0
  32. yuho/cli/commands/repl.py +444 -0
  33. yuho/cli/commands/serve.py +44 -0
  34. yuho/cli/commands/test.py +528 -0
  35. yuho/cli/commands/transpile.py +121 -0
  36. yuho/cli/commands/wizard.py +370 -0
  37. yuho/cli/completions.py +182 -0
  38. yuho/cli/error_formatter.py +193 -0
  39. yuho/cli/main.py +1064 -0
  40. yuho/config/__init__.py +46 -0
  41. yuho/config/loader.py +235 -0
  42. yuho/config/mask.py +194 -0
  43. yuho/config/schema.py +147 -0
  44. yuho/library/__init__.py +84 -0
  45. yuho/library/index.py +328 -0
  46. yuho/library/install.py +699 -0
  47. yuho/library/lockfile.py +330 -0
  48. yuho/library/package.py +421 -0
  49. yuho/library/resolver.py +791 -0
  50. yuho/library/signature.py +335 -0
  51. yuho/llm/__init__.py +45 -0
  52. yuho/llm/config.py +75 -0
  53. yuho/llm/factory.py +123 -0
  54. yuho/llm/prompts.py +146 -0
  55. yuho/llm/providers.py +383 -0
  56. yuho/llm/utils.py +470 -0
  57. yuho/lsp/__init__.py +14 -0
  58. yuho/lsp/code_action_handler.py +518 -0
  59. yuho/lsp/completion_handler.py +85 -0
  60. yuho/lsp/diagnostics.py +100 -0
  61. yuho/lsp/hover_handler.py +130 -0
  62. yuho/lsp/server.py +1425 -0
  63. yuho/mcp/__init__.py +10 -0
  64. yuho/mcp/server.py +1452 -0
  65. yuho/parser/__init__.py +8 -0
  66. yuho/parser/source_location.py +108 -0
  67. yuho/parser/wrapper.py +311 -0
  68. yuho/testing/__init__.py +48 -0
  69. yuho/testing/coverage.py +274 -0
  70. yuho/testing/fixtures.py +263 -0
  71. yuho/transpile/__init__.py +52 -0
  72. yuho/transpile/alloy_transpiler.py +546 -0
  73. yuho/transpile/base.py +100 -0
  74. yuho/transpile/blocks_transpiler.py +338 -0
  75. yuho/transpile/english_transpiler.py +470 -0
  76. yuho/transpile/graphql_transpiler.py +404 -0
  77. yuho/transpile/json_transpiler.py +217 -0
  78. yuho/transpile/jsonld_transpiler.py +250 -0
  79. yuho/transpile/latex_preamble.py +161 -0
  80. yuho/transpile/latex_transpiler.py +406 -0
  81. yuho/transpile/latex_utils.py +206 -0
  82. yuho/transpile/mermaid_transpiler.py +357 -0
  83. yuho/transpile/registry.py +275 -0
  84. yuho/verify/__init__.py +43 -0
  85. yuho/verify/alloy.py +352 -0
  86. yuho/verify/combined.py +218 -0
  87. yuho/verify/z3_solver.py +1155 -0
  88. yuho-5.0.0.dist-info/METADATA +186 -0
  89. yuho-5.0.0.dist-info/RECORD +91 -0
  90. yuho-5.0.0.dist-info/WHEEL +4 -0
  91. yuho-5.0.0.dist-info/entry_points.txt +2 -0
@@ -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