99 lines
3.0 KiB
Python
99 lines
3.0 KiB
Python
"""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)
|