Migrating from removed constructs
Chord's grammar is a one-way ratchet: constructs that were removed on
the way to v1 are not merely undocumented, they are actively refused.
Each removed form produces a specific parse.* error whose message
names its replacement, so a story written against an older sketch fails
loudly and points the way, rather than misbehaving quietly. Every row
below is backed by an expected-to-fail fixture in this reference's
harness, which asserts the exact code still fires.
| Removed form | Diagnostic code | Replacement |
|---|---|---|
top-level when <actor> <verb>s … rule | parse.removed-when | an on/after clause on the owner |
top-level once <condition> rule | parse.removed-once | the , once clause modifier |
top-level every N turns rule | parse.removed-every | a define sequence or every turn clause |
define flag <name> | parse.removed-flag | owner states: or a derived condition |
flag trait-field type | parse.removed-flag-field | trait states |
if / else / end if | parse.removed-if | must, the when suffix, or select |
top-level define score … worth N | parse.removed-score | an owner-attached score line |
ordered / once phrase strategy | parse.phrase-strategy-retired | stopping / first-time |
on <verb> it / after <verb> it clause head | parse.removed-head-it | on <actor> <verb> — the head names who acts |
it / its in a statement, condition, or possessive | analysis.it-removed | the owner's name; still the carrier inside define trait |
create the player | parse.removed-create-player | a named character, playable, and a before the game starts block |
a create block with no kind, or a trait before the kind (Chord 4.0) | analysis.missing-kind-noun / analysis.kind-not-first | the kind as the block's first line — a thing, a room, a container, openable |
define text <name> from "<module>" (Chord 4.0) | parse.removed-text-hatch | a define phrase, with a strategy (cycling, first-time, …) if the text varies |
define action <name> from "<module>" (Chord 4.0) | parse.removed-action-hatch | a define action block written in Chord |
define chain <name> from "<module>" (Chord 4.0) | parse.removed-chain-hatch | none — the behavior needs support in the language; say so in an issue |
a region containing rooms, or nested regions (Chord 4.0) | parse.removed-containing | the region's rooms group; a room's in <region> membership |
use state-machines (Chord 4.0) | analysis.extension-core | nothing — define machine is core; remove the line |
a container with max items N / with max weight N (Chord 4.0) | parse.removed-capacity-setting | the holds <n> line |
a one-line passage <words> to <room> (Chord 4.0) | parse.passage-line-form | the passage to <room> … end passage block with its grammar |
passage: on a vehicle (Chord 4.0) | parse.passage-is-pace | pace: — a flight's trip time |
The through-line is stickiness (design.md, given 9): every one of these
removals took a floating, top-level behavior and re-homed it on the thing
it is actually about. A when rule floated beside the world; an on
clause lives on the room it reacts in. A global flag floated in no
one's namespace; a states: line lives on its owner. The migration is
almost always "find the thing this was really about, and move it there."
The three newest rows sharpen the same idea one turn further. A clause used to
say what it acted on and leave who acted implicit; now the head names the
actor and the block it sits in supplies the object. it was a pronoun with no
antecedent on the line, so a clause could not be read alone; now the owner is
written, except inside a define trait, where the carrier has no name to
write. And the player stopped being a block to become a role a named character
holds — see The player role.
The Chord 4.0 rows are the first since 3.0.0 to turn the ratchet on the
language's own closed syntax rather than on a floating construct. A
create block names its kind first because the kind is what the rest of
the block is read against; the three hatch forms are gone because a story
that reaches into TypeScript is no longer a Chord story, and what Chord
cannot say is a language gap to close, not a hole to climb through; a region
declares its rooms because membership belongs on the region, not in a
nesting; use state-machines and the capacity settings are refused
because the vocabulary is core and holds is the one spelling; and a
passage is a block with a grammar, so the one-line form and a vehicle's
passage: line both have a named replacement.
The richest case is if, because a single removed construct fans out to
three replacements. This no longer parses:
on the player opening
if the door is locked-fast
phrase clunk
end if
end on
It reports parse.removed-if, and which replacement you want depends on
what the if was doing. If it was guarding the whole action (this may
not proceed unless a condition holds), it becomes a must requirement. If it was making one statement
conditional, it becomes that
statement plus a when suffix (phrase clunk when the door is locked-fast).
If it was choosing between several
bodies by a value, it becomes
a select. The removed if had all three
jobs; Chord gives each
its own, clearer form.
The formal grammar
The complete grammar (productions, layout rules, and the ratchet history) lives in the Chord grammar reference, with the machine-readable EBNF downloadable there.