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,338 @@
1
+ """
2
+ Block-notation transpiler for Yuho.
3
+
4
+ Outputs a structured text representation of statute blocks,
5
+ providing a hierarchical view of the statute structure.
6
+ """
7
+
8
+ from typing import List, Any, Optional
9
+ from dataclasses import dataclass
10
+
11
+ from yuho.transpile.base import TranspilerBase, TranspileTarget
12
+ from yuho.ast.nodes import (
13
+ ModuleNode,
14
+ StatuteNode,
15
+ DefinitionNode,
16
+ ElementNode,
17
+ PenaltyNode,
18
+ IllustrationNode,
19
+ )
20
+ from yuho.ast.visitor import ASTVisitor
21
+
22
+
23
+ @dataclass
24
+ class Block:
25
+ """Represents a block in block-notation."""
26
+ block_type: str
27
+ name: str
28
+ content: List[str]
29
+ children: List["Block"]
30
+ level: int = 0
31
+
32
+
33
+ class BlockBuilder(ASTVisitor):
34
+ """Builds block notation from AST."""
35
+
36
+ def __init__(self):
37
+ self.blocks: List[Block] = []
38
+ self.current_statute: Optional[Block] = None
39
+
40
+ def visit_module(self, node: ModuleNode) -> None:
41
+ """Visit module node."""
42
+ for child in getattr(node, "children", []) or []:
43
+ self.visit(child)
44
+
45
+ for statute in getattr(node, "statutes", []) or []:
46
+ self.visit(statute)
47
+
48
+ def visit_statute(self, node: StatuteNode) -> None:
49
+ """Visit statute node."""
50
+ section = getattr(node, "section_number", "") or getattr(node, "section", "") or "?"
51
+ title = getattr(node, "title", "") or ""
52
+
53
+ statute_block = Block(
54
+ block_type="STATUTE",
55
+ name=f"S{section}",
56
+ content=[f"title: {title}"],
57
+ children=[],
58
+ level=0,
59
+ )
60
+ self.current_statute = statute_block
61
+
62
+ # Visit children
63
+ if hasattr(node, "definitions") and node.definitions:
64
+ self._build_definitions_block(node.definitions)
65
+
66
+ if hasattr(node, "elements") and node.elements:
67
+ self._build_elements_block(node.elements)
68
+
69
+ if hasattr(node, "penalty") and node.penalty:
70
+ self._build_penalty_block(node.penalty)
71
+
72
+ if hasattr(node, "illustrations") and node.illustrations:
73
+ self._build_illustrations_block(node.illustrations)
74
+
75
+ self.blocks.append(statute_block)
76
+ self.current_statute = None
77
+
78
+ def _build_definitions_block(self, definitions: List[Any]) -> None:
79
+ """Build definitions block."""
80
+ if not self.current_statute:
81
+ return
82
+
83
+ content = []
84
+ for defn in definitions:
85
+ term = getattr(defn, "term", "") or getattr(defn, "name", "") or "?"
86
+ definition = getattr(defn, "definition", "") or getattr(defn, "value", "") or ""
87
+ # Truncate long definitions
88
+ if len(definition) > 60:
89
+ definition = definition[:57] + "..."
90
+ content.append(f"{term} := {definition}")
91
+
92
+ block = Block(
93
+ block_type="DEFINITIONS",
94
+ name="defs",
95
+ content=content,
96
+ children=[],
97
+ level=1,
98
+ )
99
+ self.current_statute.children.append(block)
100
+
101
+ def _build_elements_block(self, elements: List[Any]) -> None:
102
+ """Build elements block."""
103
+ if not self.current_statute:
104
+ return
105
+
106
+ children = []
107
+
108
+ # Group by type
109
+ actus_reus = []
110
+ mens_rea = []
111
+ other = []
112
+
113
+ for elem in elements:
114
+ elem_type = getattr(elem, "element_type", "") or getattr(elem, "type", "") or "element"
115
+ name = getattr(elem, "name", "") or getattr(elem, "identifier", "") or "?"
116
+ desc = getattr(elem, "description", "") or getattr(elem, "value", "") or ""
117
+
118
+ if len(desc) > 50:
119
+ desc = desc[:47] + "..."
120
+
121
+ entry = f"{name}: {desc}"
122
+
123
+ if "actus" in elem_type.lower():
124
+ actus_reus.append(entry)
125
+ elif "mens" in elem_type.lower():
126
+ mens_rea.append(entry)
127
+ else:
128
+ other.append(entry)
129
+
130
+ if actus_reus:
131
+ children.append(Block(
132
+ block_type="ACTUS_REUS",
133
+ name="act",
134
+ content=actus_reus,
135
+ children=[],
136
+ level=2,
137
+ ))
138
+
139
+ if mens_rea:
140
+ children.append(Block(
141
+ block_type="MENS_REA",
142
+ name="intent",
143
+ content=mens_rea,
144
+ children=[],
145
+ level=2,
146
+ ))
147
+
148
+ if other:
149
+ children.append(Block(
150
+ block_type="CIRCUMSTANCE",
151
+ name="context",
152
+ content=other,
153
+ children=[],
154
+ level=2,
155
+ ))
156
+
157
+ block = Block(
158
+ block_type="ELEMENTS",
159
+ name="elements",
160
+ content=[],
161
+ children=children,
162
+ level=1,
163
+ )
164
+ self.current_statute.children.append(block)
165
+
166
+ def _build_penalty_block(self, penalty: Any) -> None:
167
+ """Build penalty block."""
168
+ if not self.current_statute:
169
+ return
170
+
171
+ content = []
172
+
173
+ # Handle different penalty structures
174
+ if hasattr(penalty, "imprisonment"):
175
+ imp = penalty.imprisonment
176
+ if imp:
177
+ content.append(f"imprisonment: {imp}")
178
+
179
+ if hasattr(penalty, "fine"):
180
+ fine = penalty.fine
181
+ if fine:
182
+ content.append(f"fine: {fine}")
183
+
184
+ if hasattr(penalty, "supplementary"):
185
+ supp = penalty.supplementary
186
+ if supp:
187
+ content.append(f"supplementary: {supp}")
188
+
189
+ # If penalty has clauses
190
+ if hasattr(penalty, "clauses"):
191
+ for clause in penalty.clauses or []:
192
+ clause_type = getattr(clause, "type", "") or getattr(clause, "penalty_type", "") or "?"
193
+ value = getattr(clause, "value", "") or ""
194
+ content.append(f"{clause_type}: {value}")
195
+
196
+ if content:
197
+ block = Block(
198
+ block_type="PENALTY",
199
+ name="penalty",
200
+ content=content,
201
+ children=[],
202
+ level=1,
203
+ )
204
+ self.current_statute.children.append(block)
205
+
206
+ def _build_illustrations_block(self, illustrations: List[Any]) -> None:
207
+ """Build illustrations block."""
208
+ if not self.current_statute:
209
+ return
210
+
211
+ content = []
212
+ for illus in illustrations:
213
+ label = getattr(illus, "label", "") or "?"
214
+ text = getattr(illus, "text", "") or getattr(illus, "description", "") or ""
215
+ if len(text) > 60:
216
+ text = text[:57] + "..."
217
+ content.append(f"{label} {text}")
218
+
219
+ if content:
220
+ block = Block(
221
+ block_type="ILLUSTRATIONS",
222
+ name="examples",
223
+ content=content,
224
+ children=[],
225
+ level=1,
226
+ )
227
+ self.current_statute.children.append(block)
228
+
229
+
230
+ def format_blocks(blocks: List[Block], style: str = "box") -> str:
231
+ """
232
+ Format blocks into text output.
233
+
234
+ Args:
235
+ blocks: List of Block objects
236
+ style: "box" for box drawing, "indent" for simple indentation
237
+
238
+ Returns:
239
+ Formatted string
240
+ """
241
+ lines: List[str] = []
242
+
243
+ def render_block_box(block: Block, indent: int = 0) -> None:
244
+ """Render a block with box drawing characters."""
245
+ prefix = " " * indent
246
+ width = 60 - (indent * 2)
247
+
248
+ # Top border
249
+ lines.append(f"{prefix}┌{'─' * (width - 2)}┐")
250
+
251
+ # Header
252
+ header = f" [{block.block_type}] {block.name} "
253
+ padding = width - 4 - len(header)
254
+ lines.append(f"{prefix}│{header}{'─' * max(0, padding)}│")
255
+
256
+ # Separator
257
+ lines.append(f"{prefix}├{'─' * (width - 2)}┤")
258
+
259
+ # Content
260
+ for line in block.content:
261
+ # Wrap long lines
262
+ if len(line) > width - 4:
263
+ line = line[:width - 7] + "..."
264
+ padding = width - 4 - len(line)
265
+ lines.append(f"{prefix}│ {line}{' ' * max(0, padding)} │")
266
+
267
+ if not block.content and not block.children:
268
+ lines.append(f"{prefix}│{' ' * (width - 2)}│")
269
+
270
+ # Children
271
+ if block.children:
272
+ if block.content:
273
+ lines.append(f"{prefix}├{'─' * (width - 2)}┤")
274
+ for child in block.children:
275
+ render_block_box(child, indent + 1)
276
+
277
+ # Bottom border
278
+ lines.append(f"{prefix}└{'─' * (width - 2)}┘")
279
+
280
+ def render_block_indent(block: Block, indent: int = 0) -> None:
281
+ """Render a block with simple indentation."""
282
+ prefix = " " * indent
283
+
284
+ lines.append(f"{prefix}[{block.block_type}] {block.name}")
285
+
286
+ for line in block.content:
287
+ lines.append(f"{prefix} | {line}")
288
+
289
+ for child in block.children:
290
+ render_block_indent(child, indent + 1)
291
+
292
+ # Choose renderer
293
+ renderer = render_block_box if style == "box" else render_block_indent
294
+
295
+ for block in blocks:
296
+ renderer(block)
297
+ lines.append("")
298
+
299
+ return "\n".join(lines)
300
+
301
+
302
+ class BlocksTranspiler(TranspilerBase):
303
+ """Transpiler that outputs block-notation format."""
304
+
305
+ @property
306
+ def target(self) -> TranspileTarget:
307
+ return TranspileTarget.BLOCKS
308
+
309
+ def transpile(self, ast: ModuleNode) -> str:
310
+ """
311
+ Transpile AST to block-notation format.
312
+
313
+ Args:
314
+ ast: The root ModuleNode
315
+
316
+ Returns:
317
+ Block-notation string
318
+ """
319
+ builder = BlockBuilder()
320
+ builder.visit(ast)
321
+
322
+ # Header
323
+ lines = [
324
+ "╔════════════════════════════════════════════════════════════╗",
325
+ "║ BLOCK NOTATION VIEW ║",
326
+ "╚════════════════════════════════════════════════════════════╝",
327
+ "",
328
+ ]
329
+
330
+ # Render blocks
331
+ output = format_blocks(builder.blocks, style="box")
332
+ lines.append(output)
333
+
334
+ # Footer
335
+ lines.append("")
336
+ lines.append(f"Total statutes: {len(builder.blocks)}")
337
+
338
+ return "\n".join(lines)