Skip to content

Index

backends

Per-language AST backends and their suffix-keyed registry.

LanguageBackend is the interface; registry dispatches a file to its backend by extension. Python is built in; TypeScript/Svelte are optional.

LanguageBackend

Bases: Protocol

A language-specific parsing + symbol-extraction backend.

Implementations live in core/backends/<language>.py and register themselves in core/registry.py keyed by file suffix. The four members map exactly to the four points where the language matters:

  • :attr:suffixes — which file extensions this backend owns.
  • :meth:parse_source — grammar (tree-sitter Language/Parser).
  • :meth:extract_module — node-name mapping into :class:ModuleInfo.
  • (import resolution and call extraction are handled by the analyzer/ callers layers, which dispatch to the backend that owns each file.)

Everything else — ranking, impact, dead-code, traversal, the MCP tools — is language-agnostic and consumes the returned :class:ModuleInfo.

Source code in packages/axm-ast/src/axm_ast/core/backends/base.py
Python
@runtime_checkable
class LanguageBackend(Protocol):
    """A language-specific parsing + symbol-extraction backend.

    Implementations live in ``core/backends/<language>.py`` and register
    themselves in ``core/registry.py`` keyed by file suffix. The four members
    map exactly to the four points where the language matters:

    * :attr:`suffixes` — which file extensions this backend owns.
    * :meth:`parse_source` — grammar (tree-sitter ``Language``/``Parser``).
    * :meth:`extract_module` — node-name mapping into :class:`ModuleInfo`.
    * (import resolution and call extraction are handled by the analyzer/
      callers layers, which dispatch to the backend that owns each file.)

    Everything else — ranking, impact, dead-code, traversal, the MCP tools —
    is language-agnostic and consumes the returned :class:`ModuleInfo`.
    """

    @property
    def name(self) -> str:
        """Human-readable language name (e.g. ``"python"``, ``"typescript"``)."""
        ...

    @property
    def suffixes(self) -> tuple[str, ...]:
        """File extensions this backend owns (e.g. ``(".py",)``)."""
        ...

    def parse_source(self, source: str) -> Tree:
        """Parse *source* into a tree-sitter ``Tree`` with this grammar."""
        ...

    def parse_file(self, path: Path) -> Tree:
        """Parse the file at *path* into a tree-sitter ``Tree``.

        Implementations may cache by (path, mtime); they must raise
        ``FileNotFoundError`` for a missing file.
        """
        ...

    def extract_module(self, path: Path) -> ModuleInfo:
        """Extract the full :class:`ModuleInfo` (symbols, imports, docstring).

        This is the only language-aware step: it walks the grammar's concrete
        syntax tree, mapping language-specific node types into the shared,
        language-agnostic symbol model.
        """
        ...

    def extract_calls(
        self, module: ModuleInfo, module_name: str | None = None
    ) -> list[CallSite]:
        """Extract every call-site from *module* into the shared model.

        Walks the grammar's call nodes (``call`` in Python, ``call_expression``
        in TypeScript) and records each :class:`CallSite` (symbol, location,
        enclosing scope). The returned model is language-agnostic, so the
        downstream call-graph / impact / dead-code layers are unchanged.

        Args:
            module: A parsed module (carries the source path).
            module_name: Dotted module name for ``CallSite.module`` (defaults to
                the file stem).
        """
        ...
name property

Human-readable language name (e.g. "python", "typescript").

suffixes property

File extensions this backend owns (e.g. (".py",)).

extract_calls(module, module_name=None)

Extract every call-site from module into the shared model.

Walks the grammar's call nodes (call in Python, call_expression in TypeScript) and records each :class:CallSite (symbol, location, enclosing scope). The returned model is language-agnostic, so the downstream call-graph / impact / dead-code layers are unchanged.

Parameters:

Name Type Description Default
module ModuleInfo

A parsed module (carries the source path).

required
module_name str | None

Dotted module name for CallSite.module (defaults to the file stem).

None
Source code in packages/axm-ast/src/axm_ast/core/backends/base.py
Python
def extract_calls(
    self, module: ModuleInfo, module_name: str | None = None
) -> list[CallSite]:
    """Extract every call-site from *module* into the shared model.

    Walks the grammar's call nodes (``call`` in Python, ``call_expression``
    in TypeScript) and records each :class:`CallSite` (symbol, location,
    enclosing scope). The returned model is language-agnostic, so the
    downstream call-graph / impact / dead-code layers are unchanged.

    Args:
        module: A parsed module (carries the source path).
        module_name: Dotted module name for ``CallSite.module`` (defaults to
            the file stem).
    """
    ...
extract_module(path)

Extract the full :class:ModuleInfo (symbols, imports, docstring).

This is the only language-aware step: it walks the grammar's concrete syntax tree, mapping language-specific node types into the shared, language-agnostic symbol model.

Source code in packages/axm-ast/src/axm_ast/core/backends/base.py
Python
def extract_module(self, path: Path) -> ModuleInfo:
    """Extract the full :class:`ModuleInfo` (symbols, imports, docstring).

    This is the only language-aware step: it walks the grammar's concrete
    syntax tree, mapping language-specific node types into the shared,
    language-agnostic symbol model.
    """
    ...
parse_file(path)

Parse the file at path into a tree-sitter Tree.

Implementations may cache by (path, mtime); they must raise FileNotFoundError for a missing file.

Source code in packages/axm-ast/src/axm_ast/core/backends/base.py
Python
def parse_file(self, path: Path) -> Tree:
    """Parse the file at *path* into a tree-sitter ``Tree``.

    Implementations may cache by (path, mtime); they must raise
    ``FileNotFoundError`` for a missing file.
    """
    ...
parse_source(source)

Parse source into a tree-sitter Tree with this grammar.

Source code in packages/axm-ast/src/axm_ast/core/backends/base.py
Python
def parse_source(self, source: str) -> Tree:
    """Parse *source* into a tree-sitter ``Tree`` with this grammar."""
    ...

backend_for(path)

Return the backend that owns path by its suffix, or None.

Source code in packages/axm-ast/src/axm_ast/core/backends/registry.py
Python
def backend_for(path: Path) -> LanguageBackend | None:
    """Return the backend that owns *path* by its suffix, or ``None``."""
    return get_backend(path.suffix)

get_backend(suffix)

Return the backend registered for suffix, or None if unsupported.

Source code in packages/axm-ast/src/axm_ast/core/backends/registry.py
Python
def get_backend(suffix: str) -> LanguageBackend | None:
    """Return the backend registered for *suffix*, or ``None`` if unsupported."""
    _ensure_default_backends()
    return _REGISTRY.get(suffix)

register_backend(backend)

Register backend for each of its suffixes (idempotent, last wins).

Source code in packages/axm-ast/src/axm_ast/core/backends/registry.py
Python
def register_backend(backend: LanguageBackend) -> None:
    """Register *backend* for each of its suffixes (idempotent, last wins)."""
    for suffix in backend.suffixes:
        _REGISTRY[suffix] = backend

supported_suffixes()

Return every registered file suffix (used for multi-language discovery).

Source code in packages/axm-ast/src/axm_ast/core/backends/registry.py
Python
def supported_suffixes() -> frozenset[str]:
    """Return every registered file suffix (used for multi-language discovery)."""
    _ensure_default_backends()
    return frozenset(_REGISTRY)