1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
# 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 + keyword match in `Lexer.cc`'s if-chain →
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. Ordinary string literals need STRING.NEW(C_STRING); the compiler bridges OUTPUT/casts/files through C_STR(), INPUT through READ_INPUT(), and READFILE through assignment from C_STRING.
|