Grammar tooling explanation
FCSTM syntax support is deliberately multi-layered. ANTLR defines parseable syntax, listener code turns parse trees into repository AST nodes, model import gives those nodes semantic meaning, and separate highlighters/editors make the same syntax usable in documents and authoring tools.
Why one syntax change touches several layers
A grammar change is not complete when a .g4 file accepts new text. Users see
syntax through command errors, model diagnostics, rendered documentation, VSCode
highlighting, completion suggestions, hover text, snippets, and LLM-facing
prompts. If one layer is updated and another layer is stale, the project teaches
contradictory language rules.
The maintenance invariant is therefore:
grammar decides what can parse;
listener and AST nodes decide what syntax shape is represented;
model import decides what the syntax means;
diagnostics decide how invalid or risky forms are explained;
Pygments and TextMate decide how humans visually recognize syntax;
editor features decide how authors discover and repair syntax while typing;
documentation and tests decide what users can rely on.
Parser grammar versus semantic model
The parser should remain a syntactic recognizer. It can reject malformed token orders and missing punctuation, but it should not own every semantic rule. For example, resolving whether a transition target exists depends on the state tree, so that belongs in model import and validation rather than in a parser rule.
This separation keeps grammar readable and makes diagnostics more useful: syntax errors can point to malformed text, while semantic diagnostics can point to resolved model facts such as duplicate states, illegal transitions, or unresolved references.
Generated parser boundary
ANTLR-generated files are regeneration outputs. Hand-editing them creates a
fork that disappears on the next make antlr_build. Manual behavior belongs
in listener, node, model, diagnostics, or editor code. This makes regeneration a
safe maintenance step instead of a risky rewrite.
Highlighting is not validation
Pygments and TextMate highlighters classify text for human readability. They should track grammar facts, but they should not become a second parser. A highlighter may conservatively color ambiguous text; it must not advertise a keyword, operator, or block form that the parser rejects.
The practical rule is: highlighters help a reader see the structure, while parser/model tests prove that the structure is accepted and meaningful.
Why Pygments and TextMate both exist
Pygments serves Python and documentation contexts: Sphinx pages, terminal formatters, notebooks, and other Python tools. TextMate serves editor contexts: VSCode and other TextMate-compatible editors. Their consumers and syntax formats are different, so both need explicit maintenance even when they represent the same language.
VSCode role
The VSCode extension combines TextMate highlighting with parser-backed and metadata-backed authoring features: diagnostics, symbols, completion, hover, snippets, and preview support. It is an authoring aid, not the canonical runtime or model importer. When VSCode disagrees with Python parsing, Python grammar and model behavior remain authoritative, but the editor must be fixed so authors do not learn the wrong language.
Documentation and LLM guide role
User documentation explains the supported language in four roles: tutorial, how-to, explanation, and reference. The packaged LLM grammar guide is a compact prompt-facing grammar source. Syntax changes that matter to users should update both the human docs and the prompt-facing guide so human readers and LLM repair flows receive the same rule set.
Test boundary rationale
Python and JavaScript/editor tests stay independent. If both sides need the same syntax scenario, duplicate the minimal DSL fixture in each side’s own test tree. This avoids a Python unit test depending on Node.js and avoids a JavaScript editor test depending on repository-level Python fixtures.
Drift examples
Drift |
Why it is harmful |
Correct response |
|---|---|---|
Parser accepts a new keyword but highlighters do not color it. |
Documentation and editor views make valid syntax look suspicious or plain. |
Update Pygments, TextMate, and editor validation. |
Highlighter advertises an operator the parser rejects. |
Users copy examples that cannot parse. |
Remove or correct the highlighter pattern and add a negative example if needed. |
VSCode completion suggests a syntax form that model import rejects. |
Authors receive false confidence before command-line validation fails. |
Update completion and diagnostics to match parser/model behavior. |
LLM guide omits a new syntax form. |
LLM-assisted repair keeps generating older or incomplete DSL. |
Update the guide and checksum with the syntax docs. |
Generated parser output is edited by hand. |
The fix is lost at the next regeneration. |
Move behavior to source files and regenerate outputs. |
Trace example: adding one keyword
A one-word syntax change is not a one-file change. Suppose a new lifecycle-like keyword were added. The safe reasoning path is:
Grammar decides whether the token can be parsed.
Listener and AST nodes decide where the parsed fact is stored.
Model import decides whether the fact has valid semantics.
Pygments and TextMate decide whether humans see the same token class.
VSCode decides whether authoring tools suggest, diagnose, and explain it.
Human docs and the LLM grammar guide decide whether readers and repair prompts learn the same rule.
Tests on each side prove the Python and JavaScript/editor boundaries without sharing test-only fixtures.
This is why the completion standard is deliberately broader than make
antlr_build. Regeneration proves that generated parser code is current; it does
not prove semantic import, highlighting, editor behavior, or prompt guidance.
Boundary examples: what each symptom means
The same visible symptom can come from different layers. Use concrete reproductions and repair ownership instead of guessing.
Symptom |
Minimal reproduction |
Likely owner |
Correct first repair |
Incorrect shortcut |
|---|---|---|---|---|
A valid command-line file is not highlighted. |
|
Pygments, TextMate, or VSCode language registration. |
Update syntax display assets and run editor validation. |
Changing parser grammar just to influence colors. |
VSCode completion suggests invalid syntax. |
Insert the completed snippet and run |
VSCode completion or snippets. |
Remove or fix the suggestion and add a focused editor test. |
Loosening parser/model validation to accept a bad suggestion. |
A new parser rule compiles but model import loses data. |
Parse succeeds, but inspect output omits the new event, action, or transition fact. |
Listener, AST node, or model importer. |
Add an AST/model test that checks the represented fact. |
Only updating documentation because the grammar accepts the text. |
Documentation example fails after a grammar change. |
Copy the documented FCSTM block into a file and run |
The page and its generated example resources. |
Update the example or explicitly mark it as an invalid counterexample. |
Leaving the stale example because Sphinx still builds. |
LLM repair keeps generating old syntax. |
The human docs are updated, but |
Prompt-facing grammar guide and checksum. |
Update the guide, run |
Treating the human documentation update as enough for prompt users. |
These examples are intentionally not phrased as one universal command. The right check depends on which boundary changed. A parser-only patch should not be forced to run VSCode packaging, but a syntax-support patch that claims editor parity must include editor evidence.
Completion standard
A grammar/tooling change is complete when each affected surface tells the same story:
the grammar parses the intended syntax;
AST/model import represents the intended meaning;
diagnostics reject invalid forms with useful messages;
highlighters and editor tooling recognize the visible syntax;
user docs and the LLM guide teach the same rule;
tests or explicit validation commands cover the changed surfaces.