""" Base interfaces for the refactored architecture. This module defines the abstract base classes (interfaces) that establish the contracts for all components in the refactored converter architecture. """ from abc import ABC, abstractmethod from typing import Any, Optional, List from lxml import etree as ET class ConversionContext: """ Shared context object that flows through the conversion pipeline. This context holds all state and intermediate results during the conversion process, allowing stages to communicate and share data. """ pass # Will be implemented as dataclass in context.py class Stage(ABC): """ Abstract base class for pipeline stages. Each stage represents a distinct step in the conversion pipeline (e.g., input parsing, validation, transformation, conversion, output). """ @abstractmethod def process(self, context: ConversionContext) -> ConversionContext: """ Process the conversion context and return updated context. Args: context: The current conversion context with all state Returns: Updated conversion context with stage results Raises: ValueError: If stage preconditions are not met RuntimeError: If stage processing fails """ pass @abstractmethod def validate_input(self, context: ConversionContext) -> bool: """ Validate that stage preconditions are met. Args: context: The current conversion context Returns: True if preconditions are met, False otherwise """ pass @property @abstractmethod def name(self) -> str: """Get the human-readable name of this stage.""" pass class Visitor(ABC): """ Abstract base class for MathML element visitors. Each visitor handles conversion of a specific MathML element type (e.g., , , , ) to LaTeX format. """ def __init__(self, strategy: 'Strategy'): """ Initialize visitor with a conversion strategy. Args: strategy: The subject-specific formatting strategy to use """ self.strategy = strategy self.context: Optional[ConversionContext] = None @abstractmethod def visit(self, node: ET.Element) -> str: """ Convert MathML element to LaTeX string. Args: node: The MathML element to convert Returns: LaTeX string representation of the element Raises: ValueError: If node is not the expected element type """ pass @abstractmethod def can_handle(self, node: ET.Element) -> bool: """ Check if this visitor can handle the given node. Args: node: The MathML element to check Returns: True if this visitor can convert the node, False otherwise """ pass @property @abstractmethod def element_name(self) -> str: """Get the MathML element name this visitor handles (e.g., 'mi', 'mo').""" pass class Strategy(ABC): """ Abstract base class for subject-specific formatting strategies. Strategies encapsulate the subject-specific logic for formatting mathematical notation (e.g., math, chemistry, physics, statistics). """ @abstractmethod def format_identifier(self, text: str, node: ET.Element) -> str: """ Format an identifier ( element) according to subject conventions. Args: text: The identifier text content node: The element node (for accessing attributes) Returns: Formatted LaTeX string for the identifier Examples: Math: "sin" → "\\sin" (function recognition) Chemistry: "Fe" → "\\mathrm{Fe}" (element formatting) """ pass @abstractmethod def format_operator(self, text: str, node: ET.Element) -> str: """ Format an operator ( element) according to subject conventions. Args: text: The operator text content node: The element node Returns: Formatted LaTeX string for the operator Examples: Math: "+" → "+" Chemistry: "→" → "\\rightarrow" """ pass @abstractmethod def format_number(self, text: str, node: ET.Element) -> str: """ Format a number ( element) according to subject conventions. Args: text: The number text content node: The element node Returns: Formatted LaTeX string for the number """ pass @abstractmethod def detect_context(self, equation_text: str) -> bool: """ Detect if an equation belongs to this subject area. Args: equation_text: The full equation text to analyze Returns: True if equation likely belongs to this subject, False otherwise """ pass @property @abstractmethod def name(self) -> str: """Get the strategy name (e.g., 'math', 'chemistry', 'physics').""" pass class VisitorRegistry(ABC): """ Abstract base class for visitor registry/factory. The registry maintains a mapping of MathML element names to their corresponding visitor classes and provides visitor instances on demand. """ @abstractmethod def register(self, element_name: str, visitor_class: type) -> None: """ Register a visitor class for a specific element type. Args: element_name: MathML element name (e.g., 'mi', 'mo') visitor_class: The visitor class to handle this element """ pass @abstractmethod def get_visitor(self, element_name: str, strategy: Strategy) -> Visitor: """ Get a visitor instance for the given element type. Args: element_name: MathML element name strategy: The conversion strategy to use Returns: Visitor instance configured with the strategy Raises: ValueError: If no visitor registered for element_name """ pass @abstractmethod def has_visitor(self, element_name: str) -> bool: """ Check if a visitor is registered for the element type. Args: element_name: MathML element name Returns: True if visitor is registered, False otherwise """ pass class Parser(ABC): """ Abstract base class for input parsers. Parsers handle different input formats (DOCX, OMML, MathML) and convert them to a unified format (MathML strings) for the conversion pipeline. """ @abstractmethod def parse(self, source: str) -> List[str]: """ Parse input and return list of MathML strings. Args: source: Input source (file path, XML string, etc.) Returns: List of MathML equation strings Raises: ValueError: If input format is invalid FileNotFoundError: If source file doesn't exist (for file-based parsers) RuntimeError: If parsing fails """ pass @abstractmethod def can_handle(self, source: str) -> bool: """ Check if this parser can handle the given input source. Args: source: Input source to check Returns: True if this parser can handle the source, False otherwise """ pass @property @abstractmethod def parser_type(self) -> str: """ Get the parser type identifier. Returns: Parser type string (e.g., "docx", "omml", "mathml") """ pass