UNPKG

@macroforge/mcp-server

Version:

MCP server for Macroforge documentation and code analysis

81 lines (63 loc) • 4.1 kB
# Patterns & Fragments A pattern is a sequence of metavariables (`$name:Kind`), repetitions, and the literal separators `,` and `;`. Anything else in a pattern is a parse error. ```typescript const $pick = macroRules` ($obj:Ident, $key:Lit) => $obj[$key] `; ``` ## Fragment specifiers There are 11 fragment kinds. Each has a PascalCase name and a lowercase alias — note that two of the aliases are **not** simply the lowercased name: | Kind | Alias | Matches | | ----------- | ------------- | ---------------------------------------- | | `Expr` | `expr` | Any expression | | `Stmt` | `stmt` | Any statement — _see below_ | | `Block` | `block` | An arrow function (`() => { … }`) | | `Ident` | `ident` | A bare identifier | | `Type` | **`ty`** | A TypeScript type — _type position only_ | | `Pat` | `pat` | A destructuring pattern — _see below_ | | `Lit` | **`literal`** | A literal value | | `Path` | `path` | An identifier or dotted member (`a.b.c`) | | `Item` | `item` | A top-level declaration — _see below_ | | `Decorator` | `decorator` | A decorator annotation — _see below_ | | `Tt` | `tt` | Structural fallback | ## What each kind actually accepts In **value position** (a normal `$macro(...)` call), JavaScript's grammar only permits expressions as arguments. That constrains things more than the names suggest: - **`Lit`** accepts string, numeric, boolean, `null`, BigInt, regex, **and template** literals. - **`Path`** accepts an identifier or a _static_ member expression. `a.b.c` matches; `a[b]` and `a?.b` do **not**. - **`Block`** accepts only an arrow function expression — not a bare `{ … }`, which isn't an expression. - **`Tt`** is a fallback that matches any expression. It is not a universal wildcard: it cannot match statements, patterns, or items. - **Spread arguments (`...xs`) never match any fragment kind.** Four kinds parse fine but produce a hard error if you use them in value position, because nothing they describe can appear as a call argument. Each error explains the mismatch and suggests an alternative: | Kind | Why it can't match, and what to do instead | | ----------- | --------------------------------------------------------------------------------------------- | | `Stmt` | Statements aren't expressions. Pass `() => { …stmts }` and capture it with `Block`. | | `Type` | Types only appear in type position. Use `Expr` here, or declare a `kind: "type"` macro. | | `Pat` | Destructuring patterns only appear in binding position. Capture the whole object with `Expr`. | | `Item` | Declarations can't be arguments. Capture a function/class _expression_ with `Expr`. | | `Decorator` | Decorators attach to declarations. Capture the call itself with `Expr`. | In **type position**, the rules invert: only `Type` and `Tt` are accepted, and any other kind errors. See [Type-Position Macros](/docs/declarative-macros/type-macros). ## Literal separators `,` and `;` may appear as literal pattern elements, but they are **ignored during matching** — they're structural sugar, not something the matcher enforces. A consequence worth knowing: the common `$(,)?` trailing-comma idiom is a no-op. It parses, and it does no harm, but it isn't what makes trailing commas work. ## Matching order Arms are tried top to bottom, and the first match wins. Put your most specific arms first: ```typescript const $sum = macroRules` () => 0 ($x:Expr) => $x ($x:Expr, $($rest:Expr),+) => ($x + $sum($($rest),+)) `; ``` `()` matches exactly zero arguments. If no arm matches, you get a `NoArmMatched` error listing every pattern that was tried — see the [diagnostics catalogue](/docs/declarative-macros/cross-file).