5. Your first syntax change¶
New syntax is the most invasive type of change: it touches multiple stages and increases long-term maintenance cost.
This chapter shows how to do it safely and predictably.
RFC required for new language features
If you are proposing a new user-visible language feature (syntax or semantics), write an RFC first:
Bugfixes and chores do not require an RFC.
Before you start¶
Read:
- Extending the language (the builtin-vs-syntax decision and checklists)
Your mental checklist¶
Keep the pipeline aligned (to avoid language/tooling drift):
- Syntax crate (
crates/incan_syntax/): lexer → parser → AST → diagnostics - Formatter (
src/format/): prints AST back (idempotent; never emits invalid syntax) - Semantic core (
crates/incan_core/): canonical vocab / shared semantic helpers (avoid duplicating “meaning” in multiple layers) - Compiler (
src/frontend/,src/backend/):- typechecker validates and annotates
- lowering turns AST into IR
- emission generates correct Rust
- Runtime/stdlib (
crates/incan_stdlib/,stdlib/): behavior that can live outside the compiler should live here
Rule of thumb: prefer pushing shared meaning “down” into incan_core/incan_syntax/incan_stdlib, and keep the incan
(root) crate focused on orchestration and pipeline wiring.
You should expect Rust exhaustiveness errors to guide your work when you add enum variants.
Pick a “first syntax change” that stays small¶
A good first syntax change:
- is local (one new statement or expression form),
- has a simple typing rule,
- emits Rust in an obvious way,
- can be covered by 1–2 regression tests.
Avoid starting with a feature that requires new runtime types, new module system rules, or complex ownership behavior.
Next¶
Next chapter: 06. Tooling loop: formatter + tests.