aboutsummaryrefslogtreecommitdiff
path: root/AGENTS/CONVENTIONS/SYMBOLS_AND_ERRORS.md
blob: e21bed118049d7f722c7e37051fdd5fef271d98c (plain)
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

# Conventions: symbols, dispatch and diagnostics

Part of [Load-bearing conventions](../CONVENTIONS.md).

**One symbol namespace, one table.** `Symbols` (name → `SymbolInfo{Storage, TypeName,
IsArray, ConstantValue, ScopeID, IsReference, IsGlobal, ReadOnly}`) in CodeGen is the
only symbol table.

- Use `symbolAddress` for an object address: class variables and BYVAL class
  parameters store their current object address in a reference slot (`IsReference`).
- Use SymbolInfo::isConstant() for constant checks: scalar literals use
  ConstantValue; owning STRING constants use ReadOnly plus Storage.
- Read values through emitExpr. Writable/address-taking paths reject constants
  through requireWritable; internal STRING copy/assignment source bindings may read
  their storage.
- Register variables through `registerSymbol()` (parameters are also bound directly
  by FunctionGen). `prepareSymbolDeclaration` diagnoses same-scope conflicts
  involving constants before declaration emission.
- Unique scope IDs distinguish sibling blocks; `emitScopedBlock` restores only
  bindings introduced by constants or hiding constants, preserving other variables'
  existing function-wide visibility.
- FunctionGen saves/restores Symbols and inherits visible globals plus previously
  visible top-level constants (ScopeID 0). Locals may shadow globals. CodeGen
  isolates constant scope bookkeeping and array metadata around each function body.
  Arrays also restore metadata at control-flow block exits.

**Designators degrade.** The parser turns `x` / `arr[i]` into the legacy
`VariableExprAST`/`ArrayAccessExprAST` (and their assign statements) and only builds
`DesignatorExprAST`/`DesignatorAssignStmtAST` for chains involving `.`, `^` or
multiple accessors — existing programs keep byte-identical ASTs. `CodeGen::emitLValue`
resolves any of these to `{address, type name}`, emitting index and null-deref checks
on the way; `loadFromLValue` owns the rvalue read of the resolved value.

**Dispatch is by `dynamic_cast` chain, not virtual methods.** `CodeGen::emitStmtImpl`
in `StatementGen.cc` and `emitExpr` in `ExpressionGen.cc` walk linear if-chains of
casts. **A statement type missing from the chain is silently ignored** — no error.
Adding an AST node always means touching these chains. Emit statements through
`emitStmt`, whose wrapper cleans up statement temporaries after dispatch.

**Compiler error style: print an `error:` line, set the component's error flag, and
keep compiling.** No exceptions, no exit mid-compile.

- `Lexer`/`Parser`/`CodeGen` each expose `hadError()`; AST expressions/statements
  share source-line metadata. Parser entry points record token locations. CodeGen
  saves/restores its diagnostic line across recursive emission and coercion.
- Diagnostics use the gcc layout `<path>:<line>: error: <message>` (`<path>:` when
  the line is unknown). Lexer/parser/driver call `cps::sourceError(Line, fmt, ...)`
  (`sourceWarning` for warnings) from `Diagnostics.h` with the bare message: no
  `error:` label, location or newline. `reportError` routes through the same helper
  with `DiagnosticLine`. `DiagnosticSource` holds the unit path as given on the
  command line.
- `CodeGen::reportError(fmt, ...)` is the helper for codegen-side diagnostics
  (`ArrayHandler` reaches it through its `CodeGen &` parameter,
  `FunctionGen`/`ArithmeticHandler` hold a `bool &HadError` reference).
- The driver exits non-zero when any flag is set, printing `compilation terminated.`
  once. Errors that prevent compilation (missing input, file I/O or invalid IR) use
  `fatal error:`. Execution-stage errors do not print the compilation termination
  line.
- The parser drops a failed statement and resyncs at the next token that can start a
  statement or end a block (`syncToStatementStart`), then continues; the failure is
  always diagnosed and reflected in the exit code.