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
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Statute scaffold generator for Yuho.
|
|
3
|
+
|
|
4
|
+
Generates boilerplate .yh files with proper structure for
|
|
5
|
+
quick statute creation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional, List
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class ScaffoldConfig:
|
|
18
|
+
"""Configuration for scaffold generation."""
|
|
19
|
+
section: str
|
|
20
|
+
title: str
|
|
21
|
+
with_definitions: bool = True
|
|
22
|
+
with_actus_reus: bool = True
|
|
23
|
+
with_mens_rea: bool = True
|
|
24
|
+
with_penalty: bool = True
|
|
25
|
+
with_illustrations: bool = True
|
|
26
|
+
num_definitions: int = 1
|
|
27
|
+
num_illustrations: int = 1
|
|
28
|
+
template: str = "standard" # standard, minimal, full
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def normalize_section(section: str) -> str:
|
|
32
|
+
"""
|
|
33
|
+
Normalize section number format.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
"299" -> "299"
|
|
37
|
+
"s299" -> "299"
|
|
38
|
+
"S299A" -> "299A"
|
|
39
|
+
"section 299" -> "299"
|
|
40
|
+
"""
|
|
41
|
+
# Remove common prefixes
|
|
42
|
+
section = re.sub(r"^(?:s(?:ection)?\s*)", "", section, flags=re.IGNORECASE)
|
|
43
|
+
return section.strip()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def generate_filename(section: str, title: str) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Generate a filename from section and title.
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
("299", "Culpable Homicide") -> "s299_culpable_homicide.yh"
|
|
52
|
+
"""
|
|
53
|
+
# Normalize section
|
|
54
|
+
section = normalize_section(section).lower()
|
|
55
|
+
|
|
56
|
+
# Convert title to snake_case
|
|
57
|
+
title_snake = re.sub(r"[^\w\s]", "", title.lower())
|
|
58
|
+
title_snake = re.sub(r"\s+", "_", title_snake.strip())
|
|
59
|
+
|
|
60
|
+
return f"s{section}_{title_snake}.yh"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def generate_standard_scaffold(config: ScaffoldConfig) -> str:
|
|
64
|
+
"""Generate standard scaffold with typical structure."""
|
|
65
|
+
lines: List[str] = []
|
|
66
|
+
|
|
67
|
+
section = normalize_section(config.section)
|
|
68
|
+
|
|
69
|
+
# Header comment
|
|
70
|
+
lines.append(f"// Statute: Section {section} - {config.title}")
|
|
71
|
+
lines.append(f"// Generated by: yuho generate")
|
|
72
|
+
lines.append("")
|
|
73
|
+
|
|
74
|
+
# Statute declaration
|
|
75
|
+
lines.append(f'statute "{section}" "{config.title}" {{')
|
|
76
|
+
|
|
77
|
+
# Definitions
|
|
78
|
+
if config.with_definitions:
|
|
79
|
+
lines.append("")
|
|
80
|
+
lines.append(" definitions {")
|
|
81
|
+
for i in range(config.num_definitions):
|
|
82
|
+
term = f"term_{i + 1}" if config.num_definitions > 1 else "key_term"
|
|
83
|
+
lines.append(f' "{term}" := "TODO: Define {term}";')
|
|
84
|
+
lines.append(" }")
|
|
85
|
+
|
|
86
|
+
# Elements
|
|
87
|
+
lines.append("")
|
|
88
|
+
lines.append(" elements {")
|
|
89
|
+
|
|
90
|
+
if config.with_actus_reus:
|
|
91
|
+
lines.append(' actus_reus act := "TODO: Describe the physical act or conduct";')
|
|
92
|
+
|
|
93
|
+
if config.with_mens_rea:
|
|
94
|
+
lines.append(' mens_rea intent := "TODO: Describe the required mental state";')
|
|
95
|
+
|
|
96
|
+
lines.append(" }")
|
|
97
|
+
|
|
98
|
+
# Penalty
|
|
99
|
+
if config.with_penalty:
|
|
100
|
+
lines.append("")
|
|
101
|
+
lines.append(" penalty {")
|
|
102
|
+
lines.append(" imprisonment := 10Y; // TODO: Set appropriate term")
|
|
103
|
+
lines.append(" fine := SGD 10000; // TODO: Set appropriate fine")
|
|
104
|
+
lines.append(" }")
|
|
105
|
+
|
|
106
|
+
# Illustrations
|
|
107
|
+
if config.with_illustrations:
|
|
108
|
+
lines.append("")
|
|
109
|
+
lines.append(" illustrations {")
|
|
110
|
+
for i in range(config.num_illustrations):
|
|
111
|
+
label = chr(ord('a') + i)
|
|
112
|
+
lines.append(f' ({label}) "TODO: Provide example {label} illustrating application";')
|
|
113
|
+
lines.append(" }")
|
|
114
|
+
|
|
115
|
+
lines.append("}")
|
|
116
|
+
|
|
117
|
+
return "\n".join(lines)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def generate_minimal_scaffold(config: ScaffoldConfig) -> str:
|
|
121
|
+
"""Generate minimal scaffold with only required sections."""
|
|
122
|
+
lines: List[str] = []
|
|
123
|
+
|
|
124
|
+
section = normalize_section(config.section)
|
|
125
|
+
|
|
126
|
+
lines.append(f'statute "{section}" "{config.title}" {{')
|
|
127
|
+
lines.append("")
|
|
128
|
+
lines.append(" elements {")
|
|
129
|
+
lines.append(' actus_reus act := "TODO: Define";')
|
|
130
|
+
lines.append(" }")
|
|
131
|
+
lines.append("")
|
|
132
|
+
lines.append("}")
|
|
133
|
+
|
|
134
|
+
return "\n".join(lines)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def generate_full_scaffold(config: ScaffoldConfig) -> str:
|
|
138
|
+
"""Generate full scaffold with all possible sections."""
|
|
139
|
+
lines: List[str] = []
|
|
140
|
+
|
|
141
|
+
section = normalize_section(config.section)
|
|
142
|
+
|
|
143
|
+
# Header
|
|
144
|
+
lines.append("// ============================================================================")
|
|
145
|
+
lines.append(f"// SECTION {section}: {config.title.upper()}")
|
|
146
|
+
lines.append("// ============================================================================")
|
|
147
|
+
lines.append("//")
|
|
148
|
+
lines.append("// Description: TODO: Provide overview of this statute")
|
|
149
|
+
lines.append("// Source: TODO: Reference source legislation")
|
|
150
|
+
lines.append("// Effective: TODO: Date")
|
|
151
|
+
lines.append("//")
|
|
152
|
+
lines.append("")
|
|
153
|
+
|
|
154
|
+
lines.append(f'statute "{section}" "{config.title}" {{')
|
|
155
|
+
|
|
156
|
+
# Definitions
|
|
157
|
+
lines.append("")
|
|
158
|
+
lines.append(" // Define key legal terms used in this statute")
|
|
159
|
+
lines.append(" definitions {")
|
|
160
|
+
lines.append(' "perpetrator" := "The person who commits the act";')
|
|
161
|
+
lines.append(' "victim" := "The person against whom the act is committed";')
|
|
162
|
+
lines.append(' "property" := "Any movable or immovable asset";')
|
|
163
|
+
lines.append(" }")
|
|
164
|
+
|
|
165
|
+
# Elements
|
|
166
|
+
lines.append("")
|
|
167
|
+
lines.append(" // Elements that must be proven for conviction")
|
|
168
|
+
lines.append(" elements {")
|
|
169
|
+
lines.append(' actus_reus physical_act := "TODO: The physical conduct or act";')
|
|
170
|
+
lines.append(' actus_reus result := "TODO: The result or consequence (if any)";')
|
|
171
|
+
lines.append(' mens_rea intention := "TODO: The required mental state";')
|
|
172
|
+
lines.append(' circumstance context := "TODO: Circumstances that must exist";')
|
|
173
|
+
lines.append(" }")
|
|
174
|
+
|
|
175
|
+
# Exceptions / Defenses
|
|
176
|
+
lines.append("")
|
|
177
|
+
lines.append(" // Exceptions or defenses to liability")
|
|
178
|
+
lines.append(" // exceptions {")
|
|
179
|
+
lines.append(" // defense := TODO;")
|
|
180
|
+
lines.append(" // }")
|
|
181
|
+
|
|
182
|
+
# Penalty
|
|
183
|
+
lines.append("")
|
|
184
|
+
lines.append(" // Punishment upon conviction")
|
|
185
|
+
lines.append(" penalty {")
|
|
186
|
+
lines.append(" imprisonment := 2Y to 10Y;")
|
|
187
|
+
lines.append(" fine := SGD 5000 to SGD 50000;")
|
|
188
|
+
lines.append(' supplementary := "Or both imprisonment and fine";')
|
|
189
|
+
lines.append(" }")
|
|
190
|
+
|
|
191
|
+
# Illustrations
|
|
192
|
+
lines.append("")
|
|
193
|
+
lines.append(" // Examples illustrating application of this statute")
|
|
194
|
+
lines.append(" illustrations {")
|
|
195
|
+
lines.append(' (a) "Example showing when the offense IS committed: TODO";')
|
|
196
|
+
lines.append(' (b) "Example showing when the offense is NOT committed: TODO";')
|
|
197
|
+
lines.append(' (c) "Borderline case for consideration: TODO";')
|
|
198
|
+
lines.append(" }")
|
|
199
|
+
|
|
200
|
+
# Cross-references
|
|
201
|
+
lines.append("")
|
|
202
|
+
lines.append(" // Related statutes")
|
|
203
|
+
lines.append(" // references {")
|
|
204
|
+
lines.append(" // see \"300\"; // Related offense")
|
|
205
|
+
lines.append(" // see \"301\"; // Enhanced penalty provision")
|
|
206
|
+
lines.append(" // }")
|
|
207
|
+
|
|
208
|
+
lines.append("}")
|
|
209
|
+
|
|
210
|
+
return "\n".join(lines)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def generate_scaffold(config: ScaffoldConfig) -> str:
|
|
214
|
+
"""
|
|
215
|
+
Generate scaffold based on template type.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
config: Scaffold configuration
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Generated Yuho source code
|
|
222
|
+
"""
|
|
223
|
+
generators = {
|
|
224
|
+
"standard": generate_standard_scaffold,
|
|
225
|
+
"minimal": generate_minimal_scaffold,
|
|
226
|
+
"full": generate_full_scaffold,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
generator = generators.get(config.template, generate_standard_scaffold)
|
|
230
|
+
return generator(config)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def run_generate(
|
|
234
|
+
section: str,
|
|
235
|
+
title: str,
|
|
236
|
+
output: Optional[str] = None,
|
|
237
|
+
template: str = "standard",
|
|
238
|
+
no_definitions: bool = False,
|
|
239
|
+
no_penalty: bool = False,
|
|
240
|
+
no_illustrations: bool = False,
|
|
241
|
+
force: bool = False,
|
|
242
|
+
verbose: bool = False,
|
|
243
|
+
color: bool = True,
|
|
244
|
+
) -> None:
|
|
245
|
+
"""
|
|
246
|
+
Run scaffold generator.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
section: Section number (e.g., "299", "s300A")
|
|
250
|
+
title: Statute title
|
|
251
|
+
output: Output file path (None = auto-generate)
|
|
252
|
+
template: Template type (standard, minimal, full)
|
|
253
|
+
no_definitions: Skip definitions block
|
|
254
|
+
no_penalty: Skip penalty block
|
|
255
|
+
no_illustrations: Skip illustrations block
|
|
256
|
+
force: Overwrite existing file
|
|
257
|
+
verbose: Verbose output
|
|
258
|
+
color: Use colors
|
|
259
|
+
"""
|
|
260
|
+
# Validate template
|
|
261
|
+
valid_templates = ["standard", "minimal", "full"]
|
|
262
|
+
if template not in valid_templates:
|
|
263
|
+
click.echo(f"Error: Unknown template '{template}'", err=True)
|
|
264
|
+
click.echo(f"Available: {', '.join(valid_templates)}", err=True)
|
|
265
|
+
raise SystemExit(1)
|
|
266
|
+
|
|
267
|
+
# Build config
|
|
268
|
+
config = ScaffoldConfig(
|
|
269
|
+
section=section,
|
|
270
|
+
title=title,
|
|
271
|
+
template=template,
|
|
272
|
+
with_definitions=not no_definitions,
|
|
273
|
+
with_penalty=not no_penalty,
|
|
274
|
+
with_illustrations=not no_illustrations,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Full template ignores individual flags
|
|
278
|
+
if template == "full":
|
|
279
|
+
config.with_definitions = True
|
|
280
|
+
config.with_penalty = True
|
|
281
|
+
config.with_illustrations = True
|
|
282
|
+
|
|
283
|
+
# Generate
|
|
284
|
+
code = generate_scaffold(config)
|
|
285
|
+
|
|
286
|
+
# Determine output path
|
|
287
|
+
if output:
|
|
288
|
+
path = Path(output)
|
|
289
|
+
else:
|
|
290
|
+
filename = generate_filename(section, title)
|
|
291
|
+
path = Path(filename)
|
|
292
|
+
|
|
293
|
+
# Check existing
|
|
294
|
+
if path.exists() and not force:
|
|
295
|
+
click.echo(f"Error: File already exists: {path}", err=True)
|
|
296
|
+
click.echo("Use --force to overwrite", err=True)
|
|
297
|
+
raise SystemExit(1)
|
|
298
|
+
|
|
299
|
+
# Write
|
|
300
|
+
path.write_text(code + "\n")
|
|
301
|
+
|
|
302
|
+
# Output
|
|
303
|
+
if verbose:
|
|
304
|
+
click.echo(click.style("Generated scaffold:", fg="cyan", bold=True))
|
|
305
|
+
click.echo("")
|
|
306
|
+
click.echo(code)
|
|
307
|
+
click.echo("")
|
|
308
|
+
|
|
309
|
+
click.echo(click.style(f"✓ Created: {path}", fg="green"))
|
|
310
|
+
|
|
311
|
+
# Show tips
|
|
312
|
+
click.echo("")
|
|
313
|
+
click.echo(click.style("Next steps:", fg="yellow"))
|
|
314
|
+
click.echo(f" 1. Edit {path} to fill in TODO sections")
|
|
315
|
+
click.echo(f" 2. Run: yuho check {path}")
|
|
316
|
+
click.echo(f" 3. Run: yuho transpile {path} --target english")
|