docs: resolve core parser design decisions

Capture the design decisions reached while grilling the core-parser spec:

- Parser is a total function; invalid frontmatter YAML surfaces through a
  new Document AST `diagnostics` channel instead of throwing (ADR 0004).
- Parse all of OFM losslessly via `RawBlock`/`RawInline` fallbacks with
  position-sliced verbatim source and a minimal plugin set (ADR 0005).
- Grammar-driven (not last-space) quantity/unit split, fixing the
  mixed-number collision, with trimming and multi-word unit rules.
- Container blocks nest `Block[]` so annotations surface inside
  blockquotes and list items; unified wikilink/transclusion shape.

Update SPEC.md and CONTEXT.md accordingly; no parser code yet.
This commit is contained in:
2026-07-14 20:52:15 -04:00
parent 66bb497aca
commit 3ffd2b3f9f
4 changed files with 178 additions and 13 deletions

10
SPEC.md
View File

@@ -57,8 +57,11 @@ Multi-word names work naturally: `@unsalted butter{30 g}`.
- Mixed number: `1 1/2`
**Unit**: optional, free-form string.
When both quantity and unit are present, they are delimited by the **last space** inside the braces.
The quantity/unit split is **grammar-driven**: the parser matches the quantity greedily against the quantity grammar (mixed number → fraction → decimal → integer) anchored at the start of the braces, and any non-empty remainder after the delimiting space is the unit.
`@butter{1 1/2 tbsp}` → quantity `1 1/2`, unit `tbsp`.
`@butter{1 1/2}` → quantity `1 1/2`, no unit.
A plain "last space" rule cannot express this, because a mixed-number quantity itself contains a space; the grammar is authoritative.
If the brace content does not begin with a grammar-valid quantity (e.g. `@stock{a splash}`), the entire content is preserved as the quantity string and the unit is left empty, so no annotation is dropped.
**Examples**:
```
@@ -87,7 +90,7 @@ $name{quantity unit}
**Quantity**: optional. Follows the same format rules as Ingredient quantity (integer, decimal, fraction, mixed number).
**Unit**: optional, free-form. Same last-space delimiter rule as Ingredient.
**Unit**: optional, free-form. Same grammar-driven quantity/unit split as Ingredient.
**Examples**:
```
@@ -220,4 +223,5 @@ step-num = 1*DIGIT
```
> Note: `unit` in ingredient/cookware is free-form and not captured in the grammar above.
> The last-space rule applies at parse time: everything after the last space inside `{}` is the unit; everything before it is the quantity.
> At parse time the quantity is matched greedily against the `quantity` production anchored at the start of `{}`; any non-empty remainder after the delimiting space is the unit.
> If the content does not begin with a grammar-valid `quantity`, the whole brace content becomes the quantity string and the unit is empty.