"""Backend-agnostic traversal of md2gost inline AST nodes.""" from __future__ import annotations from abc import ABC, abstractmethod #: Wording used when a `@Тип:id` reference is spelled out, per ГОСТ 7.32. REFERENCE_FULL_NAMES = { "рисунок": "Рисунок", "таблица": "Таблица", "листинг": "Листинг", "формула": "формула", } def reference_full_name(ref_type: str) -> str: return REFERENCE_FULL_NAMES.get(ref_type.lower(), ref_type) class InlineRenderer(ABC): """ Formatting hooks for :func:`render_inline`. Defaults are provided only for the nodes every backend renders the same way; everything else is abstract so a new backend cannot silently drop a node type. """ @abstractmethod def escape(self, text: str) -> str: ... @abstractmethod def code_span(self, node) -> str: ... @abstractmethod def emphasis(self, inner: str) -> str: ... @abstractmethod def strong_emphasis(self, inner: str) -> str: ... @abstractmethod def link(self, node, inner: str) -> str: ... @abstractmethod def reference(self, node) -> str: ... @abstractmethod def inline_equation(self, node) -> str: ... @abstractmethod def line_break(self) -> str: ... def raw_text(self, text: str) -> str: return self.escape(text) def image(self, node) -> str: return "" # images carry a caption, so they are emitted at block level def unknown(self, node) -> str: return self.escape(str(node)) def render_inline(children, renderer: InlineRenderer) -> str: """Render a list of inline nodes into a backend-specific string.""" parts: list[str] = [] for child in children or []: node_type = type(child).__name__ if node_type in ("RawText", "Literal"): parts.append(renderer.raw_text(getattr(child, "children", "") or "")) elif node_type == "CodeSpan": parts.append(renderer.code_span(child)) elif node_type == "Emphasis": parts.append(renderer.emphasis(render_inline(child.children, renderer))) elif node_type == "StrongEmphasis": parts.append(renderer.strong_emphasis(render_inline(child.children, renderer))) elif node_type == "Link": parts.append(renderer.link(child, render_inline(child.children, renderer))) elif node_type == "Reference": parts.append(renderer.reference(child)) elif node_type == "InlineEquation": parts.append(renderer.inline_equation(child)) elif node_type == "Image": parts.append(renderer.image(child)) elif node_type == "LineBreak": parts.append(renderer.line_break()) elif hasattr(child, "children"): parts.append(render_inline(child.children, renderer)) else: parts.append(renderer.unknown(child)) return "".join(parts)