mirror of
https://github.com/fluffos/fluffos
synced 2026-08-12 18:26:06 -04:00
The macro-expansion rescan path recursed one yylex() frame per nesting level, the textual argument pre-expander (lpc_lex_expand_string) recursed per level with per-level guard-vector copies, the #if/#elif evaluator recursed per unary/paren/ternary token, and the lexer's no-token recovery paths (malformed heredocs, over-long $N, template-interpolation close) retried via recursive yylex() -- error REPORTING stops after 5 parse errors but scanning does not, so runs of malformed constructs nested a frame each (a 600KB file of '@' lines segfaulted the driver). All of these are now iterative: - lpc_lex_resolve_identifier returns LPC_TOKEN_RESCAN (no token); the identifier rule falls through and the SAME yylex() frame keeps scanning the pushed expansion buffer -- one Flex buffer per level, zero C-stack growth. - lpc_lex_expand_string is an explicit work-stack machine (one shared guard stack + O(1) name-count lookups instead of per-level copies). - the #if evaluator is an explicit-stack machine (ifexpr_eval), depth bounded by token count on the heap; no cap needed. Keeps the audit's int64_t retyping (LLP64 correctness). - heredoc recovery / @@ splice, over-long $N, and template '}' close fall through instead of recursing (pinned by lexer_retry_chains.lpc, which segfaults the previous binary). This supersedes the 2026-07-20 audit's stack-overflow mitigations for the preprocessor (kMaxIfExprDepth, MAX_EXPANSION_NESTING lowered to 32 under the sanitizer build's measured crash boundary): the recursion itself is gone, so MAX_EXPANSION_NESTING becomes kLpcMaxExpansionNesting = 65535, shared by both expansion engines as a runaway/memory bound and counted in LIVE frames only via a live-index stack (dead same-line provenance frames no longer trip it, so 128+ sequential uses of a macro on one line compile again). deep_nesting_caps.lpc's deep cases now compile -- under ASan included -- instead of being rejected; its comments and AGENTS.md's cap-sizing guidance are updated to the new design. The self-reference guard is an O(1) hash lookup. innermost_real_buffer_index -- behind every current_line read, per matched token -- is O(1) via a maintained include-buffer index stack instead of walking the whole buffer stack (the walk made deep-chain compiles quadratic). Diagnostics keep 16 innermost + 16 outermost expansion notes with an elision marker instead of one note per level. Tests: deep_macro_nesting.lpc (60000-deep chains through both engines, same-line frame accounting, beyond-cap clean error, 262144-token #if shapes), lexer_retry_chains.lpc, deep_ternary_nesting.lpc (parser-stack bound pins). Validated post-rebase on Debug and clang ASan+UBSan full suites (621/621 files each) plus RelWithDebInfo pre-rebase; gtests 320/320. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| conditionals.md | ||
| define.md | ||
| include.md | ||
| index.md | ||
| pragma.md | ||