# Document

Construct, query, edit, and serialize the editable TeXForm document tree.

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

## `Document`

Kind: class

```python
Document(knowledge_base: KnowledgeBase | None = None, mode: Literal['math', 'text'] = 'math') -> None
```

The editable document tree — the working format you read, edit, and output.

A ``Document`` wraps the internal arena tree behind a fallible, DOM-style
surface, so no internal panic ever reaches the caller. Reads go through
read-only ``Node`` handles; edits are validated eagerly and raise on misuse.
The tree is always structurally valid, even when it contains ``Error`` nodes.

A tree that ``has_errors()`` is read-only, fixed that way at construction:
every editing method then raises ``EditError``. The only use for an error
tree is inspection.

Construct one by parsing (``Parser.parse``), from scratch (``Document()``),
or from a snapshot (``Document.from_syntax``). For the conceptual model, see
the Parsing guide.

```python
import texform

doc = texform.Parser().parse(r"\frac{x}{y}")["document"]
doc.to_latex()  # '\frac { x } { y }'
```

- [Node](https://texform.dev/api/python/node.md#Node)
- [Parser](https://texform.dev/api/python/parser.md#Parser)
- [ParseResult](https://texform.dev/api/python/parser.md#ParseResult)
- [Parsing](https://texform.dev/docs/parsing.md)

### Construction and copying

#### `Document`

```python
Document(knowledge_base: KnowledgeBase | None = None, mode: Literal['math', 'text'] = 'math') -> None
```

Construct an empty document holding a single empty root.

This is a complete tree: ``has_errors()`` is ``False`` and it is fully
editable. It serializes to the empty string.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `knowledge_base` | KnowledgeBase | None | None |  |
| `mode` | Literal['math', 'text'] | 'math' |  |

```python
doc = texform.Document()
doc.to_latex()  # ''
```

#### `from_syntax`

```python
from_syntax(node: SyntaxNode, knowledge_base: KnowledgeBase | None = None) -> Document
```

Build a document from a ``SyntaxNode`` dict, the lossless parse snapshot.

``from_syntax`` and ``to_syntax`` are symmetric over every node kind,
including ``Error`` and ``Prime``. A bare ``Prime`` is a math symbol;
use ``Scripted`` with an empty group base for a leading quote. Invalid external syntax is rejected
rather than corrupting the tree. The document can be transformed by any
engine sharing its knowledge-base instance.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | SyntaxNode | — | A ``SyntaxNode`` dict, typically produced by ``to_syntax()``. |
| `knowledge_base` | KnowledgeBase | None | None |  |

**Returns** `Document` A new ``Document`` wrapping the snapshot.

- `ConformanceError`: If the snapshot does not conform to the knowledge base.

```python
result = texform.Parser().parse(r"\frac{x}{y}")
document = result["document"]
assert document is not None
syntax = document.to_syntax()
doc = texform.Document.from_syntax(syntax)
```

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

#### `copy`

```python
copy() -> Document
```

Deep-copy the tree into a new document with new node identities.

The copy shares this document's knowledge base, so a transform engine built on it accepts both. Also available as ``copy.copy`` and ``copy.deepcopy``.

**Returns** `Document`

#### `__copy__`

```python
__copy__() -> Document
```

**Returns** `Document`

#### `__deepcopy__`

```python
__deepcopy__(memo: dict[int, Any]) -> Document
```

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `memo` | dict[int, Any] | — |  |

**Returns** `Document`

### Queries

#### `root`

```python
root() -> Node
```

Return the root ``Node`` of the tree.

**Returns** `Node` The ``Root`` node handle.

```python
doc.root().kind()  # 'Root'
```

#### `has_errors`

```python
has_errors() -> bool
```

Report whether the tree contains any ``Error`` placeholder node.

This is a cheap O(1) signal, separate from structural validity and
independent of the ``abort_on_error`` parse setting. When ``True``, the
document is read-only.

**Returns** `bool` ``True`` if the tree holds at least one ``Error`` node.

```python
result = texform.Parser().parse(r"\sqrt[")
document = result["document"]
assert document is not None
document.has_errors()  # True
```

- [Document.errors](https://texform.dev/api/python/document.md#Document.errors)
- [Document.is_read_only](https://texform.dev/api/python/document.md#Document.is_read_only)

#### `is_read_only`

```python
is_read_only() -> bool
```

Report whether the document is read-only.

A tree that ``has_errors()`` is read-only and fixed that way at
construction; its error count cannot change, so its read-only-ness cannot
either.

**Returns** `bool` ``True`` if no editing method will succeed on this document.

#### `errors`

```python
errors() -> list[Node]
```

Return the ``Error`` placeholder nodes in the tree.

**Returns** `list[Node]` The list of ``Error`` node handles, empty when ``has_errors()`` is ``False``.

#### `find_commands`

```python
find_commands(name: str) -> list[Node]
```

Return every command node whose name matches.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | str | — | The command name without the leading backslash. |

**Returns** `list[Node]` The matching ``Command`` node handles, in document order.

```python
doc = texform.Parser().parse(r"\frac{x}{y} + \frac{a}{b}")["document"]
len(doc.find_commands("frac"))  # 2
```

#### `find_environments`

```python
find_environments(name: str) -> list[Node]
```

Return every environment node whose name matches.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | str | — | The environment name. |

**Returns** `list[Node]` The matching ``Environment`` node handles, in document order.

#### `node_spans`

```python
node_spans() -> list[NodeSpanEntry]
```

Return source byte spans for the nodes that carry them.

**Returns** `list[NodeSpanEntry]` A list of ``{"id": str, "span": {...}}`` entries mapping node identifiers to source byte ranges.

#### `node_at`

```python
node_at(path: str) -> Node
```

Resolve a tree path to a node handle.

Paths are rooted at ``root`` and use the scheme of ``Node.path()``; use paths read from the current tree, since edits shift them.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `path` | str | — | A path such as one returned by ``Node.path()``. |

**Returns** `Node` The node at that path.

- `EditError`: If the path does not exist.

#### `knowledge_base`

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

Return the immutable knowledge base bound to this document.

Construction and edits are checked against it; ``to_latex`` is not.

**Returns** `KnowledgeBase`

### Output

#### `to_latex`

```python
to_latex(**options) -> str
```

Serialize the tree back to LaTeX text.

``Error`` nodes round-trip their captured source snippet verbatim, so a
partial tree round-trips losslessly. The serializer guarantees text
idempotency: re-parsing and re-serializing the output yields the
same string. There is intentionally no method named ``serialize`` on
``Document``; this is the text channel and ``to_syntax()`` is the data
channel.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `**options` | Unpack[SerializeOptions] | — | ``SerializeOptions`` keywords. Omitted keys and explicit ``None`` keep the default (spaced) style. |

**Returns** `str` The serialized LaTeX string.

- `ConfigError`: If an option key is unknown or a value has the wrong type.
- `TypeError`: If options are passed as a positional dict instead of keyword arguments. Use ``doc.to_latex(**options)`` for a dict.

```python
doc = texform.Parser().parse(r"x^2")["document"]
doc.to_latex()                  # 'x ^ { 2 }'
doc.to_latex(style="compact")   # 'x^2'
```

- [SerializeOptions](https://texform.dev/api/python/serialize.md#SerializeOptions)
- [serialize](https://texform.dev/api/python/serialize.md#serialize)
- [Document.to_syntax](https://texform.dev/api/python/document.md#Document.to_syntax)

#### `to_tokenized_latex`

```python
to_tokenized_latex(**options) -> TokenizedLatex
```

Serialize the tree to LaTeX together with typed output tokens.

``start_byte`` and ``end_byte`` are UTF-8 byte offsets, not Python
string indices. Empty error snippets produce no zero-width token; use
``has_errors()`` to detect whether the document contains error nodes.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `**options` | Unpack[SerializeOptions] | — | ``SerializeOptions`` keywords, as for ``to_latex``. Omitted keys and explicit ``None`` keep the default (spaced) style. |

**Returns** `TokenizedLatex` A ``TokenizedLatex`` dict with ``latex`` and ``tokens``.

- `ConfigError`: If an option key is unknown or a value has the wrong type.

```python
tok = texform.Parser().parse(r"x^2")["document"].to_tokenized_latex()
tok["latex"]  # 'x ^ { 2 }'
tok["tokens"][0]["kind"]  # 'character'
```

#### `to_syntax`

```python
to_syntax() -> SyntaxNode
```

Convert the tree to a ``SyntaxNode`` dict for serde and transport.

This is the structured-data channel, distinct from the text channel
``to_latex()``. The conversion is symmetric with ``from_syntax``.

**Returns** `SyntaxNode` A ``SyntaxNode`` dict, the single serde DTO for the tree.

```python
doc.to_syntax()  # {'Root': {...}}
```

- [Document.from_syntax](https://texform.dev/api/python/document.md#Document.from_syntax)
- [Document.to_latex](https://texform.dev/api/python/document.md#Document.to_latex)

#### `to_columnar`

```python
to_columnar() -> ColumnarTree
```

Flatten the tree into columns for bulk structural analysis with Arrow or DataFrame tools.

Each table can be passed directly to ``pyarrow.table``. Parent, slot
index, and content columns use -1 when no row or index applies. Other
missing values are None. Modes describe each node's context. Detached
subtrees are excluded; incomplete documents remain exportable.

**Returns** `ColumnarTree`

### Staging constructors

The `create_*` methods, `parse_fragment`, `clone_node`, and `import_node` build detached subtrees owned by this document. A staged node is not in the tree until you attach it with an edit method.

#### `create_char`

```python
create_char(value: str, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``Char`` node owned by this document.

Staged nodes are not in the tree until attached with an edit method. The context ``mode`` defaults to the document root's mode; every ``create_*`` method accepts it as a keyword to construct in another context.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | str | — | The single character. ``"&"`` is a literal ampersand written ``\&``. |
| `mode` | RuntimeContentMode | None | None | The context mode, ``"math"`` or ``"text"``. |

**Returns** `Node` The detached ``Char`` node handle.

- `ConformanceError`: If the character cannot be written in that mode, such as ``\``, ``^``, ``~``, or a math-mode space.
- `EditError`: If the document is read-only.

#### `create_text`

```python
create_text(value: str, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``Text`` node owned by this document.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | str | — | The text value. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to the root mode. |

**Returns** `Node` The detached ``Text`` node handle.

- `ConformanceError`: If the text cannot be written in that mode.
- `EditError`: If the document is read-only.

#### `create_active_space`

```python
create_active_space(mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``ActiveSpace`` node owned by this document.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | RuntimeContentMode | None | None |  |

**Returns** `Node` The detached ``ActiveSpace`` node handle.

#### `create_group`

```python
create_group(mode: RuntimeContentMode | None = None, children: list[Node | str] | None = None) -> Node
```

Create a detached brace ``Group``, built in its own ``mode``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | RuntimeContentMode | None | None | The group's content mode and context. Defaults to the root mode. |
| `children` | list[Node | str] | None | None | Detached nodes or source strings. A string is parsed in ``mode`` and its nodes are spliced into the list, so one string may yield several children. |

**Returns** `Node` The detached ``Group`` node handle.

- `ParseError`: If a source string does not parse cleanly.
- `ConformanceError`: If a child was built in another mode or an infix has siblings.
- `EditError`: If a child is attached, foreign, or repeated, or the document is read-only.

```python
doc = texform.Document()
group = doc.create_group("math", ["x^2", doc.create_char("y")])
```

#### `create_command`

```python
create_command(name: str, args: list[Arg] | None = None, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached prefix ``Command`` owned by this document.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | str | — | The command name without the leading backslash. A name missing from the knowledge base creates an unknown command without arguments. |
| `args` | list[Arg] | None | None | Values for every signature slot, or only the required slots. A ``str`` is the source written inside the slot's boundaries; ``None`` omits an optional slot and means false in a star slot; ``Paired`` selects an explicit delimiter pair. ``bool`` sets a star slot. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to the root mode. |

**Returns** `Node` The detached ``Command`` node handle.

- `ConformanceError`: If the name is not a prefix command (the message names the right constructor, such as ``create_declarative`` for ``bf``), the argument count is wrong, or an argument does not fit its slot.
- `ParseError`: If a source argument does not parse cleanly.
- `EditError`: For invalid nodes or a read-only document.

```python
doc = texform.Document()
cmd = doc.create_command("sqrt", ["x"])
doc.append_child(doc.root(), cmd)
doc.to_latex()  # '\sqrt { x }'
```

#### `create_declarative`

```python
create_declarative(name: str, args: list[Arg] | None = None, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``Declarative`` command such as ``\bf``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | str | — | The declarative command name without the leading backslash. |
| `args` | list[Arg] | None | None | Slot values, as for ``create_command``. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to the root mode. |

**Returns** `Node` The detached ``Declarative`` node handle.

- `ConformanceError`: Under the conditions of ``create_command``, except the record must be declarative.
- `ParseError`: If a source argument does not parse cleanly.

#### `create_environment`

```python
create_environment(name: str, args: list[Arg] | None = None, body: Node | list[Node | str] | str | None = None, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``Environment`` owned by this document.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | str | — | The environment name. An unknown environment takes no arguments and uses the surrounding mode. |
| `args` | list[Arg] | None | None | Slot values, as for ``create_command``. |
| `body` | Node | list[Node | str] | str | None | None | A detached group in the body mode, a source string parsed in the recorded body mode, a list whose source strings are spliced as in ``create_group``, or ``None`` for an empty body. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to the root mode. |

**Returns** `Node` The detached ``Environment`` node handle.

- `ConformanceError`: If an argument does not fit the signature or the body is not a group in the body mode.
- `ParseError`: If source does not parse cleanly.
- `EditError`: For invalid nodes or a read-only document.

```python
doc = texform.Document()
env = doc.create_environment("matrix", body="a & b")
```

#### `create_alignment_tab`

```python
create_alignment_tab(mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``AlignmentTab`` node owned by this document.

This is the unescaped ``&`` that separates cells in alignment environments. Use ``create_char("&")`` for a literal ampersand, which serializes as ``\&``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | RuntimeContentMode | None | None |  |

**Returns** `Node` The detached ``AlignmentTab`` node handle.

#### `create_delimited_group`

```python
create_delimited_group(left: str, right: str, children: list[Node | str] | None = None, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``\left...\right`` group, built in math mode.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `left` | str | — | The left delimiter source, such as ``"("`` or ``"\langle"``. |
| `right` | str | — | The right delimiter source. |
| `children` | list[Node | str] | None | None | Detached nodes or source strings, spliced as in ``create_group``. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to ``"math"``. |

**Returns** `Node` The detached delimited ``Group`` node handle.

- `ConformanceError`: If a delimiter is neither ``.`` nor registered in the knowledge base, or a child does not fit.
- `ParseError`: If a source string does not parse cleanly.
- `EditError`: For invalid children or a read-only document.

#### `create_inline_math`

```python
create_inline_math(children: list[Node | str] | None = None, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``$...$`` group whose children are math.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | list[Node | str] | None | None | Detached nodes or source strings, spliced as in ``create_group``. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to ``"text"``. |

**Returns** `Node` The detached inline-math ``Group`` node handle.

- `ParseError`: If a source string does not parse cleanly.
- `ConformanceError`: If a child does not fit or the context is math.
- `EditError`: For invalid children or a read-only document.

#### `create_scripted`

```python
create_scripted(base: Node | str, sub: Node | str | None = None, sup: Node | str | None = None, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``Scripted`` node with a base and subscript and/or superscript.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `base` | Node | str | — | The base node or source string. |
| `sub` | Node | str | None | None | The subscript node or source, or ``None`` for none. |
| `sup` | Node | str | None | None | The superscript node or source, or ``None`` for none. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to ``"math"``. |

**Returns** `Node` The detached ``Scripted`` node handle.

- `ConformanceError`: If both scripts are absent, or the base is a scripted node, an infix, or an alignment tab.
- `ParseError`: If a source string does not parse cleanly.
- `EditError`: For invalid nodes or a read-only document.

```python
doc = texform.Document()
doc.append_child(doc.root(), doc.create_scripted("x", sup="2"))
```

#### `create_prime`

```python
create_prime(count: int, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached run of ``count`` primes.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `count` | int | — | The number of primes; must be positive. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to ``"math"``. |

**Returns** `Node` The detached ``Prime`` node handle.

- `ConformanceError`: If ``count`` is zero.

#### `create_infix`

```python
create_infix(name: str, left: Node | str, right: Node | str, args: list[Arg] | None = None, mode: RuntimeContentMode | None = None) -> Node
```

Create a detached ``Infix`` command such as ``\over``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | str | — | The infix command name without the leading backslash. |
| `left` | Node | str | — | The left operand node or source string. |
| `right` | Node | str | — | The right operand node or source string. |
| `args` | list[Arg] | None | None | Values for extra slots, such as the dimension of ``\above``. |
| `mode` | RuntimeContentMode | None | None | The context mode. Defaults to ``"math"``. |

**Returns** `Node` The detached ``Infix`` node handle.

- `ConformanceError`: Under the conditions of ``create_command``, except the record must be infix.
- `ParseError`: If a source operand or argument does not parse cleanly.

#### `parse_fragment`

```python
parse_fragment(source: str, mode: RuntimeContentMode | None = None) -> Node
```

Parse source into a detached implicit ``Group``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | str | — | The LaTeX source. |
| `mode` | RuntimeContentMode | None | None | The parse mode. Defaults to the root mode. |

**Returns** `Node` The detached ``Group`` node handle.

- `ParseError`: If the source does not parse cleanly. ``diagnostics`` carries the parser diagnostics.

#### `clone_node`

```python
clone_node(node: Node) -> Node
```

Deep-copy a subtree of this document as a detached node.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The subtree root to copy. |

**Returns** `Node` The detached copy, ready to attach.

- `EditError`: If ``node`` is foreign or missing, or the document is read-only.

#### `import_node`

```python
import_node(node: Node) -> Node
```

Copy a subtree from another document into this one as a detached node.

The subtree is validated against this document's knowledge base, so importing across knowledge bases can fail where ``clone_node`` cannot.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | A node of another document. |

**Returns** `Node` The detached copy, owned by this document.

- `ConformanceError`: If the subtree does not conform to this knowledge base.
- `EditError`: If the edit is invalid.

### Edit methods

All edits are validated eagerly and raise `EditError` on misuse. Nodes must belong to this document, the root cannot be detached, and a tree that `has_errors()` is read-only.

#### `append_child`

```python
append_child(parent: Node, child: Node) -> None
```

Append ``child`` as the last child of ``parent``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `parent` | Node | — | The container node to append into. |
| `child` | Node | — | The detached or detached node to attach. |

**Returns** `None`

- `EditError`: If the edit is invalid (read-only tree, root protection, wrong container shape, or a foreign node).

#### `insert_before`

```python
insert_before(anchor: Node, new: Node) -> None
```

Insert ``new`` immediately before ``anchor`` among its siblings.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `anchor` | Node | — | The node to insert before. |
| `new` | Node | — | The detached or detached node to attach. |

**Returns** `None`

- `EditError`: If the edit is invalid.

#### `insert_after`

```python
insert_after(anchor: Node, new: Node) -> None
```

Insert ``new`` immediately after ``anchor`` among its siblings.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `anchor` | Node | — | The node to insert after. |
| `new` | Node | — | The detached or detached node to attach. |

**Returns** `None`

- `EditError`: If the edit is invalid.

#### `insert_child`

```python
insert_child(parent: Node, index: int, child: Node) -> None
```

Insert ``child`` at ``index`` among ``parent``'s children.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `parent` | Node | — | The container node to insert into. |
| `index` | int | — | The zero-based position to insert at. |
| `child` | Node | — | The detached or detached node to attach. |

**Returns** `None`

- `EditError`: If the edit is invalid, including an out-of-bounds index.

#### `replace_with`

```python
replace_with(target: Node, replacement: Node) -> None
```

Replace ``target`` in the tree with ``replacement``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `target` | Node | — | The node to remove from its position. |
| `replacement` | Node | — | The detached or detached node to put in its place. |

**Returns** `None`

- `EditError`: If the edit is invalid.

#### `wrap`

```python
wrap(target: Node, wrapper: Node) -> Node
```

Wrap ``target`` inside ``wrapper``, putting ``wrapper`` in its place.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `target` | Node | — | The node to be wrapped. |
| `wrapper` | Node | — | The detached container node to wrap it in. |

**Returns** `Node` The ``wrapper`` node handle, now in the tree.

- `EditError`: If the edit is invalid.

#### `unwrap`

```python
unwrap(group: Node) -> list[Node]
```

Remove a group, splicing its children into the parent in its place.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `group` | Node | — | The group node to dissolve. |

**Returns** `list[Node]` The freed child node handles, now attached to the parent.

- `EditError`: If the edit is invalid.

```python
doc = texform.Parser().parse(r"{x y}")["document"]
group = next(n for n in doc.root().descendants() if n.kind() == "Group")
doc.unwrap(group)
doc.to_latex()  # 'x y'
```

#### `extract`

```python
extract(node: Node) -> Node
```

Detach ``node`` from the tree and return it as a detached node.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The node to remove from its position and keep. |

**Returns** `Node` The detached node handle, reusable in a later edit.

- `EditError`: If the edit is invalid.

#### `remove`

```python
remove(node: Node) -> None
```

Remove ``node`` from the tree and discard it.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The node to remove. |

**Returns** `None`

- `EditError`: If the edit is invalid, such as removing the root.

#### `clear`

```python
clear(container: Node) -> None
```

Remove every child of ``container``.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `container` | Node | — | The node whose children are removed. |

**Returns** `None`

- `EditError`: If the edit is invalid.

#### `set_command_name`

```python
set_command_name(node: Node, name: str) -> None
```

Rename a command node in place.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The ``Command`` or ``Declarative`` node to rename. |
| `name` | str | — | The new command name without the leading backslash. |

**Returns** `None`

- `EditError`: If the edit is invalid.

#### `set_env_name`

```python
set_env_name(node: Node, name: str) -> None
```

Rename an environment, keeping its arguments and body.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The ``Environment`` node to rename. |
| `name` | str | — | The new environment name. |

**Returns** `None`

- `ConformanceError`: If the new name's signature or body mode does not fit the existing arguments and body.
- `EditError`: If the edit is invalid.

#### `set_text`

```python
set_text(node: Node, value: str) -> None
```

Set a text node's value in place.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The ``Text`` node to update. |
| `value` | str | — | The new text value. |

**Returns** `None`

- `EditError`: If the edit is invalid.

#### `set_char`

```python
set_char(node: Node, value: str) -> None
```

Set a char node's value in place.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The ``Char`` node to update. |
| `value` | str | — | The new single character. |

**Returns** `None`

- `EditError`: If the edit is invalid.

#### `set_arg`

```python
set_arg(node: Node, index: int, value: Arg) -> None
```

Set the argument at ``index`` of a command-like node or environment.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The command-like node or environment whose argument is set. |
| `index` | int | — | The zero-based slot index. |
| `value` | Arg | — | The slot source (a ``str``), content node, star flag, ``None`` to clear an optional slot, or ``Paired``. |

**Returns** `None`

- `ConformanceError`: If the value does not fit the slot.
- `ParseError`: If a source string does not parse cleanly.
- `EditError`: If the edit is invalid, including an out-of-bounds index.

#### `set_arg_delimiters`

```python
set_arg_delimiters(node: Node, index: int, open: str, close: str) -> None
```

Change only the boundaries of a filled paired argument.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The command-like node. |
| `index` | int | — | The zero-based slot index. |
| `open` | str | — | The new opening delimiter source. |
| `close` | str | — | The new closing delimiter source. |

**Returns** `None`

- `ConformanceError`: If the slot is not a filled paired slot or the delimiters are not allowed.
- `EditError`: If the edit is invalid.

#### `set_delimiters`

```python
set_delimiters(node: Node, left: str, right: str) -> None
```

Change the delimiters of a ``\left...\right`` group.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The delimited ``Group`` node. |
| `left` | str | — | The new left delimiter source. |
| `right` | str | — | The new right delimiter source. |

**Returns** `None`

- `ConformanceError`: If a delimiter is neither ``.`` nor registered in the knowledge base.
- `EditError`: If the edit is invalid.

#### `set_subscript`

```python
set_subscript(target: Node, sub: Arg) -> Node
```

Set or clear the subscript of ``target``.

If ``target`` is not scripted, it becomes the base of a new ``Scripted`` node; an existing scripted base is resolved automatically.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `target` | Node | — | The base node or an existing ``Scripted`` node. |
| `sub` | Arg | — | The new subscript node or source, or ``None`` to clear it. |

**Returns** `Node` The resulting ``Scripted`` node, or the base when clearing the last script collapses the wrapper.

- `ConformanceError`: If the base cannot carry scripts.
- `ParseError`: If a source string does not parse cleanly.
- `EditError`: If the edit is invalid.

#### `set_superscript`

```python
set_superscript(target: Node, sup: Arg) -> Node
```

Set or clear the superscript of ``target``.

Behaves like ``set_subscript`` for the superscript slot.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `target` | Node | — | The base node or an existing ``Scripted`` node. |
| `sup` | Arg | — | The new superscript node or source, or ``None`` to clear it. |

**Returns** `Node` The resulting ``Scripted`` node, or the base after a collapse.

- `ConformanceError`: If the base cannot carry scripts.
- `ParseError`: If a source string does not parse cleanly.
- `EditError`: If the edit is invalid.

#### `set_prime_count`

```python
set_prime_count(node: Node, count: int) -> None
```

Set the number of primes in a ``Prime`` node.

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `node` | Node | — | The ``Prime`` node. |
| `count` | int | — | The new count; must be positive. |

**Returns** `None`

- `ConformanceError`: If ``count`` is not positive.
- `EditError`: If the edit is invalid.

## `NodeSpanEntry`

Kind: typedDict

One entry mapping a node identifier to its source byte span.

| Name | Type | Description |
| --- | --- | --- |
| `id` | str | The node identifier within the document. |
| `span` | Span | A ``{"start": int, "end": int}`` byte range into the source. |

## `TokenizedLatex`

Kind: typedDict

Serialized LaTeX and tokens produced by the same serializer traversal.

Text and tokenized output share one traversal and spacing policy; this is
not a second lexer pass.

| Name | Type | Description |
| --- | --- | --- |
| `latex` | str | The serialized LaTeX string, identical to ``Document.to_latex()``. |
| `tokens` | list[SerializationToken] | Ordered, non-overlapping, non-empty fragments. Gaps between spans contain serializer-inserted spacing. |

## `SerializationToken`

Kind: typedDict

One serialization fragment with UTF-8 byte offsets.

``start_byte`` / ``end_byte`` index the accompanying ``TokenizedLatex.latex``
string and are UTF-8 byte offsets, not Python code-point indices. Spans do
not overlap. Empty error snippets emit no token.

| Name | Type | Description |
| --- | --- | --- |
| `text` | str | The UTF-8 decoding of the byte slice ``latex.encode("utf-8")[start_byte:end_byte]``. |
| `start_byte` | int | Inclusive UTF-8 byte offset into the serialized LaTeX string. |
| `end_byte` | int | Exclusive UTF-8 byte offset into the serialized LaTeX string. |
| `kind` | SerializationTokenKind | Semantic category of the fragment (control sequence, character, ...). |
| `mode` | RuntimeContentMode | Semantic math/text mode of the fragment, independent of spacing policy. |

## `SerializationTokenKind`

Kind: typeAlias

```python
Literal['control_sequence', 'character', 'delimiter', 'text', 'raw', 'error']
```

Semantic category of one serialization fragment.

## `NodeTable`

Kind: typedDict

Preorder node columns. Row indices are local to this snapshot.

| Name | Type | Description |
| --- | --- | --- |
| `kind` | list[NodeKind] |  |
| `parent` | list[int] |  |
| `slot` | list[NodeSlotKind | None] |  |
| `slot_index` | list[int] |  |
| `depth` | list[int] |  |
| `mode` | list[RuntimeContentMode] |  |
| `name` | list[str | None] |  |
| `value` | list[str | None] |  |
| `known` | list[bool | None] |  |
| `group_kind` | list[Literal['explicit', 'implicit', 'delimited', 'inline_math'] | None] |  |
| `left` | list[str | None] |  |
| `right` | list[str | None] |  |

## `ArgumentTable`

Kind: typedDict

All argument slots, ordered by owner row and slot index.

| Name | Type | Description |
| --- | --- | --- |
| `owner` | list[int] |  |
| `index` | list[int] |  |
| `form` | list[ArgFormKind | None] |  |
| `present` | list[bool] |  |
| `value_kind` | list[Literal['math', 'text', 'operator_name', 'delimiter', 'cs_name', 'dimension', 'integer', 'key_val', 'column', 'boolean'] | None] |  |
| `value` | list[str | None] |  |
| `content` | list[int] |  |
| `open` | list[str | None] |  |
| `close` | list[str | None] |  |

## `ColumnarTree`

Kind: typedDict

Tree columns for bulk structural analysis with Arrow or DataFrame tools.

| Name | Type | Description |
| --- | --- | --- |
| `nodes` | NodeTable |  |
| `args` | ArgumentTable |  |

