Skip to content

Commit 1657cfe

Browse files
Create parser.py
1 parent 0abdaba commit 1657cfe

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed

sphinx_autodoc_vyper/parser.py

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
"""Parser for Vyper smart contracts."""
2+
3+
import os
4+
import re
5+
from dataclasses import dataclass
6+
from typing import List, Optional
7+
8+
9+
@dataclass
10+
class Parameter:
11+
"""Function parameter representation."""
12+
name: str
13+
type: str
14+
15+
16+
@dataclass
17+
class Function:
18+
"""Vyper function representation."""
19+
name: str
20+
params: List[Parameter]
21+
return_type: Optional[str]
22+
docstring: Optional[str]
23+
24+
25+
@dataclass
26+
class Contract:
27+
"""Vyper contract representation."""
28+
name: str
29+
path: str
30+
docstring: Optional[str]
31+
functions: List[Function]
32+
33+
34+
class VyperParser:
35+
"""Parser for Vyper smart contracts."""
36+
37+
def __init__(self, contracts_dir: str):
38+
self.contracts_dir = contracts_dir
39+
40+
def parse_contracts(self) -> List[Contract]:
41+
"""Parse all Vyper contracts in the directory."""
42+
contracts = []
43+
for root, _, files in os.walk(self.contracts_dir):
44+
for file in files:
45+
if file.endswith('.vy'):
46+
file_path = os.path.join(root, file)
47+
contract = self._parse_contract(file_path)
48+
contracts.append(contract)
49+
return contracts
50+
51+
def _parse_contract(self, file_path: str) -> Contract:
52+
"""Parse a single Vyper contract file."""
53+
with open(file_path, 'r', encoding='utf-8') as f:
54+
content = f.read()
55+
56+
name = os.path.basename(file_path).replace('.vy', '')
57+
rel_path = os.path.relpath(file_path, self.contracts_dir)
58+
59+
# Extract contract docstring
60+
docstring = self._extract_contract_docstring(content)
61+
62+
# Extract functions
63+
functions = self._extract_functions(content)
64+
65+
return Contract(name=name, path=rel_path, docstring=docstring, functions=functions)
66+
67+
def _extract_contract_docstring(self, content: str) -> Optional[str]:
68+
"""Extract the contract's main docstring."""
69+
match = re.search(r'^"""(.*?)"""', content, re.DOTALL | re.MULTILINE)
70+
return match.group(1).strip() if match else None
71+
72+
def _extract_functions(self, content: str) -> List[Function]:
73+
"""Extract all functions from the contract."""
74+
functions = []
75+
function_pattern = r'@external\s+def\s+([^(]+)\(([^)]*)\)(\s*->\s*[^:]+)?:\s*("""[\s\S]*?""")?'
76+
77+
for match in re.finditer(function_pattern, content):
78+
name = match.group(1).strip()
79+
params_str = match.group(2).strip()
80+
return_type = match.group(3).replace('->', '').strip() if match.group(3) else None
81+
docstring = match.group(4)[3:-3].strip() if match.group(4) else None
82+
83+
params = self._parse_params(params_str)
84+
functions.append(Function(name=name, params=params, return_type=return_type, docstring=docstring))
85+
86+
return functions
87+
88+
def _parse_params(self, params_str: str) -> List[Parameter]:
89+
"""Parse function parameters."""
90+
if not params_str:
91+
return []
92+
93+
params = []
94+
for param in params_str.split(','):
95+
if ':' in param:
96+
name, type_str = param.split(':')
97+
params.append(Parameter(name=name.strip(), type=type_str.strip()))
98+
return params

0 commit comments

Comments
 (0)