# Parser

Construct a Parser, parse LaTeX, configure strictness, and inspect the knowledge base.

https://texform.dev/api/python/parser

## `Parser`

Kind: class

```python
Parser(knowledge_base: KnowledgeBase | None = None, default_parse_config: ParseConfig | None = None) -> None
```

Turn LaTeX source into a parse result.

A ``Parser`` consults the knowledge base for command and environment
signatures to build structured parse trees. It never raises on malformed
input and never fabricates a placeholder tree; instead it reports diagnostics
and, in lenient mode, preserves unparseable fragments as ``Error`` nodes.

Since 0.4.0 the default parse configuration is ``LENIENT``
(``reject_unknown=False``, ``abort_on_error=False``). Per-call ``config``
must be a complete ``ParseConfig`` instance; a dict passed as ``config``
raises ``ConfigError``. Other keywords are overlays
(``parse(src, reject_unknown=True)``). Construct a ``KnowledgeBase`` to
customize packages and records, then share it with parsers and engines.

For the conceptual model, see the Parsing guide.

```python
parser = texform.Parser()
result = parser.parse(r"\frac{x}{y}")
result["document"].to_latex()  # '\frac { x } { y }'
parser.parse(r"\frac{x}{y}", config=texform.ParseConfig(reject_unknown=True))
```

- [ParseConfig](https://texform.dev/api/python/parser.md#ParseConfig)
- [Document](https://texform.dev/api/python/document.md#Document)
- [ParseResult](https://texform.dev/api/python/parser.md#ParseResult)
- [Parsing](https://texform.dev/docs/parsing.md)

#### `Parser`

```python
Parser(knowledge_base: KnowledgeBase | None = None, default_parse_config: ParseConfig | None = None) -> None
```

Construct a parser; None uses the process-wide default knowledge base.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `knowledge_base` | KnowledgeBase | None | None |  |
| `default_parse_config` | ParseConfig | None | None |  |

#### `parse`

```python
parse(src: str, config: ParseConfig | None = None, **overrides) -> ParseResult
```

Parse a LaTeX string into a parse result.

Parsing never raises on malformed input. The result has exactly three
honest states: a hard failure (``document`` is ``None``), a clean parse
(a ``Document`` whose ``has_errors()`` is ``False``), or a partial parse
(a read-only ``Document`` whose ``has_errors()`` is ``True``). Empty
input is a clean parse, not ``None``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `src` | str | — | The LaTeX source string. |
| `config` | ParseConfig | None | None | A complete ``ParseConfig``; ``**overrides`` are layered on top. ``None`` uses the parser default. A dict here raises ``ConfigError``. |
| `**overrides` | Unpack[ParseOverrides] | — | Keyword overlay from ``ParseOverrides``. Nested values must be dicts, not config class instances. |

**Returns** `ParseResult` A ``ParseResult`` dict with two keys: ``document`` (a ``Document`` or ``None``) and ``diagnostics`` (a list of diagnostic dicts).

- `ConfigError`: If ``config`` is not a ``ParseConfig``, a dict is passed as ``config``, or an override key or value is invalid.

```python
result = texform.Parser().parse(r"\frac{x}{y}")
document = result["document"]
diagnostics = result["diagnostics"]
```

- [ParseConfig](https://texform.dev/api/python/parser.md#ParseConfig)
- [ParseResult](https://texform.dev/api/python/parser.md#ParseResult)

#### `default_parse_config`

```python
default_parse_config() -> ParseConfig
```

Return a new ``ParseConfig`` holding this parser's defaults.

**Returns** `ParseConfig`

### Knowledge-base lookups

Query the knowledge the parser loaded. Predicate methods take a single `name`; lookup methods take `name` and a `mode` of `"math"` or `"text"` and return a knowledge-entry dict or `None`.

#### `knowledge_base`

```python
knowledge_base() -> KnowledgeBase
```

Return the immutable knowledge base shared with parsed documents.

**Returns** `KnowledgeBase`

## `ParseConfig`

Kind: class

```python
ParseConfig(reject_unknown: bool = False, abort_on_error: bool = False, max_group_depth: int = 128) -> None
```

Complete parse configuration. The only accepted ``config=`` type for parse.

Since 0.4.0 this is a complete configuration class, not a dict. Passing a
dict as ``config`` raises ``ConfigError``; use keyword overlays
(``parse(src, reject_unknown=True)``) or construct this class and pass it as
``config``. Constructor defaults match ``LENIENT`` (the parser and engine
baseline on both language bindings): ``reject_unknown=False``,
``abort_on_error=False``, ``max_group_depth=128``.

``reject_unknown`` and ``abort_on_error`` are independent: the former decides
how unknown names are handled, the latter is a strictness knob for error
recovery. Neither is equivalent to a parsed tree's ``has_errors()``.

```python
cfg = texform.ParseConfig()
cfg.reject_unknown  # False
cfg.abort_on_error  # False
texform.Parser().parse(r"x", config=texform.ParseConfig(reject_unknown=True))
```

- [Parser](https://texform.dev/api/python/parser.md#Parser)

#### `reject_unknown`

When ``True``, an unknown command or environment becomes a diagnostic; when ``False`` (default), unknown names are preserved as nodes for lenient exploration.

#### `abort_on_error`

When ``True`` (strict), the parser stops at the first error per item and produces no recovery ``Error`` nodes; when ``False`` (default, lenient), it keeps collecting diagnostics and emits ``Error`` nodes. The max-group-depth guard emits an ``Error`` node regardless of this setting.

#### `max_group_depth`

The maximum group nesting depth before the parser aborts a group. Defaults to ``128``.

#### `ParseConfig`

```python
ParseConfig(reject_unknown: bool = False, abort_on_error: bool = False, max_group_depth: int = 128) -> None
```

Construct a parse configuration.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `reject_unknown` | bool | False | Whether to reject unknown command and environment names with diagnostics instead of preserving them as nodes. |
| `abort_on_error` | bool | False | Whether to stop at the first error per item (strict) instead of collecting diagnostics and emitting ``Error`` nodes (lenient). |
| `max_group_depth` | int | 128 | The maximum group nesting depth before aborting. |

## `ParseOverrides`

Kind: typedDict

Keyword overlays for ``parse``. Omitted keys keep the baseline.

These are overlay keys, not a complete ``ParseConfig``. Passing a dict as
the ``config`` argument still raises ``ConfigError``; use these keys as
keywords (``parse(src, reject_unknown=True)``) or expand a saved dict with
``**overrides``.

| Name | Type | Description |
| --- | --- | --- |
| `reject_unknown` | bool | Overlay for ``ParseConfig.reject_unknown``. |
| `abort_on_error` | bool | Overlay for ``ParseConfig.abort_on_error``. |
| `max_group_depth` | int | Overlay for ``ParseConfig.max_group_depth``. |

## `ParseResult`

Kind: typedDict

The result of ``Parser.parse``: an optional document plus diagnostics.

There are exactly three honest states, distinguished by ``document`` and the
document's ``has_errors()``: a hard failure (``document`` is ``None``), a
clean parse (a ``Document`` whose ``has_errors()`` is ``False``), and a
partial parse (a read-only ``Document`` whose ``has_errors()`` is ``True``).
The parser never fabricates a placeholder tree, and empty input is a clean
parse rather than ``None``.

| Name | Type | Description |
| --- | --- | --- |
| `document` | Document | None | The parsed ``Document``, or ``None`` on a hard failure. |
| `diagnostics` | list[ParseDiagnostic] | The diagnostics describing what the parser noticed. |

- [Parser](https://texform.dev/api/python/parser.md#Parser)
- [Document](https://texform.dev/api/python/document.md#Document)
- [ParseDiagnostic](https://texform.dev/api/python/parser.md#ParseDiagnostic)

## `ParseDiagnostic`

Kind: typedDict

A single parse diagnostic emitted alongside a parse result.

Diagnostics are a channel separate from ``Error`` placeholder nodes: a
diagnostic describes something the parser noticed, while an ``Error`` node is
a recovered fragment in the tree. A tree that ``has_errors()`` always carries
diagnostics, but the converse does not hold. All fields are optional.

| Name | Type | Description |
| --- | --- | --- |
| `kind` | ParseDiagnosticKind | The diagnostic category, one of the ``ParseDiagnosticKind`` values such as ``"unknown-command"`` or ``"argument-validation"``. |
| `message` | str | A human-readable description of the problem. |
| `span` | Span | A ``{"start": int, "end": int}`` byte range into the source. |
| `expected` | list[str] | The tokens that would have been accepted (may be empty). |
| `found` | str | The token actually found, or absent when not applicable. |
| `contexts` | list[dict[str, Any]] | Nested context dicts describing where the diagnostic arose (may be empty). |

## `ParseDiagnosticKind`

Kind: typeAlias

```python
Literal['ambiguous-infix', 'argument-validation', 'command-mode-error', 'comment-truncated-argument', 'environment-mode-error', 'environment-name-mismatch', 'invalid-character', 'left-right-delimiter', 'max-group-depth-exceeded', 'raw-expected-found', 'text-script-error', 'unclosed-environment', 'unclosed-inline-math', 'unexpected-math-shift', 'unknown-command', 'unknown-environment']
```

Category of a parse diagnostic, such as ``"unknown-command"`` or ``"argument-validation"``.

## `Span`

Kind: typeAlias

```python
dict[str, int]
```

Half-open UTF-8 byte range ``{"start": int, "end": int}`` into the source.

## `SyntaxNode`

Kind: typeAlias

```python
dict[str, Any]
```

Lossless parse snapshot as a plain dict. The tagged-union shape is documented on the TypeScript ``SyntaxNode`` type; Python keeps this alias unexpanded.

## `ContextItem`

Kind: typeAlias

```python
CommandItem | EnvironmentItem | DelimiterItem
```

A knowledge-injection item: a command, environment, or delimiter-control dict.

## `CommandItem`

Kind: typedDict

A custom command injected into parser knowledge.

| Name | Type | Description |
| --- | --- | --- |
| `target` | Literal['command'] | Discriminator; always ``"command"``. |
| `name` | str | Command name without the leading backslash. |
| `kind` | Literal['prefix', 'infix', 'declarative'] | Syntactic role: ``"prefix"``, ``"infix"``, or ``"declarative"``. |
| `allowed_mode` | Literal['math', 'text', 'both'] | Modes in which the command is valid: ``"math"``, ``"text"``, or ``"both"``. |
| `argspec` | str | xparse-style argument specification, such as ``"m m"`` or ``"o m"``. |
| `tags` | NotRequired[list[str]] | Optional free-form classification tags carried into the knowledge entry. |

## `EnvironmentItem`

Kind: typedDict

A custom environment injected into parser knowledge.

| Name | Type | Description |
| --- | --- | --- |
| `target` | Literal['environment'] | Discriminator; always ``"environment"``. |
| `name` | str | Environment name as written in ``\begin{...}``. |
| `allowed_mode` | Literal['math', 'text', 'both'] | Modes in which the environment may appear. |
| `body_mode` | Literal['math', 'text'] | Content mode of the environment body (``"math"`` or ``"text"``). |
| `argspec` | str | xparse-style argument specification for the environment header. |
| `tags` | NotRequired[list[str]] | Optional free-form classification tags. |

## `DelimiterItem`

Kind: typedDict

A custom delimiter control injected into parser knowledge.

| Name | Type | Description |
| --- | --- | --- |
| `target` | Literal['delimiter'] | Discriminator; always ``"delimiter"``. |
| `name` | str | Delimiter-control name without the leading backslash (for example ``"langle"``). |

