@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
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""@darkarts
|
|
2
|
+
⊢{darkarts:pattern-shortcuts}
|
|
3
|
+
∂{typing,re}
|
|
4
|
+
⚠{patterns≥15-types}
|
|
5
|
+
⊨{bidirectional:expand↔compress}
|
|
6
|
+
⚡{O(1):lookup}
|
|
7
|
+
🔒{read-only:patterns}
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
DarkArts v3.0.0 - Pattern Shortcuts
|
|
12
|
+
|
|
13
|
+
This module provides pattern shortcuts for common validations and constraints.
|
|
14
|
+
Designed for maximum token efficiency in preconditions and postconditions.
|
|
15
|
+
|
|
16
|
+
Pattern types:
|
|
17
|
+
- Type patterns (:uuid, :email, :url, :json, :jwt, :hash, :enc)
|
|
18
|
+
- Comparison operators (≥, ≤, >, <)
|
|
19
|
+
- Range notation ([N,M])
|
|
20
|
+
- Set membership (∈{...})
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import re
|
|
24
|
+
from typing import Dict, Optional, Tuple
|
|
25
|
+
|
|
26
|
+
# Pattern shortcuts
|
|
27
|
+
PATTERN_SHORTCUTS: Dict[str, str] = {
|
|
28
|
+
':uuid': 'valid UUID',
|
|
29
|
+
':email': 'valid email format',
|
|
30
|
+
':url': 'valid URL',
|
|
31
|
+
':uri': 'valid URI',
|
|
32
|
+
':json': 'valid JSON',
|
|
33
|
+
':jwt': 'valid JWT token',
|
|
34
|
+
':hash': 'hashed value',
|
|
35
|
+
':enc': 'encrypted value',
|
|
36
|
+
':hex': 'hexadecimal value',
|
|
37
|
+
':b64': 'base64 encoded',
|
|
38
|
+
':iso': 'ISO format',
|
|
39
|
+
':rfc': 'RFC compliant',
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Reverse mapping
|
|
43
|
+
PATTERN_EXPANSIONS: Dict[str, str] = {v: k for k, v in PATTERN_SHORTCUTS.items()}
|
|
44
|
+
|
|
45
|
+
# Comparison operators (Unicode mathematical symbols)
|
|
46
|
+
COMPARISON_OPERATORS: Dict[str, str] = {
|
|
47
|
+
'≥': 'greater than or equal to',
|
|
48
|
+
'≤': 'less than or equal to',
|
|
49
|
+
'>': 'greater than',
|
|
50
|
+
'<': 'less than',
|
|
51
|
+
'=': 'equal to',
|
|
52
|
+
'≠': 'not equal to',
|
|
53
|
+
'≈': 'approximately equal to',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Reverse mapping
|
|
57
|
+
COMPARISON_EXPANSIONS: Dict[str, str] = {v: k for k, v in COMPARISON_OPERATORS.items()}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def compress_pattern(text: str) -> str:
|
|
61
|
+
"""
|
|
62
|
+
Compress text using pattern shortcuts.
|
|
63
|
+
|
|
64
|
+
Examples:
|
|
65
|
+
"valid UUID" → ":uuid"
|
|
66
|
+
"valid email format" → ":email"
|
|
67
|
+
"greater than or equal to 8" → "≥8"
|
|
68
|
+
"""
|
|
69
|
+
result = text
|
|
70
|
+
|
|
71
|
+
# Replace pattern expansions with shortcuts
|
|
72
|
+
for expansion, shortcut in PATTERN_EXPANSIONS.items():
|
|
73
|
+
result = result.replace(expansion, shortcut)
|
|
74
|
+
|
|
75
|
+
# Replace comparison operators
|
|
76
|
+
for expansion, operator in COMPARISON_EXPANSIONS.items():
|
|
77
|
+
# Handle "X greater than or equal to N" → "X≥N"
|
|
78
|
+
pattern = rf'(\w+)\s+{re.escape(expansion)}\s+(\d+)'
|
|
79
|
+
result = re.sub(pattern, rf'\1{operator}\2', result)
|
|
80
|
+
|
|
81
|
+
# Handle "greater than or equal to N" → "≥N"
|
|
82
|
+
pattern = rf'{re.escape(expansion)}\s+(\d+)'
|
|
83
|
+
result = re.sub(pattern, rf'{operator}\1', result)
|
|
84
|
+
|
|
85
|
+
return result
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def expand_pattern(text: str) -> str:
|
|
89
|
+
"""
|
|
90
|
+
Expand pattern shortcuts to full text.
|
|
91
|
+
|
|
92
|
+
Examples:
|
|
93
|
+
":uuid" → "valid UUID"
|
|
94
|
+
":email" → "valid email format"
|
|
95
|
+
"≥8" → "greater than or equal to 8"
|
|
96
|
+
"""
|
|
97
|
+
result = text
|
|
98
|
+
|
|
99
|
+
# Replace pattern shortcuts with expansions
|
|
100
|
+
for shortcut, expansion in PATTERN_SHORTCUTS.items():
|
|
101
|
+
result = result.replace(shortcut, expansion)
|
|
102
|
+
|
|
103
|
+
# Replace comparison operators
|
|
104
|
+
for operator, expansion in COMPARISON_OPERATORS.items():
|
|
105
|
+
# Handle "X≥N" → "X greater than or equal to N"
|
|
106
|
+
pattern = rf'(\w+){re.escape(operator)}(\d+)'
|
|
107
|
+
result = re.sub(pattern, rf'\1 {expansion} \2', result)
|
|
108
|
+
|
|
109
|
+
# Handle "≥N" → "greater than or equal to N"
|
|
110
|
+
pattern = rf'{re.escape(operator)}(\d+)'
|
|
111
|
+
result = re.sub(pattern, rf'{expansion} \1', result)
|
|
112
|
+
|
|
113
|
+
return result
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def parse_range(text: str) -> Optional[Tuple[int, int]]:
|
|
117
|
+
"""
|
|
118
|
+
Parse range notation [N,M].
|
|
119
|
+
|
|
120
|
+
Example:
|
|
121
|
+
"[18,65]" → (18, 65)
|
|
122
|
+
"""
|
|
123
|
+
match = re.match(r'\[(\d+),(\d+)\]', text)
|
|
124
|
+
if match:
|
|
125
|
+
return (int(match.group(1)), int(match.group(2)))
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def parse_set_membership(text: str) -> Optional[list]:
|
|
130
|
+
"""
|
|
131
|
+
Parse set membership ∈{...}.
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
"∈{active,pending,completed}" → ["active", "pending", "completed"]
|
|
135
|
+
"""
|
|
136
|
+
match = re.match(r'∈\{([^}]+)\}', text)
|
|
137
|
+
if match:
|
|
138
|
+
return [item.strip() for item in match.group(1).split(',')]
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def compress_range(min_val: int, max_val: int) -> str:
|
|
143
|
+
"""
|
|
144
|
+
Compress range to notation.
|
|
145
|
+
|
|
146
|
+
Example:
|
|
147
|
+
(18, 65) → "[18,65]"
|
|
148
|
+
"""
|
|
149
|
+
return f'[{min_val},{max_val}]'
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def compress_set_membership(items: list) -> str:
|
|
153
|
+
"""
|
|
154
|
+
Compress set membership to notation.
|
|
155
|
+
|
|
156
|
+
Example:
|
|
157
|
+
["active", "pending"] → "∈{active,pending}"
|
|
158
|
+
"""
|
|
159
|
+
return f'∈{{{",".join(items)}}}'
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def validate_pattern(pattern: str) -> Tuple[bool, Optional[str]]:
|
|
163
|
+
"""
|
|
164
|
+
Validate a pattern shortcut.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
(is_valid, error_message)
|
|
168
|
+
"""
|
|
169
|
+
# Check if it contains a type pattern (var:type format)
|
|
170
|
+
if ':' in pattern and not pattern.startswith(':'):
|
|
171
|
+
# Extract the pattern part after the colon
|
|
172
|
+
parts = pattern.split(':')
|
|
173
|
+
if len(parts) == 2:
|
|
174
|
+
pattern_type = ':' + parts[1]
|
|
175
|
+
if pattern_type not in PATTERN_SHORTCUTS:
|
|
176
|
+
similar = _find_similar_pattern(pattern_type)
|
|
177
|
+
if similar:
|
|
178
|
+
return (False, f"Unknown pattern '{pattern_type}'. Did you mean '{similar}'?")
|
|
179
|
+
return (False, f"Unknown pattern '{pattern_type}'. Available patterns: {', '.join(PATTERN_SHORTCUTS.keys())}")
|
|
180
|
+
|
|
181
|
+
# Check if it's a standalone pattern shortcut
|
|
182
|
+
if pattern.startswith(':'):
|
|
183
|
+
if pattern not in PATTERN_SHORTCUTS:
|
|
184
|
+
similar = _find_similar_pattern(pattern)
|
|
185
|
+
if similar:
|
|
186
|
+
return (False, f"Unknown pattern '{pattern}'. Did you mean '{similar}'?")
|
|
187
|
+
return (False, f"Unknown pattern '{pattern}'. Available patterns: {', '.join(PATTERN_SHORTCUTS.keys())}")
|
|
188
|
+
|
|
189
|
+
# Check if it's a comparison with number
|
|
190
|
+
for operator in COMPARISON_OPERATORS.keys():
|
|
191
|
+
if operator in pattern:
|
|
192
|
+
# Extract number after operator
|
|
193
|
+
match = re.search(rf'{re.escape(operator)}(\d+)', pattern)
|
|
194
|
+
if not match:
|
|
195
|
+
return (False, f"Invalid comparison '{pattern}'. Expected format: 'var{operator}N' where N is a number")
|
|
196
|
+
|
|
197
|
+
# Check if it's a range
|
|
198
|
+
if '[' in pattern:
|
|
199
|
+
range_val = parse_range(pattern)
|
|
200
|
+
if range_val is None:
|
|
201
|
+
return (False, f"Invalid range '{pattern}'. Expected format: '[N,M]' where N and M are numbers")
|
|
202
|
+
if range_val[0] >= range_val[1]:
|
|
203
|
+
return (False, f"Invalid range '{pattern}'. Minimum must be less than maximum")
|
|
204
|
+
|
|
205
|
+
# Check if it's set membership
|
|
206
|
+
if '∈' in pattern:
|
|
207
|
+
set_val = parse_set_membership(pattern)
|
|
208
|
+
if set_val is None:
|
|
209
|
+
return (False, f"Invalid set membership '{pattern}'. Expected format: '∈{{item1,item2,...}}'")
|
|
210
|
+
if len(set_val) == 0:
|
|
211
|
+
return (False, f"Invalid set membership '{pattern}'. Set must contain at least one item")
|
|
212
|
+
|
|
213
|
+
return (True, None)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _find_similar_pattern(pattern: str) -> Optional[str]:
|
|
217
|
+
"""Find similar pattern using simple edit distance."""
|
|
218
|
+
pattern_lower = pattern.lower()
|
|
219
|
+
|
|
220
|
+
for known_pattern in PATTERN_SHORTCUTS.keys():
|
|
221
|
+
# Simple similarity check
|
|
222
|
+
if len(pattern_lower) >= 3 and known_pattern.startswith(pattern_lower[:3]):
|
|
223
|
+
return known_pattern
|
|
224
|
+
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
# Common pattern combinations
|
|
229
|
+
COMMON_PATTERNS = {
|
|
230
|
+
'id': ':uuid',
|
|
231
|
+
'identifier': ':uuid',
|
|
232
|
+
'email': ':email',
|
|
233
|
+
'url': ':url',
|
|
234
|
+
'link': ':url',
|
|
235
|
+
'token': ':jwt',
|
|
236
|
+
'password': ':hash',
|
|
237
|
+
'data': ':json',
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def suggest_pattern(field_name: str) -> Optional[str]:
|
|
242
|
+
"""
|
|
243
|
+
Suggest a pattern based on field name.
|
|
244
|
+
|
|
245
|
+
Example:
|
|
246
|
+
"id" → ":uuid"
|
|
247
|
+
"email" → ":email"
|
|
248
|
+
"""
|
|
249
|
+
return COMMON_PATTERNS.get(field_name.lower())
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""@darkarts
|
|
2
|
+
⊢{darkarts:symbol-definitions}
|
|
3
|
+
∂{typing}
|
|
4
|
+
⚠{symbols≥15-types}
|
|
5
|
+
⊨{∀symbol→unique-meaning}
|
|
6
|
+
⚡{O(1):lookup}
|
|
7
|
+
🔒{read-only:symbols}
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
DarkArts v3.0.0 - Mathematical Symbol Definitions
|
|
12
|
+
|
|
13
|
+
This module defines all mathematical symbols used in DarkArts notation.
|
|
14
|
+
|
|
15
|
+
Symbol categories:
|
|
16
|
+
- Core symbols (v2.x): ⊢, ∂, ⚠, ⊳, ⊲, ⊨, ⚡, 🔒
|
|
17
|
+
- New symbols (v3.0.0): ⇄, ⊕, ⊗, ≈, ∴, ∀, ∃
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from typing import Dict, List
|
|
21
|
+
|
|
22
|
+
# Core Symbols (v2.x)
|
|
23
|
+
CORE_SYMBOLS: Dict[str, str] = {
|
|
24
|
+
'⊢': 'purpose',
|
|
25
|
+
'∂': 'dependencies',
|
|
26
|
+
'⚠': 'assumptions',
|
|
27
|
+
'⊳': 'preconditions',
|
|
28
|
+
'⊲': 'postconditions',
|
|
29
|
+
'⊨': 'invariants',
|
|
30
|
+
'⚡': 'complexity',
|
|
31
|
+
'🔒': 'security',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# New Symbols (v3.0.0)
|
|
35
|
+
NEW_SYMBOLS: Dict[str, str] = {
|
|
36
|
+
'⇄': 'bidirectional', # Bidirectional relationships
|
|
37
|
+
'⊕': 'side_effects', # Side effects
|
|
38
|
+
'⊗': 'forbidden', # Forbidden operations
|
|
39
|
+
'≈': 'approximation', # Approximation/tolerance
|
|
40
|
+
'∴': 'consequence', # Logical consequence
|
|
41
|
+
'∀': 'universal', # Universal quantifier
|
|
42
|
+
'∃': 'existential', # Existential quantifier
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# All Symbols
|
|
46
|
+
ALL_SYMBOLS: Dict[str, str] = {**CORE_SYMBOLS, **NEW_SYMBOLS}
|
|
47
|
+
|
|
48
|
+
# Reverse mapping (name to symbol)
|
|
49
|
+
SYMBOL_NAMES: Dict[str, str] = {v: k for k, v in ALL_SYMBOLS.items()}
|
|
50
|
+
|
|
51
|
+
# Symbol descriptions
|
|
52
|
+
SYMBOL_DESCRIPTIONS: Dict[str, str] = {
|
|
53
|
+
'⊢': 'Purpose - What the function/module does',
|
|
54
|
+
'∂': 'Dependencies - External packages/modules required',
|
|
55
|
+
'⚠': 'Assumptions - Conditions assumed to be true',
|
|
56
|
+
'⊳': 'Preconditions - Conditions that must be true before execution',
|
|
57
|
+
'⊲': 'Postconditions - Conditions guaranteed after execution',
|
|
58
|
+
'⊨': 'Invariants - Properties that remain unchanged',
|
|
59
|
+
'⚡': 'Complexity - Time/space complexity (Big O notation)',
|
|
60
|
+
'🔒': 'Security - Security considerations and protections',
|
|
61
|
+
'⇄': 'Bidirectional - Bidirectional relationships or inverse operations',
|
|
62
|
+
'⊕': 'Side Effects - Operations that modify external state',
|
|
63
|
+
'⊗': 'Forbidden - Operations that must NOT happen',
|
|
64
|
+
'≈': 'Approximation - Acceptable error margins or tolerances',
|
|
65
|
+
'∴': 'Consequence - Logical implications and consequences',
|
|
66
|
+
'∀': 'Universal - Properties that apply to all elements',
|
|
67
|
+
'∃': 'Existential - Properties that must exist',
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Symbol examples
|
|
71
|
+
SYMBOL_EXAMPLES: Dict[str, List[str]] = {
|
|
72
|
+
'⊢': [
|
|
73
|
+
'⊢{u auth svc}',
|
|
74
|
+
'⊢{calc total price}',
|
|
75
|
+
'⊢{fetch u data from db}',
|
|
76
|
+
],
|
|
77
|
+
'∂': [
|
|
78
|
+
'∂{bcrypt,jsonwebtoken,db}',
|
|
79
|
+
'∂{express,cors,helmet}',
|
|
80
|
+
'∂{react,redux,axios}',
|
|
81
|
+
],
|
|
82
|
+
'⚠': [
|
|
83
|
+
'⚠{u exists in db}',
|
|
84
|
+
'⚠{db conn available}',
|
|
85
|
+
'⚠{valid sess}',
|
|
86
|
+
],
|
|
87
|
+
'⊳': [
|
|
88
|
+
'⊳{id:uuid,pw≥8}',
|
|
89
|
+
'⊳{email:email,age≥18}',
|
|
90
|
+
'⊳{amt>0,currency∈{USD,EUR}}',
|
|
91
|
+
],
|
|
92
|
+
'⊲': [
|
|
93
|
+
'⊲{ret JWT tok|null}',
|
|
94
|
+
'⊲{ret u obj|err}',
|
|
95
|
+
'⊲{ret bool}',
|
|
96
|
+
],
|
|
97
|
+
'⊨': [
|
|
98
|
+
'⊨{no db mod}',
|
|
99
|
+
'⊨{no state change}',
|
|
100
|
+
'⊨{idempotent}',
|
|
101
|
+
],
|
|
102
|
+
'⚡': [
|
|
103
|
+
'⚡{O(1)}',
|
|
104
|
+
'⚡{O(n)}',
|
|
105
|
+
'⚡{O(log n)}',
|
|
106
|
+
],
|
|
107
|
+
'🔒': [
|
|
108
|
+
'🔒{pw:hash,tok:enc}',
|
|
109
|
+
'🔒{rate limit:100/min}',
|
|
110
|
+
'🔒{auth required}',
|
|
111
|
+
],
|
|
112
|
+
'⇄': [
|
|
113
|
+
'⇄{enc↔dec}',
|
|
114
|
+
'⇄{ser↔deser}',
|
|
115
|
+
'⇄{compress↔decompress}',
|
|
116
|
+
],
|
|
117
|
+
'⊕': [
|
|
118
|
+
'⊕{writes db}',
|
|
119
|
+
'⊕{sends email}',
|
|
120
|
+
'⊕{logs to file}',
|
|
121
|
+
],
|
|
122
|
+
'⊗': [
|
|
123
|
+
'⊗{no pw log}',
|
|
124
|
+
'⊗{no network calls}',
|
|
125
|
+
'⊗{no db writes}',
|
|
126
|
+
],
|
|
127
|
+
'≈': [
|
|
128
|
+
'≈{±0.001}',
|
|
129
|
+
'≈{~99.9% accuracy}',
|
|
130
|
+
'≈{<1ms latency}',
|
|
131
|
+
],
|
|
132
|
+
'∴': [
|
|
133
|
+
'∴{if auth fails→ret null}',
|
|
134
|
+
'∴{if cache miss→fetch db}',
|
|
135
|
+
'∴{if invalid→throw err}',
|
|
136
|
+
],
|
|
137
|
+
'∀': [
|
|
138
|
+
'∀{users have email}',
|
|
139
|
+
'∀{tokens expire 1h}',
|
|
140
|
+
'∀{pw≥8}',
|
|
141
|
+
],
|
|
142
|
+
'∃': [
|
|
143
|
+
'∃{admin required}',
|
|
144
|
+
'∃{valid sess}',
|
|
145
|
+
'∃{active conn}',
|
|
146
|
+
],
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# Symbol categories
|
|
150
|
+
SYMBOL_CATEGORIES: Dict[str, List[str]] = {
|
|
151
|
+
'core': list(CORE_SYMBOLS.keys()),
|
|
152
|
+
'new': list(NEW_SYMBOLS.keys()),
|
|
153
|
+
'required': ['⊢'], # Only purpose is truly required
|
|
154
|
+
'optional': [s for s in ALL_SYMBOLS.keys() if s != '⊢'],
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def get_symbol_name(symbol: str) -> str:
|
|
159
|
+
"""Get the name of a symbol."""
|
|
160
|
+
return ALL_SYMBOLS.get(symbol, 'unknown')
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_symbol_description(symbol: str) -> str:
|
|
164
|
+
"""Get the description of a symbol."""
|
|
165
|
+
return SYMBOL_DESCRIPTIONS.get(symbol, 'Unknown symbol')
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_symbol_examples(symbol: str) -> List[str]:
|
|
169
|
+
"""Get examples for a symbol."""
|
|
170
|
+
return SYMBOL_EXAMPLES.get(symbol, [])
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def is_valid_symbol(symbol: str) -> bool:
|
|
174
|
+
"""Check if a symbol is valid."""
|
|
175
|
+
return symbol in ALL_SYMBOLS
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def get_symbol_by_name(name: str) -> str:
|
|
179
|
+
"""Get symbol by its name."""
|
|
180
|
+
return SYMBOL_NAMES.get(name, '')
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def list_symbols(category: str = 'all') -> List[str]:
|
|
184
|
+
"""
|
|
185
|
+
List symbols by category.
|
|
186
|
+
|
|
187
|
+
Categories: 'all', 'core', 'new', 'required', 'optional'
|
|
188
|
+
"""
|
|
189
|
+
if category == 'all':
|
|
190
|
+
return list(ALL_SYMBOLS.keys())
|
|
191
|
+
return SYMBOL_CATEGORIES.get(category, [])
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# Symbol validation rules
|
|
195
|
+
SYMBOL_RULES: Dict[str, Dict] = {
|
|
196
|
+
'⊢': {
|
|
197
|
+
'required': True,
|
|
198
|
+
'multiple': False,
|
|
199
|
+
'format': 'Single sentence describing purpose',
|
|
200
|
+
},
|
|
201
|
+
'∂': {
|
|
202
|
+
'required': False,
|
|
203
|
+
'multiple': False,
|
|
204
|
+
'format': 'Comma-separated list of dependencies',
|
|
205
|
+
},
|
|
206
|
+
'⚠': {
|
|
207
|
+
'required': False,
|
|
208
|
+
'multiple': True,
|
|
209
|
+
'format': 'Comma-separated list of assumptions',
|
|
210
|
+
},
|
|
211
|
+
'⊳': {
|
|
212
|
+
'required': False,
|
|
213
|
+
'multiple': True,
|
|
214
|
+
'format': 'Comma-separated list of preconditions',
|
|
215
|
+
},
|
|
216
|
+
'⊲': {
|
|
217
|
+
'required': False,
|
|
218
|
+
'multiple': True,
|
|
219
|
+
'format': 'Comma-separated list of postconditions',
|
|
220
|
+
},
|
|
221
|
+
'⊨': {
|
|
222
|
+
'required': False,
|
|
223
|
+
'multiple': True,
|
|
224
|
+
'format': 'Comma-separated list of invariants',
|
|
225
|
+
},
|
|
226
|
+
'⚡': {
|
|
227
|
+
'required': False,
|
|
228
|
+
'multiple': False,
|
|
229
|
+
'format': 'Big O notation (e.g., O(1), O(n))',
|
|
230
|
+
},
|
|
231
|
+
'🔒': {
|
|
232
|
+
'required': False,
|
|
233
|
+
'multiple': True,
|
|
234
|
+
'format': 'Comma-separated list of security measures',
|
|
235
|
+
},
|
|
236
|
+
'⇄': {
|
|
237
|
+
'required': False,
|
|
238
|
+
'multiple': True,
|
|
239
|
+
'format': 'Bidirectional relationships (e.g., enc↔dec)',
|
|
240
|
+
},
|
|
241
|
+
'⊕': {
|
|
242
|
+
'required': False,
|
|
243
|
+
'multiple': True,
|
|
244
|
+
'format': 'Comma-separated list of side effects',
|
|
245
|
+
},
|
|
246
|
+
'⊗': {
|
|
247
|
+
'required': False,
|
|
248
|
+
'multiple': True,
|
|
249
|
+
'format': 'Comma-separated list of forbidden operations',
|
|
250
|
+
},
|
|
251
|
+
'≈': {
|
|
252
|
+
'required': False,
|
|
253
|
+
'multiple': True,
|
|
254
|
+
'format': 'Approximation or tolerance (e.g., ±0.001)',
|
|
255
|
+
},
|
|
256
|
+
'∴': {
|
|
257
|
+
'required': False,
|
|
258
|
+
'multiple': True,
|
|
259
|
+
'format': 'Logical implications (e.g., if X→Y)',
|
|
260
|
+
},
|
|
261
|
+
'∀': {
|
|
262
|
+
'required': False,
|
|
263
|
+
'multiple': True,
|
|
264
|
+
'format': 'Universal properties (e.g., all users have email)',
|
|
265
|
+
},
|
|
266
|
+
'∃': {
|
|
267
|
+
'required': False,
|
|
268
|
+
'multiple': True,
|
|
269
|
+
'format': 'Existential properties (e.g., admin required)',
|
|
270
|
+
},
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def get_symbol_rules(symbol: str) -> Dict:
|
|
275
|
+
"""Get validation rules for a symbol."""
|
|
276
|
+
return SYMBOL_RULES.get(symbol, {})
|
|
@@ -20,6 +20,7 @@ Translates @voodocs annotations (DarkArts language) into human-readable document
|
|
|
20
20
|
from typing import List, Optional, Dict
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
import re
|
|
23
|
+
import sys
|
|
23
24
|
|
|
24
25
|
from darkarts.annotations.types import (
|
|
25
26
|
ParsedAnnotations,
|
|
@@ -29,6 +30,10 @@ from darkarts.annotations.types import (
|
|
|
29
30
|
ComplexityAnnotation,
|
|
30
31
|
)
|
|
31
32
|
|
|
33
|
+
# Import abbreviation expansion for v3.0.0
|
|
34
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
35
|
+
from darkarts.companion_files_expansion import expand_abbreviations, expand_pattern_shortcuts
|
|
36
|
+
|
|
32
37
|
|
|
33
38
|
class DocumentationGenerator:
|
|
34
39
|
"""Generates human-readable documentation from @voodocs annotations."""
|
|
@@ -41,8 +46,24 @@ class DocumentationGenerator:
|
|
|
41
46
|
"""Initialize mathematical symbol translation tables."""
|
|
42
47
|
# Mathematical symbols to natural language
|
|
43
48
|
self.math_symbols = {
|
|
49
|
+
# Core DarkArts symbols
|
|
50
|
+
'⊢': 'purpose',
|
|
51
|
+
'∂': 'dependencies',
|
|
52
|
+
'⊳': 'preconditions',
|
|
53
|
+
'⊲': 'postconditions',
|
|
54
|
+
'⊨': 'invariants',
|
|
55
|
+
'⚠': 'assumptions',
|
|
56
|
+
'⚡': 'complexity',
|
|
57
|
+
'🔒': 'security',
|
|
58
|
+
# v3.0.0 new symbols
|
|
59
|
+
'⇄': 'bidirectional relationship',
|
|
60
|
+
'⊕': 'side effects',
|
|
61
|
+
'⊗': 'forbidden operations',
|
|
62
|
+
'≈': 'approximately',
|
|
63
|
+
'∴': 'therefore',
|
|
44
64
|
'∀': 'for all',
|
|
45
65
|
'∃': 'there exists',
|
|
66
|
+
# Standard mathematical symbols
|
|
46
67
|
'∈': 'in',
|
|
47
68
|
'∉': 'not in',
|
|
48
69
|
'⊆': 'subset of',
|
|
@@ -53,12 +74,13 @@ class DocumentationGenerator:
|
|
|
53
74
|
'≥': 'greater than or equal to',
|
|
54
75
|
'≤': 'less than or equal to',
|
|
55
76
|
'≠': 'not equal to',
|
|
56
|
-
'≈': 'approximately equal to',
|
|
57
77
|
'⟹': 'implies',
|
|
58
78
|
'⟺': 'if and only if',
|
|
59
79
|
'⇒': 'implies',
|
|
60
80
|
'⇔': 'if and only if',
|
|
61
81
|
'→': 'leads to',
|
|
82
|
+
'←': 'comes from',
|
|
83
|
+
'↔': 'corresponds to',
|
|
62
84
|
'∧': 'and',
|
|
63
85
|
'∨': 'or',
|
|
64
86
|
'¬': 'not',
|
|
@@ -303,10 +325,17 @@ class DocumentationGenerator:
|
|
|
303
325
|
"""
|
|
304
326
|
Translate mathematical notation to natural language.
|
|
305
327
|
|
|
306
|
-
This method converts DarkArts mathematical expressions into readable English
|
|
328
|
+
This method converts DarkArts mathematical expressions into readable English,
|
|
329
|
+
including v3.0.0 abbreviation expansion.
|
|
307
330
|
"""
|
|
308
331
|
text = expression.strip()
|
|
309
332
|
|
|
333
|
+
# Expand v3.0.0 abbreviations first (e.g., "u auth svc" -> "user authentication service")
|
|
334
|
+
text = expand_abbreviations(text)
|
|
335
|
+
|
|
336
|
+
# Expand pattern shortcuts (e.g., ":uuid" -> "must be valid UUID")
|
|
337
|
+
text = expand_pattern_shortcuts(text)
|
|
338
|
+
|
|
310
339
|
# Handle quantifiers (∀, ∃) specially
|
|
311
340
|
text = self._translate_quantifiers(text)
|
|
312
341
|
|