@voodocs/cli 2.5.2 → 3.0.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.
- package/CHANGELOG.md +156 -235
- package/README.md +215 -399
- package/lib/cli/__init__.py +1 -3
- package/lib/cli/init.py +11 -13
- package/lib/darkarts/annotations/darkarts_parser.py +159 -142
- package/lib/darkarts/annotations/darkarts_parser_v2.py.bak +238 -0
- package/lib/darkarts/companion_files.py +62 -9
- package/lib/darkarts/context/ai_instructions.py +385 -571
- package/lib/darkarts/context/ai_instructions_v2.py.bak +741 -0
- package/lib/darkarts/darkarts_abbreviations.py +324 -0
- package/lib/darkarts/darkarts_parser_v3.py +486 -0
- package/lib/darkarts/darkarts_patterns.py +249 -0
- package/lib/darkarts/darkarts_symbols.py +276 -0
- package/lib/darkarts/plugins/voodocs/documentation_generator.py +31 -2
- package/lib/darkarts/validation/semantic.py +135 -18
- package/package.json +5 -4
- package/lib/cli/convert.py +0 -131
- package/lib/darkarts/voodocs_lite_dict.py +0 -216
- package/lib/darkarts/voodocs_lite_dict_v2.py +0 -198
- package/lib/darkarts/voodocs_lite_parser.py +0 -343
package/lib/cli/__init__.py
CHANGED
|
@@ -16,7 +16,7 @@ This module provides the command-line interface for VooDocs.
|
|
|
16
16
|
import click
|
|
17
17
|
from typing import Optional
|
|
18
18
|
|
|
19
|
-
__version__ = "
|
|
19
|
+
__version__ = "3.0.0"
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@click.group()
|
|
@@ -42,7 +42,6 @@ from .benchmark import benchmark
|
|
|
42
42
|
from .fix import fix
|
|
43
43
|
from .context import context
|
|
44
44
|
from .analyze import analyze
|
|
45
|
-
from .convert import convert
|
|
46
45
|
from .companion import companion
|
|
47
46
|
|
|
48
47
|
# Register commands
|
|
@@ -54,7 +53,6 @@ cli.add_command(benchmark)
|
|
|
54
53
|
cli.add_command(fix)
|
|
55
54
|
cli.add_command(context)
|
|
56
55
|
cli.add_command(analyze)
|
|
57
|
-
cli.add_command(convert)
|
|
58
56
|
cli.add_command(companion)
|
|
59
57
|
|
|
60
58
|
|
package/lib/cli/init.py
CHANGED
|
@@ -636,24 +636,22 @@ def _create_darkarts_examples(examples_dir: Path):
|
|
|
636
636
|
"""Create example files with symbolic @darkarts annotations."""
|
|
637
637
|
|
|
638
638
|
ts_example = '''/**@darkarts
|
|
639
|
-
⊢{Example TypeScript module demonstrating
|
|
639
|
+
⊢{Example TypeScript module demonstrating DarkArts v3.0.0 annotations}
|
|
640
640
|
∂{}
|
|
641
|
-
⚠{TypeScript
|
|
641
|
+
⚠{TypeScript env available}
|
|
642
642
|
*/
|
|
643
643
|
|
|
644
644
|
/**@darkarts
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
Returns a greeting message
|
|
651
|
-
Message includes the provided name
|
|
652
|
-
}
|
|
653
|
-
⊨{
|
|
654
|
-
Does ¬ modify input parameters
|
|
655
|
-
}
|
|
645
|
+
⊢{greet fn w/ name & age}
|
|
646
|
+
∂{}
|
|
647
|
+
⊳{name:string,age>0}
|
|
648
|
+
⊲{ret greeting msg}
|
|
649
|
+
⊨{no input mod}
|
|
656
650
|
⚡{O(1)}
|
|
651
|
+
⇄{greet↔farewell}
|
|
652
|
+
∴{invalid→err}
|
|
653
|
+
∀{name is string}
|
|
654
|
+
≈{~1ms}
|
|
657
655
|
*/
|
|
658
656
|
export function greet(name: string, age: number): string {
|
|
659
657
|
return `Hello, ${name}! You are ${age} years old.`;
|
|
@@ -1,78 +1,151 @@
|
|
|
1
1
|
"""@darkarts
|
|
2
|
-
⊢parser:darkarts.annotations
|
|
3
|
-
∂{re,typing,
|
|
4
|
-
⚠{@darkarts∈docstrings,unicode-support}
|
|
5
|
-
⊨{∀parse→structured-output,¬modify-src,handle-errors}
|
|
2
|
+
⊢{parser:darkarts.annotations.v3-wrapper}
|
|
3
|
+
∂{re,typing,darkarts_parser_v3}
|
|
4
|
+
⚠{@darkarts∈docstrings,unicode-support,v3-compatible}
|
|
5
|
+
⊨{∀parse→structured-output,¬modify-src,handle-errors,backward-compat}
|
|
6
6
|
🔒{read-only}
|
|
7
|
-
⚡{O(n
|
|
7
|
+
⚡{O(n)|n=annotation-length}
|
|
8
8
|
|
|
9
|
-
DarkArts Annotation Parser
|
|
9
|
+
DarkArts Annotation Parser (v3.0.0 Wrapper)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Wraps the v3.0.0 parser to maintain backward compatibility with existing code.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import re
|
|
15
|
+
import sys
|
|
15
16
|
from typing import Dict, List, Optional, Any
|
|
16
17
|
from dataclasses import dataclass, field
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
# Import v3.0.0 parser
|
|
21
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
22
|
+
try:
|
|
23
|
+
from darkarts.darkarts_parser_v3 import DarkArtsParserV3, DarkArtsAnnotation as V3Annotation
|
|
24
|
+
except ImportError:
|
|
25
|
+
# Fallback for direct import
|
|
26
|
+
import darkarts_parser_v3
|
|
27
|
+
DarkArtsParserV3 = darkarts_parser_v3.DarkArtsParserV3
|
|
28
|
+
V3Annotation = darkarts_parser_v3.DarkArtsAnnotation
|
|
17
29
|
|
|
18
30
|
from .symbols import VOCABULARY, get_meta_symbols
|
|
19
31
|
|
|
20
32
|
|
|
21
33
|
@dataclass
|
|
22
34
|
class DarkArtsAnnotation:
|
|
23
|
-
"""Parsed DarkArts annotation."""
|
|
24
|
-
|
|
35
|
+
"""Parsed DarkArts annotation (v3.0.0 compatible)."""
|
|
36
|
+
# Core fields (v2.x compatible)
|
|
37
|
+
module: Optional[str] = None # From ⊢ (purpose)
|
|
25
38
|
dependencies: List[str] = field(default_factory=list) # From ∂
|
|
26
39
|
assumptions: List[str] = field(default_factory=list) # From ⚠
|
|
27
40
|
invariants: List[str] = field(default_factory=list) # From ⊨
|
|
28
|
-
security:
|
|
29
|
-
performance: Optional[str] = None # From ⚡
|
|
30
|
-
complexity: Optional[str] = None # From
|
|
31
|
-
|
|
32
|
-
|
|
41
|
+
security: List[str] = field(default_factory=list) # From 🔒
|
|
42
|
+
performance: Optional[str] = None # From ⚡ (complexity)
|
|
43
|
+
complexity: Optional[str] = None # From ⚡
|
|
44
|
+
|
|
45
|
+
# v3.0.0 new fields
|
|
46
|
+
preconditions: List[str] = field(default_factory=list) # From ⊳
|
|
47
|
+
postconditions: List[str] = field(default_factory=list) # From ⊲
|
|
48
|
+
bidirectional: List[str] = field(default_factory=list) # From ⇄
|
|
49
|
+
side_effects: List[str] = field(default_factory=list) # From ⊕
|
|
50
|
+
forbidden: List[str] = field(default_factory=list) # From ⊗
|
|
51
|
+
approximation: List[str] = field(default_factory=list) # From ≈
|
|
52
|
+
consequence: List[str] = field(default_factory=list) # From ∴
|
|
53
|
+
universal: List[str] = field(default_factory=list) # From ∀
|
|
54
|
+
existential: List[str] = field(default_factory=list) # From ∃
|
|
55
|
+
|
|
56
|
+
# Metadata
|
|
33
57
|
raw_text: str = "" # Original annotation text
|
|
58
|
+
line_number: int = 0
|
|
59
|
+
|
|
60
|
+
# Legacy fields (for backward compatibility)
|
|
61
|
+
objectives: Optional[str] = None # Deprecated
|
|
62
|
+
configuration: Optional[str] = None # Deprecated
|
|
34
63
|
|
|
35
64
|
def to_dict(self) -> Dict[str, Any]:
|
|
36
65
|
"""Convert to dictionary."""
|
|
37
66
|
return {
|
|
38
67
|
'module': self.module,
|
|
68
|
+
'purpose': self.module, # Alias
|
|
39
69
|
'dependencies': self.dependencies,
|
|
40
70
|
'assumptions': self.assumptions,
|
|
71
|
+
'preconditions': self.preconditions,
|
|
72
|
+
'postconditions': self.postconditions,
|
|
41
73
|
'invariants': self.invariants,
|
|
42
74
|
'security': self.security,
|
|
43
75
|
'performance': self.performance,
|
|
44
76
|
'complexity': self.complexity,
|
|
45
|
-
'
|
|
46
|
-
'
|
|
77
|
+
'bidirectional': self.bidirectional,
|
|
78
|
+
'side_effects': self.side_effects,
|
|
79
|
+
'forbidden': self.forbidden,
|
|
80
|
+
'approximation': self.approximation,
|
|
81
|
+
'consequence': self.consequence,
|
|
82
|
+
'universal': self.universal,
|
|
83
|
+
'existential': self.existential,
|
|
84
|
+
'raw_text': self.raw_text,
|
|
85
|
+
'line_number': self.line_number,
|
|
47
86
|
}
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def from_v3_annotation(cls, v3_ann: V3Annotation) -> 'DarkArtsAnnotation':
|
|
90
|
+
"""Create from v3.0.0 annotation."""
|
|
91
|
+
return cls(
|
|
92
|
+
module=v3_ann.purpose,
|
|
93
|
+
dependencies=v3_ann.dependencies,
|
|
94
|
+
assumptions=v3_ann.assumptions,
|
|
95
|
+
preconditions=v3_ann.preconditions,
|
|
96
|
+
postconditions=v3_ann.postconditions,
|
|
97
|
+
invariants=v3_ann.invariants,
|
|
98
|
+
security=v3_ann.security,
|
|
99
|
+
complexity=v3_ann.complexity,
|
|
100
|
+
performance=v3_ann.complexity, # Alias
|
|
101
|
+
bidirectional=v3_ann.bidirectional,
|
|
102
|
+
side_effects=v3_ann.side_effects,
|
|
103
|
+
forbidden=v3_ann.forbidden,
|
|
104
|
+
approximation=v3_ann.approximation,
|
|
105
|
+
consequence=v3_ann.consequence,
|
|
106
|
+
universal=v3_ann.universal,
|
|
107
|
+
existential=v3_ann.existential,
|
|
108
|
+
raw_text=v3_ann.raw_text,
|
|
109
|
+
line_number=v3_ann.line_number,
|
|
110
|
+
)
|
|
48
111
|
|
|
49
112
|
|
|
50
113
|
class DarkArtsParser:
|
|
51
114
|
"""
|
|
52
|
-
Parser for DarkArts symbolic annotations.
|
|
115
|
+
Parser for DarkArts symbolic annotations (v3.0.0).
|
|
53
116
|
|
|
54
|
-
|
|
117
|
+
Wraps the v3.0.0 parser to maintain backward compatibility.
|
|
55
118
|
"""
|
|
56
119
|
|
|
57
120
|
# Pattern to match @darkarts annotations
|
|
58
121
|
PATTERN = r'"""@darkarts\s*(.*?)\s*"""'
|
|
59
122
|
|
|
60
|
-
# Meta symbol mappings
|
|
123
|
+
# Meta symbol mappings (v3.0.0)
|
|
61
124
|
META_SYMBOLS = {
|
|
62
|
-
'⊢': '
|
|
125
|
+
'⊢': 'purpose',
|
|
63
126
|
'∂': 'dependencies',
|
|
64
127
|
'⚠': 'assumptions',
|
|
128
|
+
'⊳': 'preconditions',
|
|
129
|
+
'⊲': 'postconditions',
|
|
65
130
|
'⊨': 'invariants',
|
|
131
|
+
'⚡': 'complexity',
|
|
66
132
|
'🔒': 'security',
|
|
67
|
-
'
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
'
|
|
133
|
+
'⇄': 'bidirectional',
|
|
134
|
+
'⊕': 'side_effects',
|
|
135
|
+
'⊗': 'forbidden',
|
|
136
|
+
'≈': 'approximation',
|
|
137
|
+
'∴': 'consequence',
|
|
138
|
+
'∀': 'universal',
|
|
139
|
+
'∃': 'existential',
|
|
71
140
|
}
|
|
72
141
|
|
|
73
|
-
def __init__(self):
|
|
142
|
+
def __init__(self, enable_validation: bool = False):
|
|
74
143
|
"""Initialize the parser."""
|
|
75
|
-
self.
|
|
144
|
+
self.v3_parser = DarkArtsParserV3(enable_validation=enable_validation)
|
|
145
|
+
try:
|
|
146
|
+
self.meta_symbols = get_meta_symbols()
|
|
147
|
+
except:
|
|
148
|
+
self.meta_symbols = self.META_SYMBOLS
|
|
76
149
|
|
|
77
150
|
def parse(self, source_code: str) -> List[DarkArtsAnnotation]:
|
|
78
151
|
"""
|
|
@@ -84,15 +157,14 @@ class DarkArtsParser:
|
|
|
84
157
|
Returns:
|
|
85
158
|
List of parsed annotations
|
|
86
159
|
"""
|
|
87
|
-
|
|
160
|
+
# Use v3.0.0 parser
|
|
161
|
+
v3_annotations = self.v3_parser.parse(source_code)
|
|
88
162
|
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
annotation = self.parse_annotation(annotation_text)
|
|
95
|
-
annotations.append(annotation)
|
|
163
|
+
# Convert to legacy format
|
|
164
|
+
annotations = []
|
|
165
|
+
for v3_ann in v3_annotations:
|
|
166
|
+
ann = DarkArtsAnnotation.from_v3_annotation(v3_ann)
|
|
167
|
+
annotations.append(ann)
|
|
96
168
|
|
|
97
169
|
return annotations
|
|
98
170
|
|
|
@@ -106,133 +178,78 @@ class DarkArtsParser:
|
|
|
106
178
|
Returns:
|
|
107
179
|
Parsed annotation
|
|
108
180
|
"""
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
# Split into lines
|
|
112
|
-
lines = text.strip().split('\n')
|
|
113
|
-
|
|
114
|
-
for line in lines:
|
|
115
|
-
line = line.strip()
|
|
116
|
-
if not line:
|
|
117
|
-
continue
|
|
118
|
-
|
|
119
|
-
# Check for meta symbols
|
|
120
|
-
for symbol, field_name in self.META_SYMBOLS.items():
|
|
121
|
-
if line.startswith(symbol):
|
|
122
|
-
# Extract content after symbol
|
|
123
|
-
content = line[len(symbol):].strip()
|
|
124
|
-
self._parse_field(annotation, field_name, content)
|
|
125
|
-
break
|
|
126
|
-
|
|
127
|
-
return annotation
|
|
128
|
-
|
|
129
|
-
def _parse_field(self, annotation: DarkArtsAnnotation, field_name: str, content: str):
|
|
130
|
-
"""
|
|
131
|
-
Parse a field and update annotation.
|
|
132
|
-
|
|
133
|
-
Args:
|
|
134
|
-
annotation: Annotation to update
|
|
135
|
-
field_name: Name of field (module, dependencies, etc.)
|
|
136
|
-
content: Content to parse
|
|
137
|
-
"""
|
|
138
|
-
if field_name == 'module':
|
|
139
|
-
annotation.module = content
|
|
140
|
-
|
|
141
|
-
elif field_name == 'dependencies':
|
|
142
|
-
annotation.dependencies = self._parse_set(content)
|
|
143
|
-
|
|
144
|
-
elif field_name == 'assumptions':
|
|
145
|
-
annotation.assumptions = self._parse_set(content)
|
|
146
|
-
|
|
147
|
-
elif field_name == 'invariants':
|
|
148
|
-
annotation.invariants = self._parse_set(content)
|
|
149
|
-
|
|
150
|
-
elif field_name == 'security':
|
|
151
|
-
annotation.security = content
|
|
181
|
+
# Wrap in @darkarts marker for v3 parser
|
|
182
|
+
source = f'"""@darkarts\n{text}\n"""'
|
|
152
183
|
|
|
153
|
-
|
|
154
|
-
|
|
184
|
+
# Parse with v3.0.0 parser
|
|
185
|
+
v3_annotations = self.v3_parser.parse(source)
|
|
155
186
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
elif field_name == 'configuration':
|
|
163
|
-
annotation.configuration = content
|
|
187
|
+
if v3_annotations:
|
|
188
|
+
return DarkArtsAnnotation.from_v3_annotation(v3_annotations[0])
|
|
189
|
+
else:
|
|
190
|
+
# Return empty annotation
|
|
191
|
+
return DarkArtsAnnotation(raw_text=text)
|
|
164
192
|
|
|
165
|
-
def
|
|
193
|
+
def extract_field(self, text: str, symbol: str) -> Optional[str]:
|
|
166
194
|
"""
|
|
167
|
-
|
|
195
|
+
Extract a specific field from annotation text.
|
|
168
196
|
|
|
169
197
|
Args:
|
|
170
|
-
|
|
198
|
+
text: Annotation text
|
|
199
|
+
symbol: Symbol to extract (e.g., '⊢', '∂')
|
|
171
200
|
|
|
172
201
|
Returns:
|
|
173
|
-
|
|
202
|
+
Extracted field value or None
|
|
174
203
|
"""
|
|
175
|
-
#
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if char == '{' and not in_string:
|
|
188
|
-
depth += 1
|
|
189
|
-
elif char == '}' and not in_string:
|
|
190
|
-
depth -= 1
|
|
191
|
-
elif char == '"':
|
|
192
|
-
in_string = not in_string
|
|
193
|
-
elif char == ',' and depth == 0 and not in_string:
|
|
194
|
-
item = ''.join(current).strip()
|
|
195
|
-
if item:
|
|
196
|
-
items.append(item)
|
|
197
|
-
current = []
|
|
198
|
-
continue
|
|
199
|
-
|
|
200
|
-
current.append(char)
|
|
201
|
-
|
|
202
|
-
# Add last item
|
|
203
|
-
if current:
|
|
204
|
-
item = ''.join(current).strip()
|
|
205
|
-
if item:
|
|
206
|
-
items.append(item)
|
|
207
|
-
|
|
208
|
-
return items
|
|
204
|
+
# Parse full annotation
|
|
205
|
+
ann = self.parse_annotation(text)
|
|
206
|
+
|
|
207
|
+
# Map symbol to field
|
|
208
|
+
field_name = self.META_SYMBOLS.get(symbol)
|
|
209
|
+
if field_name:
|
|
210
|
+
value = getattr(ann, field_name, None)
|
|
211
|
+
if isinstance(value, list):
|
|
212
|
+
return ', '.join(value) if value else None
|
|
213
|
+
return value
|
|
214
|
+
|
|
215
|
+
return None
|
|
209
216
|
|
|
210
|
-
def
|
|
217
|
+
def validate(self, source_code: str) -> List[str]:
|
|
211
218
|
"""
|
|
212
|
-
|
|
219
|
+
Validate @darkarts annotations in source code.
|
|
213
220
|
|
|
214
221
|
Args:
|
|
215
|
-
|
|
222
|
+
source_code: Source code containing @darkarts annotations
|
|
216
223
|
|
|
217
224
|
Returns:
|
|
218
|
-
List of
|
|
225
|
+
List of validation errors
|
|
219
226
|
"""
|
|
220
|
-
|
|
221
|
-
|
|
227
|
+
# Use v3.0.0 parser with validation enabled
|
|
228
|
+
parser = DarkArtsParserV3(enable_validation=True)
|
|
229
|
+
annotations = parser.parse(source_code)
|
|
222
230
|
|
|
223
|
-
|
|
231
|
+
errors = []
|
|
232
|
+
for ann in annotations:
|
|
233
|
+
if ann.validation_errors:
|
|
234
|
+
errors.extend(ann.validation_errors)
|
|
235
|
+
|
|
236
|
+
return errors
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# Backward compatibility aliases
|
|
240
|
+
def parse_darkarts_annotation(text: str) -> DarkArtsAnnotation:
|
|
241
|
+
"""Parse a single @darkarts annotation (legacy function)."""
|
|
242
|
+
parser = DarkArtsParser()
|
|
243
|
+
return parser.parse_annotation(text)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def extract_darkarts_annotations(source_code: str) -> List[DarkArtsAnnotation]:
|
|
247
|
+
"""Extract all @darkarts annotations from source code (legacy function)."""
|
|
248
|
+
parser = DarkArtsParser()
|
|
249
|
+
return parser.parse(source_code)
|
|
224
250
|
|
|
225
251
|
|
|
226
|
-
# Convenience function
|
|
227
252
|
def parse_darkarts(source_code: str) -> List[DarkArtsAnnotation]:
|
|
228
|
-
"""
|
|
229
|
-
Parse @darkarts annotations from source code.
|
|
230
|
-
|
|
231
|
-
Args:
|
|
232
|
-
source_code: Source code containing @darkarts annotations
|
|
233
|
-
|
|
234
|
-
Returns:
|
|
235
|
-
List of parsed annotations
|
|
236
|
-
"""
|
|
253
|
+
"""Parse @darkarts annotations from source code (legacy function)."""
|
|
237
254
|
parser = DarkArtsParser()
|
|
238
255
|
return parser.parse(source_code)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""@darkarts
|
|
2
|
+
⊢parser:darkarts.annotations
|
|
3
|
+
∂{re,typing,symbols}
|
|
4
|
+
⚠{@darkarts∈docstrings,unicode-support}
|
|
5
|
+
⊨{∀parse→structured-output,¬modify-src,handle-errors}
|
|
6
|
+
🔒{read-only}
|
|
7
|
+
⚡{O(n²)|n=annotation-length,4-loops,depth=2}
|
|
8
|
+
|
|
9
|
+
DarkArts Annotation Parser
|
|
10
|
+
|
|
11
|
+
Parses @darkarts symbolic annotations from source code.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import re
|
|
15
|
+
from typing import Dict, List, Optional, Any
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
|
|
18
|
+
from .symbols import VOCABULARY, get_meta_symbols
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class DarkArtsAnnotation:
|
|
23
|
+
"""Parsed DarkArts annotation."""
|
|
24
|
+
module: Optional[str] = None # From ⊢
|
|
25
|
+
dependencies: List[str] = field(default_factory=list) # From ∂
|
|
26
|
+
assumptions: List[str] = field(default_factory=list) # From ⚠
|
|
27
|
+
invariants: List[str] = field(default_factory=list) # From ⊨
|
|
28
|
+
security: Optional[str] = None # From 🔒
|
|
29
|
+
performance: Optional[str] = None # From ⚡
|
|
30
|
+
complexity: Optional[str] = None # From 📊
|
|
31
|
+
objectives: Optional[str] = None # From 🎯
|
|
32
|
+
configuration: Optional[str] = None # From ⚙️
|
|
33
|
+
raw_text: str = "" # Original annotation text
|
|
34
|
+
|
|
35
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
36
|
+
"""Convert to dictionary."""
|
|
37
|
+
return {
|
|
38
|
+
'module': self.module,
|
|
39
|
+
'dependencies': self.dependencies,
|
|
40
|
+
'assumptions': self.assumptions,
|
|
41
|
+
'invariants': self.invariants,
|
|
42
|
+
'security': self.security,
|
|
43
|
+
'performance': self.performance,
|
|
44
|
+
'complexity': self.complexity,
|
|
45
|
+
'objectives': self.objectives,
|
|
46
|
+
'configuration': self.configuration,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DarkArtsParser:
|
|
51
|
+
"""
|
|
52
|
+
Parser for DarkArts symbolic annotations.
|
|
53
|
+
|
|
54
|
+
Extracts and parses @darkarts annotations from source code.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# Pattern to match @darkarts annotations
|
|
58
|
+
PATTERN = r'"""@darkarts\s*(.*?)\s*"""'
|
|
59
|
+
|
|
60
|
+
# Meta symbol mappings
|
|
61
|
+
META_SYMBOLS = {
|
|
62
|
+
'⊢': 'module',
|
|
63
|
+
'∂': 'dependencies',
|
|
64
|
+
'⚠': 'assumptions',
|
|
65
|
+
'⊨': 'invariants',
|
|
66
|
+
'🔒': 'security',
|
|
67
|
+
'⚡': 'performance',
|
|
68
|
+
'📊': 'complexity',
|
|
69
|
+
'🎯': 'objectives',
|
|
70
|
+
'⚙️': 'configuration',
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
def __init__(self):
|
|
74
|
+
"""Initialize the parser."""
|
|
75
|
+
self.meta_symbols = get_meta_symbols()
|
|
76
|
+
|
|
77
|
+
def parse(self, source_code: str) -> List[DarkArtsAnnotation]:
|
|
78
|
+
"""
|
|
79
|
+
Parse all @darkarts annotations from source code.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
source_code: Source code containing @darkarts annotations
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
List of parsed annotations
|
|
86
|
+
"""
|
|
87
|
+
annotations = []
|
|
88
|
+
|
|
89
|
+
# Find all @darkarts blocks
|
|
90
|
+
matches = re.finditer(self.PATTERN, source_code, re.DOTALL)
|
|
91
|
+
|
|
92
|
+
for match in matches:
|
|
93
|
+
annotation_text = match.group(1)
|
|
94
|
+
annotation = self.parse_annotation(annotation_text)
|
|
95
|
+
annotations.append(annotation)
|
|
96
|
+
|
|
97
|
+
return annotations
|
|
98
|
+
|
|
99
|
+
def parse_annotation(self, text: str) -> DarkArtsAnnotation:
|
|
100
|
+
"""
|
|
101
|
+
Parse a single @darkarts annotation.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
text: Annotation text (without @darkarts marker)
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Parsed annotation
|
|
108
|
+
"""
|
|
109
|
+
annotation = DarkArtsAnnotation(raw_text=text)
|
|
110
|
+
|
|
111
|
+
# Split into lines
|
|
112
|
+
lines = text.strip().split('\n')
|
|
113
|
+
|
|
114
|
+
for line in lines:
|
|
115
|
+
line = line.strip()
|
|
116
|
+
if not line:
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
# Check for meta symbols
|
|
120
|
+
for symbol, field_name in self.META_SYMBOLS.items():
|
|
121
|
+
if line.startswith(symbol):
|
|
122
|
+
# Extract content after symbol
|
|
123
|
+
content = line[len(symbol):].strip()
|
|
124
|
+
self._parse_field(annotation, field_name, content)
|
|
125
|
+
break
|
|
126
|
+
|
|
127
|
+
return annotation
|
|
128
|
+
|
|
129
|
+
def _parse_field(self, annotation: DarkArtsAnnotation, field_name: str, content: str):
|
|
130
|
+
"""
|
|
131
|
+
Parse a field and update annotation.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
annotation: Annotation to update
|
|
135
|
+
field_name: Name of field (module, dependencies, etc.)
|
|
136
|
+
content: Content to parse
|
|
137
|
+
"""
|
|
138
|
+
if field_name == 'module':
|
|
139
|
+
annotation.module = content
|
|
140
|
+
|
|
141
|
+
elif field_name == 'dependencies':
|
|
142
|
+
annotation.dependencies = self._parse_set(content)
|
|
143
|
+
|
|
144
|
+
elif field_name == 'assumptions':
|
|
145
|
+
annotation.assumptions = self._parse_set(content)
|
|
146
|
+
|
|
147
|
+
elif field_name == 'invariants':
|
|
148
|
+
annotation.invariants = self._parse_set(content)
|
|
149
|
+
|
|
150
|
+
elif field_name == 'security':
|
|
151
|
+
annotation.security = content
|
|
152
|
+
|
|
153
|
+
elif field_name == 'performance':
|
|
154
|
+
annotation.performance = content
|
|
155
|
+
|
|
156
|
+
elif field_name == 'complexity':
|
|
157
|
+
annotation.complexity = content
|
|
158
|
+
|
|
159
|
+
elif field_name == 'objectives':
|
|
160
|
+
annotation.objectives = content
|
|
161
|
+
|
|
162
|
+
elif field_name == 'configuration':
|
|
163
|
+
annotation.configuration = content
|
|
164
|
+
|
|
165
|
+
def _parse_set(self, content: str) -> List[str]:
|
|
166
|
+
"""
|
|
167
|
+
Parse a set notation {a,b,c} into a list.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
content: Content like "{a,b,c}" or "a,b,c"
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
List of items
|
|
174
|
+
"""
|
|
175
|
+
# Remove braces if present
|
|
176
|
+
content = content.strip()
|
|
177
|
+
if content.startswith('{') and content.endswith('}'):
|
|
178
|
+
content = content[1:-1]
|
|
179
|
+
|
|
180
|
+
# Split by comma
|
|
181
|
+
items = []
|
|
182
|
+
current = []
|
|
183
|
+
depth = 0
|
|
184
|
+
in_string = False
|
|
185
|
+
|
|
186
|
+
for char in content:
|
|
187
|
+
if char == '{' and not in_string:
|
|
188
|
+
depth += 1
|
|
189
|
+
elif char == '}' and not in_string:
|
|
190
|
+
depth -= 1
|
|
191
|
+
elif char == '"':
|
|
192
|
+
in_string = not in_string
|
|
193
|
+
elif char == ',' and depth == 0 and not in_string:
|
|
194
|
+
item = ''.join(current).strip()
|
|
195
|
+
if item:
|
|
196
|
+
items.append(item)
|
|
197
|
+
current = []
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
current.append(char)
|
|
201
|
+
|
|
202
|
+
# Add last item
|
|
203
|
+
if current:
|
|
204
|
+
item = ''.join(current).strip()
|
|
205
|
+
if item:
|
|
206
|
+
items.append(item)
|
|
207
|
+
|
|
208
|
+
return items
|
|
209
|
+
|
|
210
|
+
def parse_file(self, file_path: str) -> List[DarkArtsAnnotation]:
|
|
211
|
+
"""
|
|
212
|
+
Parse @darkarts annotations from a file.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
file_path: Path to source file
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
List of parsed annotations
|
|
219
|
+
"""
|
|
220
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
221
|
+
source_code = f.read()
|
|
222
|
+
|
|
223
|
+
return self.parse(source_code)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
# Convenience function
|
|
227
|
+
def parse_darkarts(source_code: str) -> List[DarkArtsAnnotation]:
|
|
228
|
+
"""
|
|
229
|
+
Parse @darkarts annotations from source code.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
source_code: Source code containing @darkarts annotations
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
List of parsed annotations
|
|
236
|
+
"""
|
|
237
|
+
parser = DarkArtsParser()
|
|
238
|
+
return parser.parse(source_code)
|