Skip to content

Keep a compiler directive that comes before the unit keyword - #6

Merged
partouf merged 2 commits into
mainfrom
fix/directive-before-unit
Oct 8, 2026
Merged

partouf merged 2 commits into
mainfrom
fix/directive-before-unit

Conversation

@partouf

@partouf partouf commented Oct 8, 2026

Copy link
Copy Markdown
Member

Bug. A switch directive before the unit keyword fails the whole parse:

{$WARN UNIT_PLATFORM OFF}
unit T;
interface
implementation
end.

raises EListError: Unbalanced stack or queue operation. Delphi accepts it, and real code has it: in the Dev-C++ 4.9.9.2 sources, 9 units start with {$WARN ...}, {$D+}, {$OPTIMIZATION off}, {$APPTYPE GUI} or similar. The same directive after unit T; parses fine, and so does a conditional ({$IFNDEF X} unit Y; {$ENDIF}), because conditionals never reach CompilerDirective.

Cause. The parser reads the first token before UnitFile pushes the root node, so CompilerDirective runs with an empty stack and FStack.Peek raises.

Fix. Hold such a directive in FLeadingDirectives until the root exists. PushRootNode, now shared by UnitFile, ProgramFile, LibraryFile and PackageFile, creates the root and adopts them, so they sit first under it in source order. That is where a directive right after unit X; already goes, since GetMainSection returns the root for a node without a parent. Run frees any left over from a parse that failed before reaching the keyword.

Test. AST.DirectiveBeforeUnit is added to Test/UnitTests. It fails on main and passes with this change. All 9 Dev-C++ units parse with it.

Serialization.BinaryRoundTrip fails with and without this change under Delphi 10.4, as noted in #5.

🤖 Generated with Claude Code

Partouf and others added 2 commits October 8, 2026 19:41
A switch directive before `unit` - {$WARN UNIT_PLATFORM OFF}, {$D+},
{$APPTYPE GUI}, all accepted by Delphi - failed the whole parse with
EListError "Unbalanced stack or queue operation".

The parser reads the first token before UnitFile pushes the root node, so
CompilerDirective ran with an empty stack and its FStack.Peek raised.
Conditional directives never reach CompilerDirective, which is why
{$IFNDEF X} unit Y; {$ENDIF} was fine.

Hold such a directive until the root exists. PushRootNode, now shared by
UnitFile, ProgramFile, LibraryFile and PackageFile, creates the root and
adopts them, so they sit first under it in source order - where a
directive right after `unit X;` already goes, since GetMainSection returns
the root for a node without a parent. Run frees any left over from a parse
that failed before reaching the keyword.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AST.DirectiveBeforeUnit parses a unit that starts with {$WARN ...} and
{$D+} and checks that both end up first under the ntUnit root, in source
order, and that the rest of the unit still parses. It fails on main and
passes with the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@partouf
partouf merged commit a983ca6 into main Oct 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant