@voodocs/cli 0.1.0

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 (52) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +153 -0
  3. package/USAGE.md +314 -0
  4. package/cli.py +1340 -0
  5. package/examples/.cursorrules +437 -0
  6. package/examples/instructions/.claude/instructions.md +372 -0
  7. package/examples/instructions/.cursorrules +437 -0
  8. package/examples/instructions/.windsurfrules +437 -0
  9. package/examples/instructions/VOODOCS_INSTRUCTIONS.md +437 -0
  10. package/examples/math_example.py +41 -0
  11. package/examples/phase2_test.py +24 -0
  12. package/examples/test_compound_conditions.py +40 -0
  13. package/examples/test_math_example.py +186 -0
  14. package/lib/darkarts/README.md +115 -0
  15. package/lib/darkarts/__init__.py +16 -0
  16. package/lib/darkarts/annotations/__init__.py +34 -0
  17. package/lib/darkarts/annotations/parser.py +618 -0
  18. package/lib/darkarts/annotations/types.py +181 -0
  19. package/lib/darkarts/cli.py +128 -0
  20. package/lib/darkarts/core/__init__.py +32 -0
  21. package/lib/darkarts/core/interface.py +256 -0
  22. package/lib/darkarts/core/loader.py +231 -0
  23. package/lib/darkarts/core/plugin.py +215 -0
  24. package/lib/darkarts/core/registry.py +146 -0
  25. package/lib/darkarts/exceptions.py +51 -0
  26. package/lib/darkarts/parsers/typescript/dist/cli.d.ts +9 -0
  27. package/lib/darkarts/parsers/typescript/dist/cli.d.ts.map +1 -0
  28. package/lib/darkarts/parsers/typescript/dist/cli.js +69 -0
  29. package/lib/darkarts/parsers/typescript/dist/cli.js.map +1 -0
  30. package/lib/darkarts/parsers/typescript/dist/parser.d.ts +111 -0
  31. package/lib/darkarts/parsers/typescript/dist/parser.d.ts.map +1 -0
  32. package/lib/darkarts/parsers/typescript/dist/parser.js +365 -0
  33. package/lib/darkarts/parsers/typescript/dist/parser.js.map +1 -0
  34. package/lib/darkarts/parsers/typescript/package-lock.json +51 -0
  35. package/lib/darkarts/parsers/typescript/package.json +19 -0
  36. package/lib/darkarts/parsers/typescript/src/cli.ts +41 -0
  37. package/lib/darkarts/parsers/typescript/src/parser.ts +408 -0
  38. package/lib/darkarts/parsers/typescript/tsconfig.json +19 -0
  39. package/lib/darkarts/plugins/voodocs/__init__.py +379 -0
  40. package/lib/darkarts/plugins/voodocs/ai_native_plugin.py +151 -0
  41. package/lib/darkarts/plugins/voodocs/annotation_validator.py +280 -0
  42. package/lib/darkarts/plugins/voodocs/api_spec_generator.py +486 -0
  43. package/lib/darkarts/plugins/voodocs/documentation_generator.py +610 -0
  44. package/lib/darkarts/plugins/voodocs/html_exporter.py +260 -0
  45. package/lib/darkarts/plugins/voodocs/instruction_generator.py +706 -0
  46. package/lib/darkarts/plugins/voodocs/pdf_exporter.py +66 -0
  47. package/lib/darkarts/plugins/voodocs/test_generator.py +636 -0
  48. package/package.json +70 -0
  49. package/requirements.txt +13 -0
  50. package/templates/ci/github-actions.yml +73 -0
  51. package/templates/ci/gitlab-ci.yml +35 -0
  52. package/templates/ci/pre-commit-hook.sh +26 -0
@@ -0,0 +1,610 @@
1
+ """
2
+ VooDocs Documentation Generator
3
+
4
+ Translates @voodocs annotations (using the DarkArts language) into human-readable documentation.
5
+ """
6
+
7
+ from typing import List, Optional, Dict
8
+ from pathlib import Path
9
+ import re
10
+
11
+ from darkarts.annotations.types import (
12
+ ParsedAnnotations,
13
+ ModuleAnnotation,
14
+ ClassAnnotation,
15
+ FunctionAnnotation,
16
+ ComplexityAnnotation,
17
+ )
18
+
19
+
20
+ class DocumentationGenerator:
21
+ """Generates human-readable documentation from @voodocs annotations."""
22
+
23
+ def __init__(self):
24
+ self.output_dir = Path("docs")
25
+ self._init_translation_tables()
26
+
27
+ def _init_translation_tables(self):
28
+ """Initialize mathematical symbol translation tables."""
29
+ # Mathematical symbols to natural language
30
+ self.math_symbols = {
31
+ '∀': 'for all',
32
+ '∃': 'there exists',
33
+ '∈': 'in',
34
+ '∉': 'not in',
35
+ '⊆': 'subset of',
36
+ '⊂': 'proper subset of',
37
+ '∪': 'union',
38
+ '∩': 'intersection',
39
+ '∅': 'empty set',
40
+ '≥': 'greater than or equal to',
41
+ '≤': 'less than or equal to',
42
+ '≠': 'not equal to',
43
+ '≈': 'approximately equal to',
44
+ '⟹': 'implies',
45
+ '⟺': 'if and only if',
46
+ '⇒': 'implies',
47
+ '⇔': 'if and only if',
48
+ '→': 'leads to',
49
+ '∧': 'and',
50
+ '∨': 'or',
51
+ '¬': 'not',
52
+ 'ℝ': 'real numbers',
53
+ 'ℝ⁺': 'positive real numbers',
54
+ 'ℝ⁻': 'negative real numbers',
55
+ 'ℤ': 'integers',
56
+ 'ℤ⁺': 'positive integers',
57
+ 'ℕ': 'natural numbers',
58
+ 'ℚ': 'rational numbers',
59
+ 'ℂ': 'complex numbers',
60
+ }
61
+
62
+ # Logical operators
63
+ self.logical_ops = {
64
+ '∧': 'and',
65
+ '∨': 'or',
66
+ '¬': 'not',
67
+ '⟹': 'implies that',
68
+ '⟺': 'if and only if',
69
+ '⇒': 'implies that',
70
+ '⇔': 'if and only if',
71
+ }
72
+
73
+ def generate(self, parsed: ParsedAnnotations, output_file: Optional[str] = None) -> str:
74
+ """Generate Markdown documentation from parsed annotations."""
75
+ if not parsed.has_annotations():
76
+ return self._generate_basic_docs(parsed)
77
+
78
+ sections = []
79
+
80
+ # Title
81
+ module_name = parsed.module.name.replace("_", " ").title()
82
+ sections.append(f"# {module_name}\n")
83
+
84
+ # Badges
85
+ badges = self._generate_badges(parsed)
86
+ if badges:
87
+ sections.append(badges)
88
+
89
+ # Table of Contents
90
+ toc = self._generate_table_of_contents(parsed)
91
+ if toc:
92
+ sections.append(toc)
93
+
94
+ # Module overview
95
+ if parsed.module.module_purpose or parsed.module.dependencies or parsed.module.assumptions:
96
+ sections.append(self._generate_module_section(parsed.module))
97
+
98
+ # Classes
99
+ if parsed.module.classes:
100
+ sections.append(self._generate_classes_section(parsed.module.classes))
101
+
102
+ # Functions
103
+ functions = parsed.get_all_functions()
104
+ if functions:
105
+ sections.append(self._generate_functions_section(functions))
106
+
107
+ # Combine all sections
108
+ documentation = "\n".join(sections)
109
+
110
+ # Write to file if specified
111
+ if output_file:
112
+ output_path = Path(output_file)
113
+ output_path.parent.mkdir(parents=True, exist_ok=True)
114
+ output_path.write_text(documentation, encoding='utf-8')
115
+
116
+ return documentation
117
+
118
+ def _generate_basic_docs(self, parsed: ParsedAnnotations) -> str:
119
+ """Generate basic documentation for code without @voodocs annotations."""
120
+ sections = []
121
+
122
+ module_name = parsed.module.name.replace("_", " ").title()
123
+ sections.append(f"# {module_name}\n")
124
+ sections.append("*No @voodocs annotations found in this module.*\n")
125
+ sections.append("Consider adding @voodocs annotations to enable automatic documentation generation.\n")
126
+
127
+ return "\n".join(sections)
128
+
129
+ def _generate_module_section(self, module: ModuleAnnotation) -> str:
130
+ """Generate module overview section."""
131
+ lines = ["## Overview\n"]
132
+
133
+ if module.module_purpose:
134
+ lines.append(f"{module.module_purpose}\n")
135
+
136
+ if module.dependencies:
137
+ lines.append("### Dependencies\n")
138
+ for dep in module.dependencies:
139
+ lines.append(f"- {dep}")
140
+ lines.append("")
141
+
142
+ if module.assumptions:
143
+ lines.append("### Assumptions\n")
144
+ lines.append("This module makes the following assumptions:\n")
145
+ for assumption in module.assumptions:
146
+ lines.append(f"- {assumption}")
147
+ lines.append("")
148
+
149
+ if hasattr(module, 'security_model') and module.security_model:
150
+ lines.append("### Security Model\n")
151
+ lines.append(f"{module.security_model}\n")
152
+
153
+ return "\n".join(lines)
154
+
155
+ def _generate_classes_section(self, classes: List[ClassAnnotation]) -> str:
156
+ """Generate classes section."""
157
+ lines = ["## Classes\n"]
158
+
159
+ for cls in classes:
160
+ lines.append(f"### `{cls.name}`\n")
161
+
162
+ if cls.class_invariants:
163
+ lines.append("**Class Invariants**\n")
164
+ lines.append("The following properties must always hold true for instances of this class:\n")
165
+ for inv in cls.class_invariants:
166
+ readable_inv = self._translate_to_natural_language(inv)
167
+ lines.append(f"- {readable_inv}")
168
+ lines.append("")
169
+
170
+ if cls.state_transitions:
171
+ lines.append("**State Transitions**\n")
172
+ lines.append("Valid state changes for this class:\n")
173
+ for st in cls.state_transitions:
174
+ if hasattr(st, 'from_state'):
175
+ lines.append(f"- **{st.from_state}** → **{st.to_state}**: {st.condition}")
176
+ else:
177
+ # Parse state transition string
178
+ st_text = self._format_state_transition(str(st))
179
+ lines.append(f"- {st_text}")
180
+ lines.append("")
181
+
182
+ if hasattr(cls, 'thread_safety') and cls.thread_safety:
183
+ lines.append(f"**Thread Safety**: {cls.thread_safety}\n")
184
+
185
+ # Methods
186
+ if cls.methods:
187
+ lines.append("#### Methods\n")
188
+ for method in cls.methods:
189
+ lines.append(self._generate_function_doc(method, is_method=True))
190
+
191
+ return "\n".join(lines)
192
+
193
+ def _generate_functions_section(self, functions: List[FunctionAnnotation]) -> str:
194
+ """Generate functions section."""
195
+ lines = ["## Functions\n"]
196
+
197
+ for func in functions:
198
+ lines.append(self._generate_function_doc(func, is_method=False))
199
+
200
+ return "\n".join(lines)
201
+
202
+ def _generate_function_doc(self, func: FunctionAnnotation, is_method: bool = False) -> str:
203
+ """Generate documentation for a single function."""
204
+ lines = []
205
+
206
+ # Function signature
207
+ prefix = "####" if is_method else "###"
208
+ lines.append(f"{prefix} `{func.name}()`\n")
209
+
210
+ # Purpose/solve
211
+ if func.solve:
212
+ lines.append(f"{func.solve}\n")
213
+
214
+ # Preconditions
215
+ if func.preconditions:
216
+ lines.append("**Requirements**\n")
217
+ lines.append("The following conditions must be met before calling this function:\n")
218
+ for pre in func.preconditions:
219
+ readable_pre = self._translate_to_natural_language(pre)
220
+ lines.append(f"- {readable_pre}")
221
+ lines.append("")
222
+
223
+ # Postconditions
224
+ if func.postconditions:
225
+ lines.append("**Guarantees**\n")
226
+ lines.append("After successful execution, the following conditions will be true:\n")
227
+ for post in func.postconditions:
228
+ readable_post = self._translate_to_natural_language(post)
229
+ lines.append(f"- {readable_post}")
230
+ lines.append("")
231
+
232
+ # Invariants
233
+ if func.invariants:
234
+ lines.append("**Invariants**\n")
235
+ lines.append("The following properties remain unchanged throughout execution:\n")
236
+ for inv in func.invariants:
237
+ readable_inv = self._translate_to_natural_language(inv)
238
+ lines.append(f"- {readable_inv}")
239
+ lines.append("")
240
+
241
+ # Side effects
242
+ if hasattr(func, 'side_effects') and func.side_effects:
243
+ lines.append("**Side Effects**\n")
244
+ for effect in func.side_effects:
245
+ lines.append(f"- {effect}")
246
+ lines.append("")
247
+
248
+ # Security implications
249
+ if hasattr(func, 'security_implications') and func.security_implications:
250
+ lines.append("**⚠️ Security Considerations**\n")
251
+ for implication in func.security_implications:
252
+ lines.append(f"- {implication}")
253
+ lines.append("")
254
+
255
+ # Error handling
256
+ if hasattr(func, 'error_handling') and func.error_handling:
257
+ lines.append(f"**Error Handling**: {func.error_handling}\n")
258
+
259
+ # Optimization
260
+ if func.optimize:
261
+ readable_opt = self._translate_to_natural_language(func.optimize)
262
+ lines.append(f"**Optimization Goal**: {readable_opt}\n")
263
+
264
+ # Complexity
265
+ if func.complexity:
266
+ complexity_text = self._format_complexity(func.complexity)
267
+ lines.append(f"**Performance**: {complexity_text}\n")
268
+
269
+ # Error cases
270
+ if func.error_cases:
271
+ lines.append("**Error Cases**\n")
272
+ for error in func.error_cases:
273
+ if hasattr(error, 'condition'):
274
+ lines.append(f"- **{error.error_type}**: {error.condition}")
275
+ else:
276
+ lines.append(f"- {error}")
277
+ lines.append("")
278
+
279
+ # Usage examples (generated from preconditions)
280
+ usage_example = self._generate_usage_example(func)
281
+ if usage_example:
282
+ lines.append("**Usage Example**\n")
283
+ lines.append("```python")
284
+ lines.append(usage_example)
285
+ lines.append("```\n")
286
+
287
+ return "\n".join(lines)
288
+
289
+ def _translate_to_natural_language(self, expression: str) -> str:
290
+ """
291
+ Translate mathematical notation to natural language.
292
+
293
+ This method converts DarkArts mathematical expressions into readable English.
294
+ """
295
+ text = expression.strip()
296
+
297
+ # Handle quantifiers (∀, ∃) specially
298
+ text = self._translate_quantifiers(text)
299
+
300
+ # Handle logical operators
301
+ text = self._translate_logical_operators(text)
302
+
303
+ # Handle mathematical symbols
304
+ for symbol, replacement in self.math_symbols.items():
305
+ if symbol in text:
306
+ text = text.replace(symbol, replacement)
307
+
308
+ # Clean up extra spaces
309
+ text = re.sub(r'\s+', ' ', text)
310
+
311
+ return text
312
+
313
+ def _translate_quantifiers(self, text: str) -> str:
314
+ """Translate quantifiers (∀, ∃) into natural language."""
315
+ # Pattern: ∀ x ∈ S: P(x)
316
+ # Translation: For all x in S, P(x) holds
317
+
318
+ # Universal quantifier
319
+ text = re.sub(
320
+ r'∀\s*(\w+)\s*∈\s*([^:]+):\s*(.+)',
321
+ r'For all \1 in \2, \3',
322
+ text
323
+ )
324
+
325
+ # Existential quantifier
326
+ text = re.sub(
327
+ r'∃\s*(\w+)\s*∈\s*([^:]+):\s*(.+)',
328
+ r'There exists \1 in \2 such that \3',
329
+ text
330
+ )
331
+
332
+ return text
333
+
334
+ def _translate_logical_operators(self, text: str) -> str:
335
+ """Translate logical operators into natural language."""
336
+ # Handle compound expressions
337
+ # ⇔ (if and only if)
338
+ text = re.sub(r'\s*⇔\s*', ' if and only if ', text)
339
+ text = re.sub(r'\s*⟺\s*', ' if and only if ', text)
340
+
341
+ # ⇒ (implies)
342
+ text = re.sub(r'\s*⇒\s*', ' implies ', text)
343
+ text = re.sub(r'\s*⟹\s*', ' implies ', text)
344
+
345
+ # ∧ (and)
346
+ text = re.sub(r'\s*∧\s*', ' and ', text)
347
+
348
+ # ∨ (or)
349
+ text = re.sub(r'\s*∨\s*', ' or ', text)
350
+
351
+ # ¬ (not)
352
+ text = re.sub(r'¬\s*', 'not ', text)
353
+
354
+ return text
355
+
356
+ def _format_state_transition(self, transition: str) -> str:
357
+ """Format a state transition string for better readability."""
358
+ # Pattern: STATE1 → STATE2: condition
359
+ match = re.match(r'(.+?)\s*→\s*(.+?):\s*(.+)', transition)
360
+ if match:
361
+ from_state, to_state, condition = match.groups()
362
+ return f"**{from_state.strip()}** → **{to_state.strip()}**: {condition.strip()}"
363
+ return transition
364
+
365
+ def _format_complexity(self, complexity: ComplexityAnnotation) -> str:
366
+ """Format complexity annotation into readable text."""
367
+ parts = []
368
+
369
+ if complexity.time:
370
+ parts.append(f"Time complexity is {complexity.time}")
371
+
372
+ if complexity.space:
373
+ parts.append(f"space complexity is {complexity.space}")
374
+
375
+ if parts:
376
+ return ", ".join(parts) + "."
377
+
378
+ return "Complexity not specified."
379
+
380
+ def generate_api_reference(self, parsed: ParsedAnnotations, output_file: Optional[str] = None) -> str:
381
+ """Generate API reference documentation."""
382
+ sections = []
383
+
384
+ # Title
385
+ module_name = parsed.module.name.replace("_", " ").title()
386
+ sections.append(f"# {module_name} API Reference\n")
387
+
388
+ # Table of contents
389
+ sections.append("## Table of Contents\n")
390
+
391
+ if parsed.module.classes:
392
+ sections.append("### Classes\n")
393
+ for cls in parsed.module.classes:
394
+ sections.append(f"- [{cls.name}](#{cls.name.lower()})")
395
+ sections.append("")
396
+
397
+ functions = parsed.get_all_functions()
398
+ if functions:
399
+ sections.append("### Functions\n")
400
+ for func in functions:
401
+ sections.append(f"- [{func.name}](#{func.name.lower()})")
402
+ sections.append("")
403
+
404
+ # Detailed API docs
405
+ sections.append("---\n")
406
+
407
+ # Classes
408
+ if parsed.module.classes:
409
+ for cls in parsed.module.classes:
410
+ sections.append(f"## {cls.name}\n")
411
+ sections.append(f"*Defined at line {cls.line_number}*\n")
412
+
413
+ if cls.class_invariants:
414
+ sections.append("### Class Invariants\n")
415
+ sections.append("```")
416
+ for inv in cls.class_invariants:
417
+ sections.append(inv)
418
+ sections.append("```\n")
419
+
420
+ if cls.methods:
421
+ sections.append("### Methods\n")
422
+ for method in cls.methods:
423
+ sections.append(f"#### `{method.name}()`\n")
424
+ sections.append(f"*Defined at line {method.line_number}*\n")
425
+
426
+ if method.solve:
427
+ sections.append(f"**Purpose:** {method.solve}\n")
428
+
429
+ if method.preconditions:
430
+ sections.append("**Preconditions:**")
431
+ sections.append("```")
432
+ for pre in method.preconditions:
433
+ sections.append(pre)
434
+ sections.append("```\n")
435
+
436
+ if method.postconditions:
437
+ sections.append("**Postconditions:**")
438
+ sections.append("```")
439
+ for post in method.postconditions:
440
+ sections.append(post)
441
+ sections.append("```\n")
442
+
443
+ # Functions
444
+ if functions:
445
+ sections.append("## Functions\n")
446
+ for func in functions:
447
+ sections.append(f"### `{func.name}()`\n")
448
+ sections.append(f"*Defined at line {func.line_number}*\n")
449
+
450
+ if func.solve:
451
+ sections.append(f"**Purpose:** {func.solve}\n")
452
+
453
+ if func.preconditions:
454
+ sections.append("**Preconditions:**")
455
+ sections.append("```")
456
+ for pre in func.preconditions:
457
+ sections.append(pre)
458
+ sections.append("```\n")
459
+
460
+ if func.postconditions:
461
+ sections.append("**Postconditions:**")
462
+ sections.append("```")
463
+ for post in func.postconditions:
464
+ sections.append(post)
465
+ sections.append("```\n")
466
+
467
+ if func.complexity:
468
+ complexity_text = self._format_complexity(func.complexity)
469
+ sections.append(f"**Performance:** {complexity_text}\n")
470
+
471
+ documentation = "\n".join(sections)
472
+
473
+ if output_file:
474
+ output_path = Path(output_file)
475
+ output_path.parent.mkdir(parents=True, exist_ok=True)
476
+ output_path.write_text(documentation, encoding='utf-8')
477
+
478
+ return documentation
479
+
480
+ def _generate_usage_example(self, func: FunctionAnnotation) -> Optional[str]:
481
+ """Generate a usage example from function annotations."""
482
+ if not func.preconditions:
483
+ return None
484
+
485
+ # Try to infer example values from preconditions
486
+ example_params = self._infer_example_params(func.preconditions)
487
+
488
+ if not example_params:
489
+ return None
490
+
491
+ # Build example code
492
+ lines = []
493
+ lines.append(f"# Example usage of {func.name}()")
494
+
495
+ # Add variable assignments
496
+ for param, value in example_params.items():
497
+ lines.append(f"{param} = {value}")
498
+
499
+ # Add function call
500
+ param_list = ", ".join(example_params.keys())
501
+ lines.append(f"result = {func.name}({param_list})")
502
+
503
+ # Add postcondition check if available
504
+ if func.postconditions:
505
+ lines.append("")
506
+ lines.append("# Postconditions are guaranteed:")
507
+ for post in func.postconditions[:2]: # Show first 2 postconditions
508
+ readable_post = self._translate_to_natural_language(post)
509
+ lines.append(f"# - {readable_post}")
510
+
511
+ return "\n".join(lines)
512
+
513
+ def _infer_example_params(self, preconditions: List[str]) -> Dict[str, str]:
514
+ """Infer example parameter values from preconditions."""
515
+ params = {}
516
+
517
+ for pre in preconditions:
518
+ # Pattern: "x > 0" → x = 1
519
+ if match := re.match(r'(\w+)\s*>\s*(\d+)', pre):
520
+ var_name = match.group(1)
521
+ min_val = int(match.group(2))
522
+ params[var_name] = str(min_val + 1)
523
+
524
+ # Pattern: "x >= 0" → x = 0
525
+ elif match := re.match(r'(\w+)\s*>=\s*(\d+)', pre):
526
+ var_name = match.group(1)
527
+ min_val = int(match.group(2))
528
+ params[var_name] = str(min_val)
529
+
530
+ # Pattern: "x < 100" → x = 50
531
+ elif match := re.match(r'(\w+)\s*<\s*(\d+)', pre):
532
+ var_name = match.group(1)
533
+ max_val = int(match.group(2))
534
+ if var_name in params:
535
+ # Combine with existing constraint
536
+ current = int(params[var_name])
537
+ params[var_name] = str(min(current, max_val - 1))
538
+ else:
539
+ params[var_name] = str(max_val // 2)
540
+
541
+ # Pattern: "x in [1, 2, 3]" → x = 1
542
+ elif match := re.match(r'(\w+)\s+in\s+\[(.+?)\]', pre):
543
+ var_name = match.group(1)
544
+ values = match.group(2).split(',')
545
+ params[var_name] = values[0].strip()
546
+
547
+ # Pattern: "x is string" → x = "example"
548
+ elif match := re.search(r'(\w+).*(?:is|must be).*string', pre, re.IGNORECASE):
549
+ var_name = match.group(1)
550
+ params[var_name] = '"example"'
551
+
552
+ # Pattern: "x is list" → x = [1, 2, 3]
553
+ elif match := re.search(r'(\w+).*(?:is|must be).*list', pre, re.IGNORECASE):
554
+ var_name = match.group(1)
555
+ params[var_name] = '[1, 2, 3]'
556
+
557
+ # Pattern: "x is boolean" → x = True
558
+ elif match := re.search(r'(\w+).*(?:is|must be).*(?:bool|boolean)', pre, re.IGNORECASE):
559
+ var_name = match.group(1)
560
+ params[var_name] = 'True'
561
+
562
+ return params
563
+
564
+ def _generate_badges(self, parsed: ParsedAnnotations) -> str:
565
+ """Generate status badges for the documentation."""
566
+ badges = []
567
+
568
+ # Documentation coverage badge
569
+ total_items = len(parsed.module.classes) + len(parsed.get_all_functions())
570
+ annotated_items = sum(1 for _ in parsed.module.classes if _.invariants or _.state_transitions)
571
+ annotated_items += sum(1 for f in parsed.get_all_functions() if f.preconditions or f.postconditions)
572
+
573
+ if total_items > 0:
574
+ coverage = int((annotated_items / total_items) * 100)
575
+ color = "green" if coverage >= 80 else "yellow" if coverage >= 50 else "red"
576
+ badges.append(f"![Documentation Coverage](https://img.shields.io/badge/docs-{coverage}%25-{color})")
577
+
578
+ # VooDocs badge
579
+ badges.append("![VooDocs](https://img.shields.io/badge/documented_with-VooDocs-purple)")
580
+
581
+ # Language badge
582
+ lang = parsed.language.value if hasattr(parsed.language, 'value') else str(parsed.language)
583
+ badges.append(f"![Language](https://img.shields.io/badge/language-{lang}-blue)")
584
+
585
+ return " ".join(badges) + "\\n" if badges else ""
586
+
587
+ def _generate_table_of_contents(self, parsed: ParsedAnnotations) -> str:
588
+ """Generate table of contents for the documentation."""
589
+ lines = []
590
+ lines.append("## Table of Contents\\n")
591
+
592
+ # Module overview
593
+ if parsed.module.module_purpose or parsed.module.dependencies or parsed.module.assumptions:
594
+ lines.append("- [Overview](#overview)")
595
+
596
+ # Classes
597
+ if parsed.module.classes:
598
+ lines.append("- [Classes](#classes)")
599
+ for cls in parsed.module.classes:
600
+ lines.append(f" - [{cls.name}](#{cls.name.lower()})")
601
+
602
+ # Functions
603
+ functions = parsed.get_all_functions()
604
+ if functions:
605
+ lines.append("- [Functions](#functions)")
606
+ for func in functions:
607
+ lines.append(f" - [{func.name}](#{func.name.lower()})")
608
+
609
+ lines.append("")
610
+ return "\\n".join(lines)