# Reading argspecs

The notation TeXForm uses to describe LaTeX command and environment argument signatures, with one real command per parameter kind.

https://texform.dev/docs/argspec

An argspec is the short string that describes the argument structure of a LaTeX command or environment. It records, in order, what each argument slot looks like and how the parser reads it: a mandatory braced group, an optional bracketed value, a leading star, a delimiter-wrapped argument, and so on. For `\frac{a}{b}` the argspec is `m m` — two mandatory arguments; for `\sqrt[n]{x}` it is `o m` — one optional argument followed by one mandatory argument.

You encounter argspecs in two places. Every command and environment detail page shows the argspec for that name, so you can see exactly what it accepts. And when you teach TeXForm a command it does not know — by injecting a context item into the parser — you write the argspec yourself. This page is the reference for what each notation means. To check a string before using it, pass it to `validate_argspec` (see the links at the end).

The notation follows the [xparse](https://ctan.org/pkg/xparse) argument-specifier convention, with a few additions described under [TeXForm extensions](#texform-extensions). The design rationale lives in [Argspec design](https://texform.dev/docs/argspec-design); this page covers only what the notation means and how it parses.

## Notation at a glance [#notation-at-a-glance]

An argspec is a whitespace-separated sequence of slots. Each slot is one base token, optionally preceded by `!` and followed by a value-kind annotation:

```text
[!] token [:kind] [?]
```

| Notation                      | Slot                      | Reads                                                                                                                                                            |
| ----------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `m`                           | mandatory                 | A braced group `{...}` or a single token.                                                                                                                        |
| `m{}`                         | mandatory braced group    | A `{...}` group; errors if the next token is not `{`.                                                                                                            |
| `o`                           | optional                  | A bracketed `[...]` value if present.                                                                                                                            |
| `s`                           | optional star             | A leading `*` if present.                                                                                                                                        |
| `g`                           | optional braced group     | A `{...}` group if the next token is `{`.                                                                                                                        |
| `r⟨open⟩⟨close⟩`              | required delimited        | Content wrapped by the given delimiters; errors if `open` is absent.                                                                                             |
| `d⟨open⟩⟨close⟩`              | optional delimited        | Content wrapped by the given delimiters if `open` is present.                                                                                                    |
| `r<o,c><o,c>…`                | required delimiter choice | Content wrapped by the first matching delimiter pair; errors if none match.                                                                                      |
| `d<o,c><o,c>…`                | optional delimiter choice | Same, but skipped if no pair matches.                                                                                                                            |
| `O{…}` `G{…}` `R…{…}` `D…{…}` | default-bearing aliases   | The same as `o` / `g` / `r` / `d`; the `{…}` default is recorded for documentation and ignored when parsing.                                                     |
| `:T :O :D :N :L :I :K :C`     | value-kind annotation     | Overrides how the slot's content is interpreted (text, operator-name content, delimiter, control-sequence name, dimension, integer, key–value, column template). |
| `?`                           | nullable                  | Allows the slot to carry an empty value, e.g. `m:D?`.                                                                                                            |
| `!`                           | no leading space          | The parser does not skip whitespace before testing for this slot.                                                                                                |

The base token's letter case carries meaning: lowercase `o`/`g`/`r`/`d` are the plain forms, and the uppercase `O`/`G`/`R`/`D` aliases attach a default value that TeXForm records but does not act on. A mandatory slot defaults to math-mode content with no annotation.

## Mandatory arguments: `m` [#mandatory-arguments-m]

A standard required argument. The parser reads a braced group `{...}`, or a single following token if there is no brace.

`\frac` has the argspec `m m`:

```latex
\frac{a}{b}
```

```text
\frac { a } { b }
```

Both arguments are mandatory, so a missing brace is a parse error rather than a skipped slot. With no value-kind annotation, the content is parsed in math mode.

## Mandatory braced groups: `m{}` [#mandatory-braced-groups-m]

A required argument that must be written as `{...}`. Unlike plain `m`, it never falls back to reading a single bare token, and the braces are treated as structural grouping rather than visible delimiters.

`\abs` in the `physics` package has the argspec `s m{}`:

```latex
\abs{x}
```

```text
\abs { x }
```

The `s` slot absorbs an optional `*`; the `m{}` slot requires the body to be braced.

## Optional arguments: `o` [#optional-arguments-o]

A standard optional argument. If the next token is `[`, the parser reads up to the matching `]`; otherwise the slot is left empty and parsing continues.

`\sqrt` has the argspec `o m`:

```latex
\sqrt[3]{x}
```

```text
\sqrt [ 3 ] { x }
```

Without the bracket, the optional slot is simply absent:

```latex
\sqrt{x}
```

```text
\sqrt { x }
```

## Optional star: `s` [#optional-star-s]

Tests whether the next token is `*` and consumes it if so. A command only reacts to a trailing `*` if its argspec declares an `s` slot; otherwise the `*` is treated as ordinary content.

`\ket` in the `physics` package has the argspec `s m`:

```latex
\ket*{\psi}
```

```text
\ket* { \psi }
```

Without the star, the slot records its absence and the rest of the argument list is read unchanged.

## Optional braced group: `g` [#optional-braced-group-g]

Reads a `{...}` group only when the next token is `{`. This is how commands take a variable number of trailing braced arguments: each extra `{...}` fills one more `g` slot.

`\braket` in the `physics` package has the argspec `s m g`:

```latex
\braket{a}{b}
```

```text
\braket { a } { b }
```

The first `{a}` fills the mandatory `m`; the second `{b}` fills the optional `g`. With only one braced group, the `g` slot stays empty.

## Delimited arguments: `r` and `d` [#delimited-arguments-r-and-d]

A delimited slot reads content wrapped by an explicit open/close delimiter pair instead of braces or brackets. `r` makes the wrapping required (a missing open delimiter is an error); `d` makes it optional (a non-matching next token skips the slot). The delimiters can be single characters or control sequences such as `\langle`.

A single-pair delimited form is written `d()` or `r()`. For an injected command with argspec `r()`, the argument must be parenthesized:

```latex
\wrapped(x)
```

```text
\wrapped ( x )
```

The matched delimiters are recorded on the argument, so the original wrapping round-trips through serialization rather than being normalized to braces.

## Delimiter choice: `r<…>` and `d<…>` [#delimiter-choice-r-and-d]

When a single argument slot accepts several different delimiter shapes, the delimiters are listed as `<open,close>` pairs. The parser tries each pair in order and reads with the first one that matches. This is one slot, not several: every shape feeds the same argument position.

`\grad` in the `physics` package has the argspec `d<(,)><[,]><{,}>`:

```latex
\grad(\phi)
```

```text
\grad ( \phi )
```

Because `\grad`'s delimiter slot is optional (`d`), it can also appear with no following delimiter at all, in which case the slot is skipped:

```latex
\grad\phi
```

```text
\grad \phi
```

The characters `<`, `>`, and `,` are reserved inside the pair list and cannot themselves be delimiters.

## Value-kind annotations: `:T`, `:O`, `:D`, `:N`, `:L`, `:I`, `:K`, `:C` [#value-kind-annotations-t-o-d-n-l-i-k-c]

By default a content slot is parsed as math-mode content. A `:` annotation after the token overrides how that slot's value is interpreted:

| Annotation | Slot value                                                                                           |
| ---------- | ---------------------------------------------------------------------------------------------------- |
| *(none)*   | Math-mode content.                                                                                   |
| `:T`       | Text-mode content.                                                                                   |
| `:O`       | Operator-name content: parsed as math content, but marked so operator-name runs serialize compactly. |
| `:D`       | A delimiter token.                                                                                   |
| `:N`       | A control-sequence name (a bare name string; escape sequences are rejected).                         |
| `:L`       | A dimension / length value.                                                                          |
| `:I`       | An integer value.                                                                                    |
| `:K`       | A key–value list.                                                                                    |
| `:C`       | A column template (such as `c\|c` or `p{1em}`).                                                      |

`\label` has the argspec `m:N` — a mandatory argument read as a name string:

```latex
\label{sec:intro}
```

```text
\label { sec:intro }
```

The annotation attaches to the base token, so it combines with any form: `o:L` is an optional dimension, `m{}:T` is a required text-mode braced group, and `r():K` is a required parenthesized key–value argument.

`\operatorname` has the argspec `s m:O`: an optional star followed by operator-name content. The `:O` annotation is distinct from the uppercase `O{default}` alias; `m:O` is still mandatory, while `O{foo}:O` would be an optional slot with a documented default.

## TeXForm extensions [#texform-extensions]

The base letters `m`, `o`, `s`, `g`, `r`, and `d` carry their xparse meanings. The following notations are TeXForm additions, introduced because describing LaTeX **math** commands precisely needs information that xparse does not record:

* **Value-kind annotations (`:T`, `:O`, `:D`, `:N`, `:L`, `:I`, `:K`, `:C`).** xparse does not distinguish what kind of value an argument holds; TeXForm needs that distinction to parse operator-name content, delimiters, dimensions, names, and column templates correctly rather than as generic content.
* **The nullable modifier `?`.** Appended to an annotation, it allows the slot to carry an empty value while still being present — for example `m:D?` is a mandatory delimiter slot that may be written as empty `{}`, normalized to "no delimiter". It is accepted on every value kind except star and column.
* **The required braced group `m{}`.** xparse's `g` is optional only; `m{}` is the required counterpart, where the braces are structural grouping rather than visible delimiters.
* **Delimiter-choice pair lists (`r<…>`, `d<…>`).** A single `r` or `d` in xparse names exactly one delimiter pair. The `<open,close>` list lets one slot accept several delimiter shapes — the form used by commands like `\grad` and `\qty` that wrap their argument in `()`, `[]`, `{}`, or `||` interchangeably.

The `s` token is also treated as an ordinary optional slot that may carry the `!` no-leading-space prefix (as in the `\\` argspec `!s !o:L`), rather than as a special standalone marker.

A few xparse tokens are intentionally not part of this notation, because no supported math command needs them: `l`, `u`, `t`, `e`/`E` embellishments, `v` verbatim, the `b` body specifier (an environment's body mode is tracked separately, not in its argspec), and the `+` long-argument prefix.

## Validate an argspec [#validate-an-argspec]

To confirm a string parses and to inspect the resulting slots, use the validate-argspec function. It returns whether the string is valid, an error message if not, the slot count, and the parsed structure.

**Python**

```python
import json
import texform

print(json.dumps(texform.validate_argspec("o m"), indent=2))
```

```text
{
  "valid": true,
  "error": null,
  "arg_count": 2,
  "parsed": [
    {
      "required": false,
      "no_leading_space": false,
      "nullable": false,
      "kind": {
        "type": "content",
        "mode": "math"
      },
      "form": {
        "type": "standard"
      }
    },
    {
      "required": true,
      "no_leading_space": false,
      "nullable": false,
      "kind": {
        "type": "content",
        "mode": "math"
      },
      "form": {
        "type": "standard"
      }
    }
  ]
}
```

**JavaScript**

```ts
import { validateArgspec } from "texform";

console.log(JSON.stringify(validateArgspec("o m"), null, 2));
```

```text
{
  "argCount": 2,
  "error": null,
  "parsed": [
    {
      "form": {
        "type": "standard"
      },
      "kind": {
        "mode": "math",
        "type": "content"
      },
      "noLeadingSpace": false,
      "nullable": false,
      "required": false
    },
    {
      "form": {
        "type": "standard"
      },
      "kind": {
        "mode": "math",
        "type": "content"
      },
      "noLeadingSpace": false,
      "nullable": false,
      "required": true
    }
  ],
  "valid": true
}
```

An invalid string reports where parsing failed:

**Python**

```python
import json
import texform

print(json.dumps(texform.validate_argspec("m{x}"), indent=2))
```

```text
{
  "valid": false,
  "error": "invalid argspec (validate_argspec) at char 2: `m` only supports required braced group syntax `m{}`",
  "arg_count": null,
  "parsed": null
}
```

**JavaScript**

```ts
import { validateArgspec } from "texform";

console.log(JSON.stringify(validateArgspec("m{x}"), null, 2));
```

```text
{
  "argCount": null,
  "error": "invalid argspec (validate_argspec) at char 2: `m` only supports required braced group syntax `m{}`",
  "parsed": null,
  "valid": false
}
```

## See also [#see-also]

* [`validate_argspec` (Python)](https://texform.dev/api/python/validate-argspec) and [`validateArgspec` (JavaScript)](https://texform.dev/api/js/validate-argspec) — the validation API and result shape.
* [Inject custom commands](https://texform.dev/docs/inject-custom-commands) — write an argspec and teach it to the parser.
* [Argspec design](https://texform.dev/docs/argspec-design) — why this notation, what it extends, and its known limits.

