grammars are values, not strings
summary: when grammars become first-class, the language stops being two languages.
Core idea: treating grammars as runtime values lets parsing, semantic actions, and downstream reactions compose inside one language instead of across generator boundaries.
When grammars are values, the language stops being two languages, and the action body of a parser rule is just a lambda in the host language.
In O, a rule is one line:
.md.h1: <- "#"# .md.sp (!.md.lf \.)* .md.lf/{`h1!.md.concat x};
<- is the rule arrow; the trailing /{...} is the action lambda. That action is a normal O lambda. It can call kernels, build columns, fire reactions - whatever the language can already do. A code-generated parser cannot do this, because the action body is interpreted by a generator that does not know what the host language is.
The other thing the value-shape gets you is composition without ceremony. A larger rule embeds a smaller one by name. A reaction can accept "the grammar to apply" the way a sort can accept "the comparator to use". A new venue ships a new tag at 09:30 UTC and the grammar is one line away from a redefinition.
If you have ever owned a parser from a code generator, you know exactly which friction I am describing the absence of.