# Extension recipes Step-by-step patterns for adding features. Read [CONVENTIONS.md](CONVENTIONS.md) first — every recipe below assumes those invariants. **New statement:** token in `Lexer.h` enum + entry in `Lexer.cc`'s Keywords table → node class in `AST.h` or a feature header (`FunctionAST.h`/`FileAST.h` precedent; unique_ptr children, move ctor, raw-ptr getters) → `ParseXxx` in the matching `Parser*.cc` file + branch in `ParseStatementImpl` (block statements parse their body with `ParseBlock({terminator tokens})`; control-flow parsers live in `ParserControlFlow.cc`; comma-separated expression lists use `ParseExprList(close, allowEmpty)`; add the new statement's leading keyword to `syncToStatementStart`) → `dynamic_cast` branch in `CodeGen::emitStmtImpl` in `StatementGen.cc`. Keep calls through the `emitStmt` wrapper so statement temporaries are cleaned up. Emit statement bodies through `emitScopedBlock` so constants cannot escape their lexical block. Guard every fall-through branch in control-flow codegen with `if (!Builder->GetInsertBlock()->hasTerminator())` — a RETURN in the body may have already terminated the block. **New AST nodes must be cloned in `lib/CodeGen/TemplateClone.cc`.** Its dynamic_cast chains deep-clone every expression and statement permitted in a template body, preserving source lines. Add structural type substitution for each new type-bearing field and expand packs only through the argument-list helper. Unsupported nodes must diagnose rather than disappear; nested function definitions remain forbidden in template bodies. **New builtin function:** add a `{Name, Arity, ReturnTypeName}` row to the shared `Builtins` table in the internal `lib/CodeGen/Builtins.h` (single source of truth — arity checking in `emitCall`, the return type in `getExprTypeInfo`, and the reject-user-redefinition check all read it), then implement IR emission as a method on the fitting handler (for example FileHandler) or inline, and add the name intercept in `CodeGen::emitCall` — null-check every emitted argument before use, like the existing branches do. `emitCall` lives in `ExpressionGen.cc`. Builtins are plain identifiers — nothing is needed in lexer/parser. **New binary operator:** token (if multi-char) → one line in `BinopPrecedence` (Parser ctor; levels: OR=3, AND=5, comparisons=10, `+ - &`=20, `* / DIV MOD`=40; precedence must be > 0) → branch in the shared `CodeGen::emitBinaryOp` value dispatcher in `ExpressionGen.cc` or its `ArithmeticHandler::emitBinaryOp` leaf, which dispatches on raw LLVM operand types. Preserve overload selection and C/user-kind checks before reaching that leaf. STRING concatenation and comparisons are ordinary library operator overloads; implement string operations in pseudocode-lib rather than adding compiler builtin branches. **New builtin type:** register in `TypeSystem::registerBuiltins` in `NativeTypeSystem.cc` (sizes are derived — never hand-written) + `TypeKind` case in the **exhaustive, default-less switches**: `getZeroValue`, `coerceValueToType`, `emitOutputValue`, `TypeInfo::isOutputtable`, the INPUT branch in CodeGen + lexer keyword + `ParseTypeName` case in `ParserType.cc`. The compiler warns on a missing enumerator — that is the anti-silent-fall-through mechanism; never add a `default`. **New user-defined type KIND (like enum/record/pointer):** payload struct + checked accessor in `TypeInfo`, `declareUserType`/`defineXxx` pair in TypeSystem (two-pass: names then definitions), decl AST node in `TypeAST.h`, `ParseTypeDecl` branch, pre-pass branch in `CodeGen::runTypePrePass`, admissibility rules in `emitCoercedExpr` + `emitUserKindBinaryOp`, and a decision for every hole: OUTPUT/INPUT/WRITEFILE/file-name operands, FOR variables, builtin args, `getExprTypeInfo` classification. Everything not explicitly admitted must be a compile error. **Native C types:** add representation/format metadata in `NativeTypeSystem.cc`'s registration, using compiler-derived facts rather than fixed widths. Preserve canonical CIdentity and alias compatibility. Update CTypeGen conversions, promotions and I/O together; never let native C operands fall through to the legacy ArithmeticHandler or representation-only coercions. C_TYPE parses a string import, not an ordinary function call. Casts are CastExprAST values; their type must be recognized by both emission and getExprTypeInfo. **New designator access form:** `AccessKind` case in `TypeAST.h` + `ParseDesignatorSuffix` + the walk in `CodeGen::emitLValue` **and** the static walk in `getExprTypeInfo`'s designator branch (they must agree) — plus the degrade rule in `ParseIdentifierExpr`/`ParseAssignStmt` if the new form can collapse to a legacy node. **New runtime check:** add `emitXxxCheck` in `RuntimeCheck` (build an i1 fail-condition and call `emitCheck` with a diagnostic format and arguments — it splits the block and leaves the builder at the continuation), then call it from the emit site. Failure prints the source line and error reason to stderr before throwing an anonymous exception; generated main supplies the uncaught message and status 1. Precedents: `emitIndexCheck`, `emitEnumRangeCheck`, `emitNullDerefCheck`, `emitArrayArgBoundsCheck` and FileHandler's direct `emitCheck` calls. **New potentially throwing call:** route it through `ExceptionHandler::emitCall` so it invokes the current handler or catch cleanup when needed. Give generated functions unwind tables via `prepareFunction`. Exception ABI dependencies must also be registered in EmbeddedRunner for static-runtime JIT symbol lookup. **New libc dependency:** declare via `getOrInsertFunction` in the owning component's `setupExternalFunctions()` (re-declaring the same symbol in two handlers is fine — it dedupes by name). **Standard-library functions:** define ordinary functions/operators in the appropriate pseudocode-lib/*.cps unit with C ABI helpers in the matching .cc as needed. std.cps imports all public library units. CMake rebuilds and embeds source/object changes; do not add standard-library functions to Builtins.h. Memory allocation functions live in memory.cps/memory.cc; only keyword NEW/DELETE need the MemoryGen bridge. Double-quoted literals produce C_STRING; STRING contexts require marked IMPLICIT NEW(C_STRING), while CHAR uses marked IMPLICIT NEW(CHAR); the compiler bridges OUTPUT/casts/files through C_STR(), INPUT through READ_INPUT(), and READFILE through assignment from C_STRING.