Say what a name may contain, and what a calc operand reads

The Computation section carried the caveat this branch removes — that a field
shown and used in a calc is drawn twice — so it now describes the hold
instead. The name rules name all four reserved characters rather than the
dot alone, in both the Data and Data format sections, and the alternation
entry says a repeated arm is a load error and points at weight.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
M
2026-08-30 23:17:46 +02:00
committed by lilleman-tw
parent 9ed0d3bf55
commit ac0309cf94
+21 -12
View File
@@ -153,8 +153,9 @@ A `*Fakes` is **not** safe for concurrent use — create one per goroutine.
The library ships a ready-to-use set under [`data/`](data): one folder per locale The library ships a ready-to-use set under [`data/`](data): one folder per locale
(`en_US`, `sv_SE`) plus a locale-neutral `misc` folder. Point either tool at the (`en_US`, `sv_SE`) plus a locale-neutral `misc` folder. Point either tool at the
whole tree, a single folder, a copy, or your own directory — anywhere on disk. A whole tree, a single folder, a copy, or your own directory — anywhere on disk. A
category or folder name must not contain a dot, and dot-prefixed entries are category or folder name must not use `.`, `|`, `(` or `}` (see [Data
skipped, so a data directory can also be a checkout. format](#data-format)), and dot-prefixed entries are skipped, so a data directory
can also be a checkout.
A directory is just a namespace. Each JSON file is a category named after the A directory is just a namespace. Each JSON file is a category named after the
file; each subdirectory is a dot-path segment — folders nest exactly like JSON file; each subdirectory is a dot-path segment — folders nest exactly like JSON
@@ -235,8 +236,10 @@ This yields e.g. `bar foo baz`. `repeat` must be a positive integer and
is a field**. So write `seperator` and you get a field by that name while the is a field**. So write `seperator` and you get a field by that name while the
option stays unset. `New` rejects an option that cannot take effect — a option stays unset. `New` rejects an option that cannot take effect — a
`separator` without a `repeat` above 1, a `weight` outside a choice — and a name `separator` without a `repeat` above 1, a `weight` outside a choice — and a name
containing a dot: a category or folder no dot path could reach, or a field whose using a character the grammars reserve: `.` separates the segments of a path, `|`
dot a token would read as a path (see **Correlated fields**). the arms of a token, `(` opens a function call and `}` ends the token, so a name
carrying one is a name no format could ever spell. That holds for a category, a
folder and a field alike.
**Functions.** A `{name()}` token calls a built-in function instead of rendering **Functions.** A `{name()}` token calls a built-in function instead of rendering
a field. `{luhn()}` appends a Luhn check digit over the digits emitted **so far** a field. `{luhn()}` appends a Luhn check digit over the digits emitted **so far**
@@ -309,11 +312,13 @@ hyphenated field name can't be an operand. The expression is checked at `New`
{ "format": "{net} x {qty} = {calc(net * qty, 2)}", "net": ["19.99"], "qty": ["3"] } { "format": "{net} x {qty} = {calc(net * qty, 2)}", "net": ["19.99"], "qty": ["3"] }
``` ```
renders `19.99 x 3 = 59.97`. Each name is rendered where it appears, so a field renders `19.99 x 3 = 59.97`. A field a `calc` reads is drawn **once per
shown *and* used in a `calc` is drawn twice — keep a shared operand in a expansion** and held, so the operand shown is the operand computed — give `net`
single-value field if the two must agree. A field that doesn't render to a number three prices and the line still multiplies the one it printed. The hold covers
yields `NaN`, and a division by zero yields `Inf`; both print rather than failing every reading of that name in the format, so `{w} {w} {calc(w)}` is one value
the render. three times; a name no `calc` reads is unaffected, and `{word} {word}` still draws
twice. A field that doesn't render to a number yields `NaN`, and a division by
zero yields `Inf`; both print rather than failing the render.
**References.** A `{..path}` token renders a node from the **data root** instead **References.** A `{..path}` token renders a node from the **data root** instead
of a sibling field — the dot path is the one `Fake` takes, resolved across every of a sibling field — the dot path is the one `Fake` takes, resolved across every
@@ -348,8 +353,10 @@ beside Tranås. Each row carries its own `weight`, so how often a place appears
too. The rule holds both ways: two tails of one head come from the same row, and too. The rule holds both ways: two tails of one head come from the same row, and
one path read twice reads one value (`{p.first} … {p.first}@…` gives one name). one path read twice reads one value (`{p.first} … {p.first}@…` gives one name).
**One draw, one spelling.** A format may not both *render* a level and *read a **One draw, one spelling.** The hold above is what a dotted token reads, and a
path into* it — `{p}` beside `{p.first}`, or `{place}` beside `{place.locality}`. `{calc()}` operand reads its sibling the same way (see **Computation**). A format
may not both *render* a level and *read a path into* it — `{p}` beside
`{p.first}`, or `{place}` beside `{place.locality}`.
Rendering a level expands it afresh while a path reads the level's held draw, so Rendering a level expands it afresh while a path reads the level's held draw, so
the two would disagree; naming the fields you want is the one spelling that the two would disagree; naming the fields you want is the one spelling that
always agrees, and the other is a load error. This covers every way a level can always agrees, and the other is a load error. This covers every way a level can
@@ -404,7 +411,9 @@ renders, and `List` advertises it.
| `{..path}` | render the node at a dot path from the data root (see **References**) | | `{..path}` | render the node at a dot path from the data root (see **References**) |
`{a|b}` renders one of the sibling fields `a` or `b`, chosen at random; an arm `{a|b}` renders one of the sibling fields `a` or `b`, chosen at random; an arm
may be a `{..path}` reference too (`{name|..en_US.person}`). may be a `{..path}` reference too (`{name|..en_US.person}`). The arms are picked
evenly and must differ — `{a|a|b}` would skew the odds, which is what `weight` is
for, so a repeated arm is a load error.
Inside a `format`, `0 1 A a` are **always** character classes — so a fixed `0`, Inside a `format`, `0 1 A a` are **always** character classes — so a fixed `0`,
`1`, `A` or `a` must be escaped (`#1`, `#A`) or it becomes random. A format of `1`, `A` or `a` must be escaped (`#1`, `#A`) or it becomes random. A format of