@voodocs/cli 2.4.0 → 2.5.1
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 +47 -0
- package/README.md +26 -0
- package/lib/cli/analyze.py +277 -0
- package/lib/darkarts/priority_analyzer/__init__.py +0 -0
- package/lib/darkarts/priority_analyzer/analyzer.py +301 -0
- package/lib/darkarts/priority_analyzer/complexity.py +271 -0
- package/lib/darkarts/priority_analyzer/dependencies.py +275 -0
- package/lib/darkarts/priority_analyzer/security.py +200 -0
- package/lib/darkarts/voodocs_lite_dict.py +216 -0
- package/lib/darkarts/voodocs_lite_dict_v2.py +198 -0
- package/lib/darkarts/voodocs_lite_parser.py +343 -0
- package/package.json +5 -1
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VooDocs Lite Parser
|
|
3
|
+
|
|
4
|
+
Parses VooDocs Lite format and converts between Lite and Standard formats.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import re
|
|
8
|
+
from typing import Dict, Optional
|
|
9
|
+
from .voodocs_lite_dict import (
|
|
10
|
+
expand_text,
|
|
11
|
+
compress_text,
|
|
12
|
+
get_lite_symbol,
|
|
13
|
+
get_standard_symbol,
|
|
14
|
+
LITE_TO_STANDARD,
|
|
15
|
+
STANDARD_TO_LITE,
|
|
16
|
+
)
|
|
17
|
+
from .voodocs_lite_dict_v2 import ultra_compress, ultra_expand
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class VooDocsLiteParser:
|
|
21
|
+
"""Parser for VooDocs Lite format."""
|
|
22
|
+
|
|
23
|
+
# Lite format patterns
|
|
24
|
+
LITE_PATTERNS = {
|
|
25
|
+
'purpose': r'^>\s*(.+)$',
|
|
26
|
+
'dependencies': r'^@\s*(.+)$',
|
|
27
|
+
'assumptions': r'^!\s*(.+)$',
|
|
28
|
+
'preconditions': r'^<\s*(.+)$',
|
|
29
|
+
'postconditions': r'^>\s*(.+)$', # Same as purpose, context-dependent
|
|
30
|
+
'invariants': r'^=\s*(.+)$',
|
|
31
|
+
'complexity': r'^~\s*(.+)$',
|
|
32
|
+
'security': r'^#\s*(.+)$',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Standard format patterns
|
|
36
|
+
STANDARD_PATTERNS = {
|
|
37
|
+
'purpose': r'⊢\{([^}]+)\}',
|
|
38
|
+
'dependencies': r'∂\{([^}]+)\}',
|
|
39
|
+
'assumptions': r'⚠\{([^}]+)\}',
|
|
40
|
+
'preconditions': r'⊳\{([^}]+)\}',
|
|
41
|
+
'postconditions': r'⊲\{([^}]+)\}',
|
|
42
|
+
'invariants': r'⊨\{([^}]+)\}',
|
|
43
|
+
'complexity': r'⚡\{([^}]+)\}',
|
|
44
|
+
'security': r'🔒\{([^}]+)\}',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def parse_lite(cls, content: str) -> Dict[str, any]:
|
|
49
|
+
"""
|
|
50
|
+
Parse VooDocs Lite format annotation.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
content: Lite format annotation text
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Dictionary with parsed fields
|
|
57
|
+
"""
|
|
58
|
+
result = {
|
|
59
|
+
'purpose': None,
|
|
60
|
+
'dependencies': [],
|
|
61
|
+
'assumptions': [],
|
|
62
|
+
'preconditions': [],
|
|
63
|
+
'postconditions': [],
|
|
64
|
+
'invariants': [],
|
|
65
|
+
'complexity': None,
|
|
66
|
+
'security': [],
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
lines = content.strip().split('\n')
|
|
70
|
+
|
|
71
|
+
for line in lines:
|
|
72
|
+
line = line.strip()
|
|
73
|
+
if not line:
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
# Check each pattern
|
|
77
|
+
if line.startswith('>'):
|
|
78
|
+
# Could be purpose or postcondition
|
|
79
|
+
# If we already have purpose, it's postcondition
|
|
80
|
+
match = re.match(cls.LITE_PATTERNS['purpose'], line)
|
|
81
|
+
if match:
|
|
82
|
+
value = match.group(1).strip()
|
|
83
|
+
if result['purpose'] is None:
|
|
84
|
+
result['purpose'] = value
|
|
85
|
+
else:
|
|
86
|
+
result['postconditions'].append(value)
|
|
87
|
+
|
|
88
|
+
elif line.startswith('@'):
|
|
89
|
+
match = re.match(cls.LITE_PATTERNS['dependencies'], line)
|
|
90
|
+
if match:
|
|
91
|
+
deps = match.group(1).strip()
|
|
92
|
+
# Split by comma
|
|
93
|
+
result['dependencies'] = [d.strip() for d in deps.split(',')]
|
|
94
|
+
|
|
95
|
+
elif line.startswith('!'):
|
|
96
|
+
match = re.match(cls.LITE_PATTERNS['assumptions'], line)
|
|
97
|
+
if match:
|
|
98
|
+
result['assumptions'].append(match.group(1).strip())
|
|
99
|
+
|
|
100
|
+
elif line.startswith('<'):
|
|
101
|
+
match = re.match(cls.LITE_PATTERNS['preconditions'], line)
|
|
102
|
+
if match:
|
|
103
|
+
result['preconditions'].append(match.group(1).strip())
|
|
104
|
+
|
|
105
|
+
elif line.startswith('='):
|
|
106
|
+
match = re.match(cls.LITE_PATTERNS['invariants'], line)
|
|
107
|
+
if match:
|
|
108
|
+
result['invariants'].append(match.group(1).strip())
|
|
109
|
+
|
|
110
|
+
elif line.startswith('~'):
|
|
111
|
+
match = re.match(cls.LITE_PATTERNS['complexity'], line)
|
|
112
|
+
if match:
|
|
113
|
+
result['complexity'] = match.group(1).strip()
|
|
114
|
+
|
|
115
|
+
elif line.startswith('#'):
|
|
116
|
+
match = re.match(cls.LITE_PATTERNS['security'], line)
|
|
117
|
+
if match:
|
|
118
|
+
result['security'].append(match.group(1).strip())
|
|
119
|
+
|
|
120
|
+
return result
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def parse_standard(cls, content: str) -> Dict[str, any]:
|
|
124
|
+
"""
|
|
125
|
+
Parse standard VooDocs format annotation.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
content: Standard format annotation text
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Dictionary with parsed fields
|
|
132
|
+
"""
|
|
133
|
+
result = {
|
|
134
|
+
'purpose': None,
|
|
135
|
+
'dependencies': [],
|
|
136
|
+
'assumptions': [],
|
|
137
|
+
'preconditions': [],
|
|
138
|
+
'postconditions': [],
|
|
139
|
+
'invariants': [],
|
|
140
|
+
'complexity': None,
|
|
141
|
+
'security': [],
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Extract each field
|
|
145
|
+
for field, pattern in cls.STANDARD_PATTERNS.items():
|
|
146
|
+
matches = re.findall(pattern, content)
|
|
147
|
+
|
|
148
|
+
if field in ['purpose', 'complexity']:
|
|
149
|
+
# Single value fields
|
|
150
|
+
if matches:
|
|
151
|
+
result[field] = matches[0].strip()
|
|
152
|
+
elif field == 'dependencies':
|
|
153
|
+
# Comma-separated list
|
|
154
|
+
if matches:
|
|
155
|
+
deps = matches[0].strip()
|
|
156
|
+
result[field] = [d.strip() for d in deps.split(',')]
|
|
157
|
+
else:
|
|
158
|
+
# Multiple value fields
|
|
159
|
+
result[field] = [m.strip() for m in matches]
|
|
160
|
+
|
|
161
|
+
return result
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def lite_to_standard(cls, lite_content: str, expand_abbreviations: bool = True) -> str:
|
|
165
|
+
"""
|
|
166
|
+
Convert Lite format to Standard format.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
lite_content: Lite format annotation
|
|
170
|
+
expand_abbreviations: Whether to expand abbreviations
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Standard format annotation
|
|
174
|
+
"""
|
|
175
|
+
parsed = cls.parse_lite(lite_content)
|
|
176
|
+
|
|
177
|
+
lines = []
|
|
178
|
+
|
|
179
|
+
# Purpose
|
|
180
|
+
if parsed['purpose']:
|
|
181
|
+
text = parsed['purpose']
|
|
182
|
+
if expand_abbreviations:
|
|
183
|
+
text = expand_text(text)
|
|
184
|
+
lines.append(f"⊢{{{text}}}")
|
|
185
|
+
|
|
186
|
+
# Dependencies
|
|
187
|
+
if parsed['dependencies']:
|
|
188
|
+
deps = ', '.join(parsed['dependencies'])
|
|
189
|
+
if expand_abbreviations:
|
|
190
|
+
deps = expand_text(deps)
|
|
191
|
+
lines.append(f"∂{{{deps}}}")
|
|
192
|
+
|
|
193
|
+
# Assumptions
|
|
194
|
+
if parsed['assumptions']:
|
|
195
|
+
for assumption in parsed['assumptions']:
|
|
196
|
+
text = assumption
|
|
197
|
+
if expand_abbreviations:
|
|
198
|
+
text = expand_text(text)
|
|
199
|
+
lines.append(f"⚠{{{text}}}")
|
|
200
|
+
|
|
201
|
+
# Preconditions
|
|
202
|
+
if parsed['preconditions']:
|
|
203
|
+
for precond in parsed['preconditions']:
|
|
204
|
+
text = precond
|
|
205
|
+
if expand_abbreviations:
|
|
206
|
+
text = expand_text(text)
|
|
207
|
+
lines.append(f"⊳{{{text}}}")
|
|
208
|
+
|
|
209
|
+
# Postconditions
|
|
210
|
+
if parsed['postconditions']:
|
|
211
|
+
for postcond in parsed['postconditions']:
|
|
212
|
+
text = postcond
|
|
213
|
+
if expand_abbreviations:
|
|
214
|
+
text = expand_text(text)
|
|
215
|
+
lines.append(f"⊲{{{text}}}")
|
|
216
|
+
|
|
217
|
+
# Invariants
|
|
218
|
+
if parsed['invariants']:
|
|
219
|
+
for invariant in parsed['invariants']:
|
|
220
|
+
text = invariant
|
|
221
|
+
if expand_abbreviations:
|
|
222
|
+
text = expand_text(text)
|
|
223
|
+
lines.append(f"⊨{{{text}}}")
|
|
224
|
+
|
|
225
|
+
# Complexity
|
|
226
|
+
if parsed['complexity']:
|
|
227
|
+
text = parsed['complexity']
|
|
228
|
+
# Don't expand complexity (O(n) should stay as-is)
|
|
229
|
+
lines.append(f"⚡{{{text}}}")
|
|
230
|
+
|
|
231
|
+
# Security
|
|
232
|
+
if parsed['security']:
|
|
233
|
+
for security in parsed['security']:
|
|
234
|
+
text = security
|
|
235
|
+
if expand_abbreviations:
|
|
236
|
+
text = expand_text(text)
|
|
237
|
+
lines.append(f"🔒{{{text}}}")
|
|
238
|
+
|
|
239
|
+
return '\n'.join(lines)
|
|
240
|
+
|
|
241
|
+
@classmethod
|
|
242
|
+
def standard_to_lite(cls, standard_content: str, compress_abbreviations: bool = True) -> str:
|
|
243
|
+
"""
|
|
244
|
+
Convert Standard format to Lite format.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
standard_content: Standard format annotation
|
|
248
|
+
compress_abbreviations: Whether to compress to abbreviations
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Lite format annotation
|
|
252
|
+
"""
|
|
253
|
+
parsed = cls.parse_standard(standard_content)
|
|
254
|
+
|
|
255
|
+
lines = []
|
|
256
|
+
|
|
257
|
+
# Purpose
|
|
258
|
+
if parsed['purpose']:
|
|
259
|
+
text = parsed['purpose']
|
|
260
|
+
if compress_abbreviations:
|
|
261
|
+
text = ultra_compress(text)
|
|
262
|
+
lines.append(f">{text}")
|
|
263
|
+
|
|
264
|
+
# Dependencies
|
|
265
|
+
if parsed['dependencies']:
|
|
266
|
+
deps = ','.join(parsed['dependencies'])
|
|
267
|
+
if compress_abbreviations:
|
|
268
|
+
deps = ultra_compress(deps)
|
|
269
|
+
lines.append(f"@{deps}")
|
|
270
|
+
|
|
271
|
+
# Assumptions
|
|
272
|
+
if parsed['assumptions']:
|
|
273
|
+
for assumption in parsed['assumptions']:
|
|
274
|
+
text = assumption
|
|
275
|
+
if compress_abbreviations:
|
|
276
|
+
text = ultra_compress(text)
|
|
277
|
+
lines.append(f"!{text}")
|
|
278
|
+
|
|
279
|
+
# Preconditions
|
|
280
|
+
if parsed['preconditions']:
|
|
281
|
+
for precond in parsed['preconditions']:
|
|
282
|
+
text = precond
|
|
283
|
+
if compress_abbreviations:
|
|
284
|
+
text = ultra_compress(text)
|
|
285
|
+
lines.append(f"<{text}")
|
|
286
|
+
|
|
287
|
+
# Postconditions
|
|
288
|
+
if parsed['postconditions']:
|
|
289
|
+
for postcond in parsed['postconditions']:
|
|
290
|
+
text = postcond
|
|
291
|
+
if compress_abbreviations:
|
|
292
|
+
text = ultra_compress(text)
|
|
293
|
+
lines.append(f">{text}")
|
|
294
|
+
|
|
295
|
+
# Invariants
|
|
296
|
+
if parsed['invariants']:
|
|
297
|
+
for invariant in parsed['invariants']:
|
|
298
|
+
text = invariant
|
|
299
|
+
if compress_abbreviations:
|
|
300
|
+
text = ultra_compress(text)
|
|
301
|
+
lines.append(f"={text}")
|
|
302
|
+
|
|
303
|
+
# Complexity
|
|
304
|
+
if parsed['complexity']:
|
|
305
|
+
text = parsed['complexity']
|
|
306
|
+
# Don't compress complexity
|
|
307
|
+
lines.append(f"~{text}")
|
|
308
|
+
|
|
309
|
+
# Security
|
|
310
|
+
if parsed['security']:
|
|
311
|
+
for security in parsed['security']:
|
|
312
|
+
text = security
|
|
313
|
+
if compress_abbreviations:
|
|
314
|
+
text = ultra_compress(text)
|
|
315
|
+
lines.append(f"#{text}")
|
|
316
|
+
|
|
317
|
+
return '\n'.join(lines)
|
|
318
|
+
|
|
319
|
+
@classmethod
|
|
320
|
+
def detect_format(cls, content: str) -> str:
|
|
321
|
+
"""
|
|
322
|
+
Detect whether content is Lite or Standard format.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
'lite', 'standard', or 'unknown'
|
|
326
|
+
"""
|
|
327
|
+
# Check for Lite symbols at start of lines
|
|
328
|
+
lite_indicators = ['>', '@', '!', '<', '=', '~', '#']
|
|
329
|
+
has_lite = any(line.strip().startswith(sym) for sym in lite_indicators for line in content.split('\n'))
|
|
330
|
+
|
|
331
|
+
# Check for Standard symbols
|
|
332
|
+
standard_indicators = ['⊢', '∂', '⚠', '⊳', '⊲', '⊨', '⚡', '🔒']
|
|
333
|
+
has_standard = any(sym in content for sym in standard_indicators)
|
|
334
|
+
|
|
335
|
+
if has_lite and not has_standard:
|
|
336
|
+
return 'lite'
|
|
337
|
+
elif has_standard and not has_lite:
|
|
338
|
+
return 'standard'
|
|
339
|
+
elif has_lite and has_standard:
|
|
340
|
+
# Mixed format, prefer standard
|
|
341
|
+
return 'standard'
|
|
342
|
+
else:
|
|
343
|
+
return 'unknown'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voodocs/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "AI-Native Symbolic Documentation System - The world's first documentation tool using mathematical notation with semantic validation",
|
|
5
5
|
"main": "voodocs_cli.py",
|
|
6
6
|
"bin": {
|
|
@@ -64,6 +64,10 @@
|
|
|
64
64
|
"lib/darkarts/exceptions.py",
|
|
65
65
|
"lib/darkarts/telemetry.py",
|
|
66
66
|
"lib/darkarts/companion_files.py",
|
|
67
|
+
"lib/darkarts/voodocs_lite_dict.py",
|
|
68
|
+
"lib/darkarts/voodocs_lite_dict_v2.py",
|
|
69
|
+
"lib/darkarts/voodocs_lite_parser.py",
|
|
70
|
+
"lib/darkarts/priority_analyzer/",
|
|
67
71
|
"lib/darkarts/parsers/typescript/dist/",
|
|
68
72
|
"lib/darkarts/parsers/typescript/src/",
|
|
69
73
|
"lib/darkarts/parsers/typescript/package.json",
|