@voodocs/cli 2.5.3 → 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.
@@ -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__ = "2.5.3"
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 symbolic @darkarts annotations}
639
+ ⊢{Example TypeScript module demonstrating DarkArts v3.0.0 annotations}
640
640
  ∂{}
641
- ⚠{TypeScript environment available}
641
+ ⚠{TypeScript env available}
642
642
  */
643
643
 
644
644
  /**@darkarts
645
- {
646
- name must be a non-empty string
647
- age must be a positive number
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,symbols}
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²)|n=annotation-length,4-loops,depth=2}
7
+ ⚡{O(n)|n=annotation-length}
8
8
 
9
- DarkArts Annotation Parser
9
+ DarkArts Annotation Parser (v3.0.0 Wrapper)
10
10
 
11
- Parses @darkarts symbolic annotations from source code.
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
- module: Optional[str] = None # From ⊢
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: 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 ⚙️
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
- 'objectives': self.objectives,
46
- 'configuration': self.configuration,
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
- Extracts and parses @darkarts annotations from source code.
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
- '⊢': 'module',
125
+ '⊢': 'purpose',
63
126
  '∂': 'dependencies',
64
127
  '⚠': 'assumptions',
128
+ '⊳': 'preconditions',
129
+ '⊲': 'postconditions',
65
130
  '⊨': 'invariants',
131
+ '⚡': 'complexity',
66
132
  '🔒': 'security',
67
- '': 'performance',
68
- '📊': 'complexity',
69
- '🎯': 'objectives',
70
- '⚙️': 'configuration',
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.meta_symbols = get_meta_symbols()
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
- annotations = []
160
+ # Use v3.0.0 parser
161
+ v3_annotations = self.v3_parser.parse(source_code)
88
162
 
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)
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
- 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
181
+ # Wrap in @darkarts marker for v3 parser
182
+ source = f'"""@darkarts\n{text}\n"""'
152
183
 
153
- elif field_name == 'performance':
154
- annotation.performance = content
184
+ # Parse with v3.0.0 parser
185
+ v3_annotations = self.v3_parser.parse(source)
155
186
 
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
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 _parse_set(self, content: str) -> List[str]:
193
+ def extract_field(self, text: str, symbol: str) -> Optional[str]:
166
194
  """
167
- Parse a set notation {a,b,c} into a list.
195
+ Extract a specific field from annotation text.
168
196
 
169
197
  Args:
170
- content: Content like "{a,b,c}" or "a,b,c"
198
+ text: Annotation text
199
+ symbol: Symbol to extract (e.g., '⊢', '∂')
171
200
 
172
201
  Returns:
173
- List of items
202
+ Extracted field value or None
174
203
  """
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
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 parse_file(self, file_path: str) -> List[DarkArtsAnnotation]:
217
+ def validate(self, source_code: str) -> List[str]:
211
218
  """
212
- Parse @darkarts annotations from a file.
219
+ Validate @darkarts annotations in source code.
213
220
 
214
221
  Args:
215
- file_path: Path to source file
222
+ source_code: Source code containing @darkarts annotations
216
223
 
217
224
  Returns:
218
- List of parsed annotations
225
+ List of validation errors
219
226
  """
220
- with open(file_path, 'r', encoding='utf-8') as f:
221
- source_code = f.read()
227
+ # Use v3.0.0 parser with validation enabled
228
+ parser = DarkArtsParserV3(enable_validation=True)
229
+ annotations = parser.parse(source_code)
222
230
 
223
- return self.parse(source_code)
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)