diff options
| author | _ov4 <ov4@ov4.org> | 2026-07-26 12:07:51 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-07-26 12:07:51 +0000 |
| commit | 4a25b279d8f6c662fa8152cfe42e038d1ea06901 (patch) | |
| tree | 58223f2db133a10827e0a6d0c18c33f04bbc2527 /AGENTS | |
| parent | efc86b5c7625cf4c7074319f42f68807a25bde9d (diff) | |
refactor: keep pseudocode signatures, single call-marshalling path
emitPrototype now records each FUNCTION/PROCEDURE's pseudocode signature
(FuncSig: return type + per-param type/BYREF flag). CodeGen::
marshalCallArgs replaces the two verbatim-duplicated marshalling blocks
and dispatches on the declared signature instead of inferring BYREF from
pointer-typed LLVM params: BYVAL STRING params now receive the string
data pointer (no more runtime garbage), and BYVAL args are coerced to
the declared type (INTEGER->REAL args no longer emit invalid IR).
Top-level prototypes are pre-registered in compile(), so calls may
precede definitions; GenerateCall no longer fabricates i64 externs for
unknown names - an unregistered callee is a compile error. OUTPUT
reports an error when it cannot infer the expression type instead of
printing with the INTEGER format.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'AGENTS')
| -rw-r--r-- | AGENTS/CONVENTIONS.md | 19 | ||||
| -rw-r--r-- | AGENTS/PITFALLS.md | 3 | ||||
| -rw-r--r-- | AGENTS/SUBSYSTEMS.md | 21 |
3 files changed, 22 insertions, 21 deletions
diff --git a/AGENTS/CONVENTIONS.md b/AGENTS/CONVENTIONS.md index c05734f..e09b6ee 100644 --- a/AGENTS/CONVENTIONS.md +++ b/AGENTS/CONVENTIONS.md @@ -58,16 +58,15 @@ non-zero when any flag is set. The parser still drops a failed statement, skips token, and continues — later statements can be swallowed during recovery, but the failure is always diagnosed and reflected in the exit code. -**BYREF is a pointer-typed parameter, inferred structurally at call sites.** -`FunctionGen` emits pointer params for BYREF; call sites (in CodeGen, duplicated for -call-expr and call-stmt) treat *any* pointer-typed callee param as BYREF and require the -argument to be a bare variable whose alloca is passed. The callee side, however, binds -by the *declared* flag. Since STRING itself lowers to `ptr`, the two sides disagree for -STRING: the caller always passes the alloca, but a declared-BYVAL STRING param (the -default) copies that alloca *address* and treats it as the string data pointer — reading -garbage at runtime. Only declared-BYREF STRING parameters work, and STRING arguments can -never be literals/expressions. Function names get no mangling — a pseudocode -`FUNCTION printf` collides with libc. +**Calls dispatch on the declared signature.** `FunctionGen::emitPrototype` records every +function's pseudocode signature (`FuncSig`: return type name + per-param type name and +BYREF flag); `CodeGen::marshalCallArgs` — the single call-marshalling path for both +call-expr and call-stmt — requires BYREF arguments to be bare variables (their alloca is +passed) and coerces BYVAL arguments to the declared parameter type. STRING works on both +sides in both modes. Top-level prototypes are pre-registered before statement emission, +so call-before-definition is fine; a call with no registered signature is a compile +error. Function names get no mangling — a pseudocode `FUNCTION printf` collides with +libc. **Builtins are plain identifiers backed by one table.** All 11 builtins (LENGTH/MID/RIGHT/LEFT/LCASE/UCASE/ASC/CHR/IS_NUM/NUM_TO_STR/STR_TO_NUM) parse as diff --git a/AGENTS/PITFALLS.md b/AGENTS/PITFALLS.md index b068dee..393f507 100644 --- a/AGENTS/PITFALLS.md +++ b/AGENTS/PITFALLS.md @@ -20,8 +20,7 @@ test programs. bits. - **`getExprTypeInfo` returns nullptr** for expressions it can't classify (null-check it at any new call site) and internally defaults to INTEGER for arithmetic binaries and - unknown-function calls; OUTPUT substitutes INTEGER for a nullptr result — so wrong - inference prints garbage instead of erroring. + unknown-function calls; OUTPUT reports an error for a nullptr result. - The lexer emits no newline tokens; the grammar is purely keyword-delimited. Bare `RETURN` is only recognized right before ENDIF/ELSE/ENDFUNCTION/ENDPROCEDURE. - Invalid modules still print their IR, but `verifyModule` runs at the end of diff --git a/AGENTS/SUBSYSTEMS.md b/AGENTS/SUBSYSTEMS.md index 2910bb3..f26cddc 100644 --- a/AGENTS/SUBSYSTEMS.md +++ b/AGENTS/SUBSYSTEMS.md @@ -6,9 +6,10 @@ known bugs: `PITFALLS.md`.) **Arrays** (`ArrayHandler`): a DECLAREd array is one malloc'd flat buffer (memset to zero), row-major, any rank, any bounds. Bounds and multipliers are *compile-time SSA values* cached in `ArrayMetadata` (global `ArrayTable` map, keyed by name). Every access -goes through `getMetadata` → per-dimension `emitIndexCheck` → `computeFlatIndex` → -`getElementPointer`; never hand-roll indexing. STRING elements read as null until -written; every read path must replicate the null-guard `select` to the empty string. +goes through `emitCheckedIndices` (validates the index count for the caller's semantics, +then per-dimension coerce + `emitIndexCheck`) → `computeFlatIndex` → `loadElement` +(load + the STRING null-guard `select` to the empty string); never hand-roll indexing +or element loads. `OUTPUT arr` (and partially-indexed accesses) is intercepted by `ArrayHandler::tryEmitArrayOutput` — the first branch of OUTPUT dispatch, emitting nested print loops — so any rework of OUTPUT must preserve that interception. @@ -17,12 +18,14 @@ nested print loops — so any rework of OUTPUT must preserve that interception. name. Params are `tuple<name, typeString, isByRef>`. BYVAL args are copied into entry-block allocas; BYREF args bind the incoming pointer directly as the variable's storage. Missing RETURNS defaults the return type to INTEGER; procedures use "VOID". -**Definitions must precede first call**: calling a not-yet-defined function fabricates -an extern declaration (i64 return, param types guessed from the arguments), and when the -real definition arrives, `emitFunctionDef` reuses that declaration by name without -checking the signature — the body attaches to the wrong prototype and the module is -invalid. Body emission is inverted: CodeGen injects a `StmtEmitter` callback, and must -restore its own insert point afterwards. +`emitPrototype` records each function's pseudocode signature in a `FuncSig` map +(`FunctionGen::getSignature`); `CodeGen::marshalCallArgs` dispatches call arguments on +those *declared* param types and BYREF flags (BYVAL args are coerced to the declared +type), and `getExprTypeInfo` reads return types from the same map. Top-level prototypes +are pre-registered at the start of `compile()`, so calls may precede definitions; +calling a name with no signature is a compile error (no extern is fabricated). Body +emission is inverted: CodeGen injects a `StmtEmitter` callback, and must restore its +own insert point afterwards. **Runtime checks** (`RuntimeCheck`): only array bounds checking is actually wired up. `emitDivZeroCheck` exists but has **zero call sites** — division by zero (for `/`, DIV |
