Skip to content

Parser

Parsing QVR source into AST nodes.

parser

Parser for the quivers DSL.

The lexer/parser pipeline is delegated to panproto after overriding its qvr protocol with Quivers' packaged current grammar. The public parse entry point consumes .qvr source bytes and returns a Module of didactic models.

This package's submodules group the walker logic by topic:

  • ._registry for the panproto registry singleton, ParseError, and the _Tree view that every walker reads from.
  • ._helpers for the low-level helpers _required_text, _required_field, _field_text, and _walk_draw_arg.
  • .options for the [k=v, ...] option-block walkers.
  • .expressions for type / space / morphism-expression / let-arith walkers.
  • .program_steps for program-block step walkers.
  • .statements for the top-level _walk_statement dispatcher and every per-declaration walker (object, morphism, deduction, contraction, signature, encoder, decoder, loss, ...).
  • .core for the public parse / parse_file entry points and the whole-tree syntax validation that rejects ERROR and missing nodes.

Every public name is re-exported here so from quivers.dsl.parser import X keeps working unchanged.

ParseError

Bases: Exception

Raised when .qvr source fails to parse or wrap into AST nodes.

parse and parse_file raise it with a message that starts with the file path and locates the first syntax error.

parse

parse(source: str | bytes, file_path: str = '<source>') -> Module

Parse .qvr source into a Module.

PARAMETER DESCRIPTION
source

The source text, or its UTF-8 bytes.

TYPE: str | bytes

file_path

The path recorded on diagnostics.

TYPE: str DEFAULT: '<source>'

RETURNS DESCRIPTION
Module

The module's statements in source order.

RAISES DESCRIPTION
ParseError

If the source has a syntax error or a node the AST cannot represent.

Source code in src/quivers/dsl/parser/core.py
def parse(source: str | bytes, file_path: str = "<source>") -> Module:
    """Parse `.qvr` source into a `Module`.

    Parameters
    ----------
    source : str | bytes
        The source text, or its UTF-8 bytes.
    file_path : str
        The path recorded on diagnostics.

    Returns
    -------
    Module
        The module's statements in source order.

    Raises
    ------
    ParseError
        If the source has a syntax error or a node the AST cannot
        represent.
    """
    if isinstance(source, str):
        source_bytes = source.encode("utf-8")
    else:
        source_bytes = source

    try:
        schema = _registry().parse_with_protocol("qvr", source_bytes, file_path)
    except panproto.PanprotoError as exc:
        raise ParseError(f"{file_path}: panproto failed to parse: {exc}") from exc
    tree = _Tree(schema, source_bytes)

    _reject_malformed(tree, file_path)

    root_id = next(
        (vid for vid in tree.vertices if tree.kind(vid) == "source_file"),
        None,
    )
    if root_id is None:
        raise ParseError(
            f"{file_path}: source failed to parse (the tree has no source_file "
            "root); check that every program body ends with a return step"
        )

    statements: list[Statement] = []
    for child in tree.positional(root_id):
        ckind = tree.kind(child)
        if ckind in ("line_comment", "block_comment"):
            # plain comments are extras and are dropped at parse time;
            # `#!` doc comments ride each declaration's `docs` field
            # and are attached by the per-declaration walkers.
            continue
        result = _walk_statement(tree, child)
        if isinstance(result, list):
            statements.extend(result)
        else:
            statements.append(result)
    return Module(statements=tuple(statements))

parse_file

parse_file(path: str | Path) -> Module

Parse a .qvr file at path.

PARAMETER DESCRIPTION
path

The file to read.

TYPE: str | Path

RETURNS DESCRIPTION
Module

The file's statements in source order.

RAISES DESCRIPTION
OSError

If the file cannot be read.

ParseError

If the file does not parse.

Source code in src/quivers/dsl/parser/core.py
def parse_file(path: str | Path) -> Module:
    """Parse a `.qvr` file at `path`.

    Parameters
    ----------
    path : str | Path
        The file to read.

    Returns
    -------
    Module
        The file's statements in source order.

    Raises
    ------
    OSError
        If the file cannot be read.
    ParseError
        If the file does not parse.
    """
    p = Path(path)
    return parse(p.read_bytes(), str(p))