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,404 @@
1
+ """
2
+ GraphQL schema transpiler - generate GraphQL schema from Yuho AST.
3
+
4
+ Converts Yuho statutes and types to GraphQL schema definitions
5
+ suitable for building legal APIs. Includes:
6
+ - Type definitions for statutes, elements, penalties
7
+ - Query types for statute lookup
8
+ - Enum types for currencies, element types, etc.
9
+ """
10
+
11
+ from typing import List, Set
12
+
13
+ from yuho.ast import nodes
14
+ from yuho.ast.visitor import Visitor
15
+ from yuho.transpile.base import TranspileTarget, TranspilerBase
16
+
17
+
18
+ class GraphQLTranspiler(TranspilerBase, Visitor):
19
+ """
20
+ Transpile Yuho AST to GraphQL schema definition language (SDL).
21
+
22
+ Generates a complete GraphQL schema with types, queries, and enums
23
+ for building legal statute APIs.
24
+ """
25
+
26
+ def __init__(self, include_descriptions: bool = True):
27
+ """
28
+ Initialize GraphQL transpiler.
29
+
30
+ Args:
31
+ include_descriptions: Whether to include description comments
32
+ """
33
+ self._output: List[str] = []
34
+ self._indent_level = 0
35
+ self.include_descriptions = include_descriptions
36
+ self._defined_types: Set[str] = set()
37
+
38
+ @property
39
+ def target(self) -> TranspileTarget:
40
+ return TranspileTarget.GRAPHQL
41
+
42
+ def transpile(self, ast: nodes.ModuleNode) -> str:
43
+ """Transpile AST to GraphQL schema."""
44
+ self._output = []
45
+ self._defined_types = set()
46
+
47
+ # Emit header comment
48
+ self._emit("# Auto-generated GraphQL schema from Yuho statutes")
49
+ self._emit("# Do not edit manually")
50
+ self._emit("")
51
+
52
+ # Emit built-in scalar types
53
+ self._emit_scalars()
54
+
55
+ # Emit enums
56
+ self._emit_enums()
57
+
58
+ # Emit custom struct types from AST
59
+ for struct in ast.type_defs:
60
+ self._visit_struct_def(struct)
61
+
62
+ # Emit core legal types
63
+ self._emit_core_types()
64
+
65
+ # Emit statute types
66
+ for statute in ast.statutes:
67
+ self._visit_statute(statute)
68
+
69
+ # Emit query root
70
+ self._emit_query_root(ast)
71
+
72
+ return "\n".join(self._output)
73
+
74
+ def _emit(self, text: str) -> None:
75
+ """Add a line to output with current indentation."""
76
+ indent = " " * self._indent_level
77
+ self._output.append(f"{indent}{text}")
78
+
79
+ def _emit_blank(self) -> None:
80
+ """Add a blank line."""
81
+ self._output.append("")
82
+
83
+ def _emit_description(self, text: str, multiline: bool = False) -> None:
84
+ """Emit a GraphQL description string."""
85
+ if not self.include_descriptions:
86
+ return
87
+ if multiline or "\n" in text:
88
+ self._emit('"""')
89
+ for line in text.split("\n"):
90
+ self._emit(line)
91
+ self._emit('"""')
92
+ else:
93
+ self._emit(f'"{text}"')
94
+
95
+ # =========================================================================
96
+ # Scalar and Enum Types
97
+ # =========================================================================
98
+
99
+ def _emit_scalars(self) -> None:
100
+ """Emit custom scalar type definitions."""
101
+ self._emit_description("Monetary amount with currency")
102
+ self._emit("scalar Money")
103
+ self._emit_blank()
104
+
105
+ self._emit_description("Duration of time (e.g., imprisonment term)")
106
+ self._emit("scalar Duration")
107
+ self._emit_blank()
108
+
109
+ self._emit_description("Percentage value")
110
+ self._emit("scalar Percent")
111
+ self._emit_blank()
112
+
113
+ self._emit_description("Date in ISO 8601 format")
114
+ self._emit("scalar Date")
115
+ self._emit_blank()
116
+
117
+ def _emit_enums(self) -> None:
118
+ """Emit enum type definitions."""
119
+ # Currency enum
120
+ self._emit_description("Supported currency types")
121
+ self._emit("enum Currency {")
122
+ self._indent_level += 1
123
+ for currency in nodes.Currency:
124
+ self._emit(currency.name)
125
+ self._indent_level -= 1
126
+ self._emit("}")
127
+ self._emit_blank()
128
+
129
+ # Element type enum
130
+ self._emit_description("Types of legal elements")
131
+ self._emit("enum ElementType {")
132
+ self._indent_level += 1
133
+ self._emit("ACTUS_REUS")
134
+ self._emit("MENS_REA")
135
+ self._emit("CIRCUMSTANCE")
136
+ self._indent_level -= 1
137
+ self._emit("}")
138
+ self._emit_blank()
139
+
140
+ # =========================================================================
141
+ # Core Legal Types
142
+ # =========================================================================
143
+
144
+ def _emit_core_types(self) -> None:
145
+ """Emit core legal type definitions."""
146
+ # Definition type
147
+ self._emit_description("Legal definition within a statute")
148
+ self._emit("type Definition {")
149
+ self._indent_level += 1
150
+ self._emit_description("Term being defined")
151
+ self._emit("term: String!")
152
+ self._emit_description("Definition text")
153
+ self._emit("definition: String!")
154
+ self._indent_level -= 1
155
+ self._emit("}")
156
+ self._emit_blank()
157
+
158
+ # Element type
159
+ self._emit_description("Element of an offense (actus reus or mens rea)")
160
+ self._emit("type Element {")
161
+ self._indent_level += 1
162
+ self._emit_description("Type of element")
163
+ self._emit("elementType: ElementType!")
164
+ self._emit_description("Element name/identifier")
165
+ self._emit("name: String!")
166
+ self._emit_description("Description of the element")
167
+ self._emit("description: String!")
168
+ self._indent_level -= 1
169
+ self._emit("}")
170
+ self._emit_blank()
171
+
172
+ # Penalty type
173
+ self._emit_description("Penalty specification for a statute")
174
+ self._emit("type Penalty {")
175
+ self._indent_level += 1
176
+ self._emit_description("Minimum imprisonment term")
177
+ self._emit("imprisonmentMin: Duration")
178
+ self._emit_description("Maximum imprisonment term")
179
+ self._emit("imprisonmentMax: Duration")
180
+ self._emit_description("Minimum fine amount")
181
+ self._emit("fineMin: Money")
182
+ self._emit_description("Maximum fine amount")
183
+ self._emit("fineMax: Money")
184
+ self._emit_description("Additional penalty information")
185
+ self._emit("supplementary: String")
186
+ self._indent_level -= 1
187
+ self._emit("}")
188
+ self._emit_blank()
189
+
190
+ # Illustration type
191
+ self._emit_description("Illustration example within a statute")
192
+ self._emit("type Illustration {")
193
+ self._indent_level += 1
194
+ self._emit_description("Label (e.g., '(a)', '(b)')")
195
+ self._emit("label: String")
196
+ self._emit_description("Illustration description")
197
+ self._emit("description: String!")
198
+ self._indent_level -= 1
199
+ self._emit("}")
200
+ self._emit_blank()
201
+
202
+ # Statute type
203
+ self._emit_description("Legal statute/provision")
204
+ self._emit("type Statute {")
205
+ self._indent_level += 1
206
+ self._emit_description("Section number (e.g., '299', '300')")
207
+ self._emit("sectionNumber: String!")
208
+ self._emit_description("Title of the statute")
209
+ self._emit("title: String")
210
+ self._emit_description("Legal definitions in this statute")
211
+ self._emit("definitions: [Definition!]!")
212
+ self._emit_description("Elements of the offense")
213
+ self._emit("elements: [Element!]!")
214
+ self._emit_description("Penalty specification")
215
+ self._emit("penalty: Penalty")
216
+ self._emit_description("Illustrative examples")
217
+ self._emit("illustrations: [Illustration!]!")
218
+ self._indent_level -= 1
219
+ self._emit("}")
220
+ self._emit_blank()
221
+
222
+ # =========================================================================
223
+ # Struct Definitions
224
+ # =========================================================================
225
+
226
+ def _visit_struct_def(self, node: nodes.StructDefNode) -> None:
227
+ """Generate GraphQL type for struct definition."""
228
+ type_name = self._to_pascal_case(node.name)
229
+ if type_name in self._defined_types:
230
+ return
231
+ self._defined_types.add(type_name)
232
+
233
+ self._emit_description(f"Custom type: {node.name}")
234
+ self._emit(f"type {type_name} {{")
235
+ self._indent_level += 1
236
+
237
+ for field in node.fields:
238
+ field_name = self._to_camel_case(field.name)
239
+ field_type = self._type_to_graphql(field.type_annotation)
240
+ self._emit(f"{field_name}: {field_type}")
241
+
242
+ self._indent_level -= 1
243
+ self._emit("}")
244
+ self._emit_blank()
245
+
246
+ # =========================================================================
247
+ # Statute Processing (for query generation)
248
+ # =========================================================================
249
+
250
+ def _visit_statute(self, node: nodes.StatuteNode) -> None:
251
+ """Process statute for query type generation."""
252
+ # Statutes are represented by the generic Statute type
253
+ # Individual statute data will be resolved at runtime
254
+ pass
255
+
256
+ # =========================================================================
257
+ # Query Root
258
+ # =========================================================================
259
+
260
+ def _emit_query_root(self, ast: nodes.ModuleNode) -> None:
261
+ """Emit the Query root type."""
262
+ self._emit_description("Root query type for statute API")
263
+ self._emit("type Query {")
264
+ self._indent_level += 1
265
+
266
+ # Query for single statute by section number
267
+ self._emit_description("Get a statute by section number")
268
+ self._emit("statute(sectionNumber: String!): Statute")
269
+
270
+ # Query for all statutes
271
+ self._emit_description("Get all statutes")
272
+ self._emit("statutes: [Statute!]!")
273
+
274
+ # Query for statutes by element type
275
+ self._emit_description("Find statutes containing a specific element type")
276
+ self._emit("statutesByElementType(elementType: ElementType!): [Statute!]!")
277
+
278
+ # Search statutes by text
279
+ self._emit_description("Search statutes by text in title or definitions")
280
+ self._emit("searchStatutes(query: String!): [Statute!]!")
281
+
282
+ # Get statute definitions
283
+ self._emit_description("Get all definitions across statutes")
284
+ self._emit("allDefinitions: [Definition!]!")
285
+
286
+ self._indent_level -= 1
287
+ self._emit("}")
288
+ self._emit_blank()
289
+
290
+ # Emit Mutation type for potential write operations
291
+ self._emit_description("Root mutation type for statute API")
292
+ self._emit("type Mutation {")
293
+ self._indent_level += 1
294
+ self._emit_description("Validate a statute definition (returns validation errors)")
295
+ self._emit("validateStatute(input: StatuteInput!): ValidationResult!")
296
+ self._indent_level -= 1
297
+ self._emit("}")
298
+ self._emit_blank()
299
+
300
+ # Emit input types
301
+ self._emit_input_types()
302
+
303
+ def _emit_input_types(self) -> None:
304
+ """Emit GraphQL input types for mutations."""
305
+ # Statute input
306
+ self._emit_description("Input type for statute validation")
307
+ self._emit("input StatuteInput {")
308
+ self._indent_level += 1
309
+ self._emit("sectionNumber: String!")
310
+ self._emit("title: String")
311
+ self._emit("definitions: [DefinitionInput!]")
312
+ self._emit("elements: [ElementInput!]")
313
+ self._indent_level -= 1
314
+ self._emit("}")
315
+ self._emit_blank()
316
+
317
+ # Definition input
318
+ self._emit_description("Input type for definition")
319
+ self._emit("input DefinitionInput {")
320
+ self._indent_level += 1
321
+ self._emit("term: String!")
322
+ self._emit("definition: String!")
323
+ self._indent_level -= 1
324
+ self._emit("}")
325
+ self._emit_blank()
326
+
327
+ # Element input
328
+ self._emit_description("Input type for element")
329
+ self._emit("input ElementInput {")
330
+ self._indent_level += 1
331
+ self._emit("elementType: ElementType!")
332
+ self._emit("name: String!")
333
+ self._emit("description: String!")
334
+ self._indent_level -= 1
335
+ self._emit("}")
336
+ self._emit_blank()
337
+
338
+ # Validation result
339
+ self._emit_description("Result of statute validation")
340
+ self._emit("type ValidationResult {")
341
+ self._indent_level += 1
342
+ self._emit("valid: Boolean!")
343
+ self._emit("errors: [ValidationError!]!")
344
+ self._indent_level -= 1
345
+ self._emit("}")
346
+ self._emit_blank()
347
+
348
+ # Validation error
349
+ self._emit_description("Validation error details")
350
+ self._emit("type ValidationError {")
351
+ self._indent_level += 1
352
+ self._emit("field: String!")
353
+ self._emit("message: String!")
354
+ self._emit("code: String!")
355
+ self._indent_level -= 1
356
+ self._emit("}")
357
+ self._emit_blank()
358
+
359
+ # =========================================================================
360
+ # Type Conversion Helpers
361
+ # =========================================================================
362
+
363
+ def _type_to_graphql(self, node: nodes.TypeNode) -> str:
364
+ """Convert Yuho type to GraphQL type."""
365
+ if isinstance(node, nodes.BuiltinType):
366
+ type_mapping = {
367
+ "int": "Int",
368
+ "float": "Float",
369
+ "bool": "Boolean",
370
+ "string": "String",
371
+ "money": "Money",
372
+ "percent": "Percent",
373
+ "date": "Date",
374
+ "duration": "Duration",
375
+ "void": "Void",
376
+ }
377
+ return type_mapping.get(node.name, "String")
378
+
379
+ elif isinstance(node, nodes.NamedType):
380
+ return self._to_pascal_case(node.name)
381
+
382
+ elif isinstance(node, nodes.OptionalType):
383
+ inner = self._type_to_graphql(node.inner)
384
+ # In GraphQL, types are nullable by default
385
+ # Remove ! if present since optional
386
+ return inner.rstrip("!")
387
+
388
+ elif isinstance(node, nodes.ArrayType):
389
+ elem = self._type_to_graphql(node.element_type)
390
+ return f"[{elem}!]!"
391
+
392
+ return "String"
393
+
394
+ def _to_pascal_case(self, name: str) -> str:
395
+ """Convert name to PascalCase for GraphQL types."""
396
+ parts = name.replace("-", "_").split("_")
397
+ return "".join(p.capitalize() for p in parts)
398
+
399
+ def _to_camel_case(self, name: str) -> str:
400
+ """Convert name to camelCase for GraphQL fields."""
401
+ parts = name.replace("-", "_").split("_")
402
+ if not parts:
403
+ return name
404
+ return parts[0].lower() + "".join(p.capitalize() for p in parts[1:])
@@ -0,0 +1,217 @@
1
+ """
2
+ JSON transpiler - serialize AST to JSON with type discriminators.
3
+ """
4
+
5
+ import json
6
+ from datetime import date
7
+ from decimal import Decimal
8
+ from typing import Any, Dict, List, Optional
9
+
10
+ from yuho.ast import nodes
11
+ from yuho.ast.visitor import Visitor
12
+ from yuho.transpile.base import TranspileTarget, TranspilerBase
13
+
14
+
15
+ class JSONTranspiler(TranspilerBase, Visitor):
16
+ """
17
+ Transpile Yuho AST to JSON format.
18
+
19
+ Includes type discriminators ("_type" field) and source locations
20
+ for each node to enable round-trip parsing.
21
+ """
22
+
23
+ def __init__(self, include_locations: bool = True, indent: int = 2):
24
+ """
25
+ Initialize the JSON transpiler.
26
+
27
+ Args:
28
+ include_locations: Whether to include source locations
29
+ indent: JSON indentation level (0 for compact)
30
+ """
31
+ self.include_locations = include_locations
32
+ self.indent = indent
33
+
34
+ @property
35
+ def target(self) -> TranspileTarget:
36
+ return TranspileTarget.JSON
37
+
38
+ def transpile(self, ast: nodes.ModuleNode) -> str:
39
+ """Transpile AST to JSON string."""
40
+ data = self._to_dict(ast)
41
+ return json.dumps(data, indent=self.indent if self.indent else None, ensure_ascii=False)
42
+
43
+ def _to_dict(self, node: nodes.ASTNode) -> Dict[str, Any]:
44
+ """Convert an AST node to a dictionary."""
45
+ result: Dict[str, Any] = {"_type": type(node).__name__}
46
+
47
+ # Add source location if enabled
48
+ if self.include_locations and node.source_location:
49
+ loc = node.source_location
50
+ result["_loc"] = {
51
+ "file": loc.file,
52
+ "line": loc.line,
53
+ "col": loc.col,
54
+ "end_line": loc.end_line,
55
+ "end_col": loc.end_col,
56
+ }
57
+
58
+ # Handle specific node types
59
+ if isinstance(node, nodes.IntLit):
60
+ result["value"] = node.value
61
+ elif isinstance(node, nodes.FloatLit):
62
+ result["value"] = node.value
63
+ elif isinstance(node, nodes.BoolLit):
64
+ result["value"] = node.value
65
+ elif isinstance(node, nodes.StringLit):
66
+ result["value"] = node.value
67
+ elif isinstance(node, nodes.MoneyNode):
68
+ result["currency"] = node.currency.name
69
+ result["amount"] = str(node.amount)
70
+ elif isinstance(node, nodes.PercentNode):
71
+ result["value"] = str(node.value)
72
+ elif isinstance(node, nodes.DateNode):
73
+ result["value"] = node.value.isoformat()
74
+ elif isinstance(node, nodes.DurationNode):
75
+ result["years"] = node.years
76
+ result["months"] = node.months
77
+ result["days"] = node.days
78
+ result["hours"] = node.hours
79
+ result["minutes"] = node.minutes
80
+ result["seconds"] = node.seconds
81
+ elif isinstance(node, nodes.IdentifierNode):
82
+ result["name"] = node.name
83
+ elif isinstance(node, nodes.FieldAccessNode):
84
+ result["base"] = self._to_dict(node.base)
85
+ result["field_name"] = node.field_name
86
+ elif isinstance(node, nodes.IndexAccessNode):
87
+ result["base"] = self._to_dict(node.base)
88
+ result["index"] = self._to_dict(node.index)
89
+ elif isinstance(node, nodes.FunctionCallNode):
90
+ result["callee"] = self._to_dict(node.callee)
91
+ result["args"] = [self._to_dict(a) for a in node.args]
92
+ elif isinstance(node, nodes.BinaryExprNode):
93
+ result["left"] = self._to_dict(node.left)
94
+ result["operator"] = node.operator
95
+ result["right"] = self._to_dict(node.right)
96
+ elif isinstance(node, nodes.UnaryExprNode):
97
+ result["operator"] = node.operator
98
+ result["operand"] = self._to_dict(node.operand)
99
+ elif isinstance(node, nodes.PassExprNode):
100
+ pass # Just the type is enough
101
+ elif isinstance(node, nodes.WildcardPattern):
102
+ pass
103
+ elif isinstance(node, nodes.LiteralPattern):
104
+ result["literal"] = self._to_dict(node.literal)
105
+ elif isinstance(node, nodes.BindingPattern):
106
+ result["name"] = node.name
107
+ elif isinstance(node, nodes.FieldPattern):
108
+ result["name"] = node.name
109
+ if node.pattern:
110
+ result["pattern"] = self._to_dict(node.pattern)
111
+ elif isinstance(node, nodes.StructPattern):
112
+ result["type_name"] = node.type_name
113
+ result["fields"] = [self._to_dict(f) for f in node.fields]
114
+ elif isinstance(node, nodes.MatchArm):
115
+ result["pattern"] = self._to_dict(node.pattern)
116
+ if node.guard:
117
+ result["guard"] = self._to_dict(node.guard)
118
+ result["body"] = self._to_dict(node.body)
119
+ elif isinstance(node, nodes.MatchExprNode):
120
+ if node.scrutinee:
121
+ result["scrutinee"] = self._to_dict(node.scrutinee)
122
+ result["arms"] = [self._to_dict(a) for a in node.arms]
123
+ result["ensure_exhaustiveness"] = node.ensure_exhaustiveness
124
+ elif isinstance(node, nodes.BuiltinType):
125
+ result["name"] = node.name
126
+ elif isinstance(node, nodes.NamedType):
127
+ result["name"] = node.name
128
+ elif isinstance(node, nodes.GenericType):
129
+ result["base"] = node.base
130
+ result["type_args"] = [self._to_dict(t) for t in node.type_args]
131
+ elif isinstance(node, nodes.OptionalType):
132
+ result["inner"] = self._to_dict(node.inner)
133
+ elif isinstance(node, nodes.ArrayType):
134
+ result["element_type"] = self._to_dict(node.element_type)
135
+ elif isinstance(node, nodes.FieldDef):
136
+ result["type"] = self._to_dict(node.type_annotation)
137
+ result["name"] = node.name
138
+ elif isinstance(node, nodes.StructDefNode):
139
+ result["name"] = node.name
140
+ result["fields"] = [self._to_dict(f) for f in node.fields]
141
+ if node.type_params:
142
+ result["type_params"] = list(node.type_params)
143
+ elif isinstance(node, nodes.FieldAssignment):
144
+ result["name"] = node.name
145
+ result["value"] = self._to_dict(node.value)
146
+ elif isinstance(node, nodes.StructLiteralNode):
147
+ if node.struct_name:
148
+ result["struct_name"] = node.struct_name
149
+ result["fields"] = [self._to_dict(f) for f in node.field_values]
150
+ elif isinstance(node, nodes.ParamDef):
151
+ result["type"] = self._to_dict(node.type_annotation)
152
+ result["name"] = node.name
153
+ elif isinstance(node, nodes.Block):
154
+ result["statements"] = [self._to_dict(s) for s in node.statements]
155
+ elif isinstance(node, nodes.FunctionDefNode):
156
+ result["name"] = node.name
157
+ result["params"] = [self._to_dict(p) for p in node.params]
158
+ if node.return_type:
159
+ result["return_type"] = self._to_dict(node.return_type)
160
+ result["body"] = self._to_dict(node.body)
161
+ elif isinstance(node, nodes.VariableDecl):
162
+ result["type"] = self._to_dict(node.type_annotation)
163
+ result["name"] = node.name
164
+ if node.value:
165
+ result["value"] = self._to_dict(node.value)
166
+ elif isinstance(node, nodes.AssignmentStmt):
167
+ result["target"] = self._to_dict(node.target)
168
+ result["value"] = self._to_dict(node.value)
169
+ elif isinstance(node, nodes.ReturnStmt):
170
+ if node.value:
171
+ result["value"] = self._to_dict(node.value)
172
+ elif isinstance(node, nodes.PassStmt):
173
+ pass
174
+ elif isinstance(node, nodes.ExpressionStmt):
175
+ result["expression"] = self._to_dict(node.expression)
176
+ elif isinstance(node, nodes.DefinitionEntry):
177
+ result["term"] = node.term
178
+ result["definition"] = self._to_dict(node.definition)
179
+ elif isinstance(node, nodes.ElementNode):
180
+ result["element_type"] = node.element_type
181
+ result["name"] = node.name
182
+ result["description"] = self._to_dict(node.description)
183
+ elif isinstance(node, nodes.PenaltyNode):
184
+ if node.imprisonment_min:
185
+ result["imprisonment_min"] = self._to_dict(node.imprisonment_min)
186
+ if node.imprisonment_max:
187
+ result["imprisonment_max"] = self._to_dict(node.imprisonment_max)
188
+ if node.fine_min:
189
+ result["fine_min"] = self._to_dict(node.fine_min)
190
+ if node.fine_max:
191
+ result["fine_max"] = self._to_dict(node.fine_max)
192
+ if node.supplementary:
193
+ result["supplementary"] = self._to_dict(node.supplementary)
194
+ elif isinstance(node, nodes.IllustrationNode):
195
+ if node.label:
196
+ result["label"] = node.label
197
+ result["description"] = self._to_dict(node.description)
198
+ elif isinstance(node, nodes.StatuteNode):
199
+ result["section_number"] = node.section_number
200
+ if node.title:
201
+ result["title"] = self._to_dict(node.title)
202
+ result["definitions"] = [self._to_dict(d) for d in node.definitions]
203
+ result["elements"] = [self._to_dict(e) for e in node.elements]
204
+ if node.penalty:
205
+ result["penalty"] = self._to_dict(node.penalty)
206
+ result["illustrations"] = [self._to_dict(i) for i in node.illustrations]
207
+ elif isinstance(node, nodes.ImportNode):
208
+ result["path"] = node.path
209
+ result["imported_names"] = list(node.imported_names)
210
+ elif isinstance(node, nodes.ModuleNode):
211
+ result["imports"] = [self._to_dict(i) for i in node.imports]
212
+ result["type_defs"] = [self._to_dict(t) for t in node.type_defs]
213
+ result["function_defs"] = [self._to_dict(f) for f in node.function_defs]
214
+ result["statutes"] = [self._to_dict(s) for s in node.statutes]
215
+ result["variables"] = [self._to_dict(v) for v in node.variables]
216
+
217
+ return result