111 lines
3.2 KiB
Python
111 lines
3.2 KiB
Python
"""Base parser interface and data structures."""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from dataclasses import dataclass, field
|
|
from typing import Optional
|
|
from enum import Enum
|
|
|
|
|
|
class ElementType(Enum):
|
|
"""Types of documentation elements."""
|
|
|
|
FUNCTION = "function"
|
|
CLASS = "class"
|
|
METHOD = "method"
|
|
INTERFACE = "interface"
|
|
STRUCT = "struct"
|
|
MODULE = "module"
|
|
CONSTANT = "constant"
|
|
VARIABLE = "variable"
|
|
|
|
|
|
@dataclass
|
|
class Parameter:
|
|
"""Represents a function/method parameter."""
|
|
|
|
name: str
|
|
type_hint: Optional[str] = None
|
|
description: Optional[str] = None
|
|
default_value: Optional[str] = None
|
|
is_optional: bool = False
|
|
|
|
def __str__(self) -> str:
|
|
parts = [self.name]
|
|
if self.type_hint:
|
|
parts.append(f": {self.type_hint}")
|
|
if self.default_value:
|
|
parts.append(f" = {self.default_value}")
|
|
return "".join(parts)
|
|
|
|
|
|
@dataclass
|
|
class DocElement:
|
|
"""Represents a documentation element extracted from source code."""
|
|
|
|
name: str
|
|
element_type: ElementType
|
|
description: str = ""
|
|
full_docstring: str = ""
|
|
parameters: list[Parameter] = field(default_factory=list)
|
|
return_type: Optional[str] = None
|
|
return_description: Optional[str] = None
|
|
raises: list[tuple[str, str]] = field(default_factory=list)
|
|
examples: list[str] = field(default_factory=list)
|
|
source_file: str = ""
|
|
line_number: int = 0
|
|
visibility: str = "public"
|
|
decorators: list[str] = field(default_factory=list)
|
|
attributes: list[tuple[str, Optional[str], Optional[str]]] = field(
|
|
default_factory=list
|
|
)
|
|
imports: list[str] = field(default_factory=list)
|
|
|
|
|
|
class Parser(ABC):
|
|
"""Abstract base class for code parsers."""
|
|
|
|
def __init__(self, file_path: str):
|
|
self.file_path = file_path
|
|
self.content: str = ""
|
|
self.elements: list[DocElement] = []
|
|
|
|
@abstractmethod
|
|
def parse(self) -> list[DocElement]:
|
|
"""Parse the file and extract documentation elements."""
|
|
pass
|
|
|
|
@abstractmethod
|
|
def get_language_name(self) -> str:
|
|
"""Return the name of the language this parser handles."""
|
|
pass
|
|
|
|
@classmethod
|
|
@abstractmethod
|
|
def supports_file(cls, file_path: str) -> bool:
|
|
"""Check if this parser supports the given file."""
|
|
pass
|
|
|
|
def _read_content(self) -> str:
|
|
"""Read file content safely."""
|
|
with open(self.file_path, "r", encoding="utf-8") as f:
|
|
return f.read()
|
|
|
|
def _strip_docstring_quotes(self, docstring: str) -> str:
|
|
"""Remove leading/trailing quotes from docstring."""
|
|
if not docstring:
|
|
return ""
|
|
lines = docstring.split("\n")
|
|
if len(lines) == 1:
|
|
return docstring.strip('"""').strip("'''").strip()
|
|
first_line = lines[0].strip('"""').strip("'''").strip()
|
|
last_line = lines[-1].strip('"""').strip("'''").strip()
|
|
if len(lines) > 1:
|
|
middle_lines = lines[1:-1]
|
|
cleaned = []
|
|
for line in middle_lines:
|
|
stripped = line.strip()
|
|
if stripped:
|
|
cleaned.append(stripped)
|
|
return "\n".join([first_line] + cleaned + [last_line]) if cleaned else first_line
|
|
return first_line
|