Default Text file surface and file-handle IO dispatch
- Type: feature
- Status: done
- Owner: Track A
- Opened: 2026-06-20
- Relation: Track A/B boundary follow-up for
lib-text-file-io-assign-rewrite; needed byexamples/adventure.
Problem
The RTL can now provide a PAL-backed Text record and library procedures, but
FPC-style source expects the text-file surface to be available without an
explicit uses textfile, and ReadLn/WriteLn are lexer keywords. Calls like
these are either unresolved (Assign) or intercepted by the compiler before
ordinary procedure resolution:
WriteLn(f, 'room=' + Player.RoomId);
ReadLn(f, line);
Today examples/adventure still stops at Assign being undefined. Once the
surface is visible, the builtin console lowering will also need to recognize a
file-handle first argument instead of treating all arguments as console values.
Scope
- Expose the default Pascal text-file surface via a small, table-driven
implicit RTL import, not by moving
textfileintocompiler/builtin. References to standard text-file names such asText,TextFile,Assign,AssignFile,Reset,Rewrite,Append,Close,CloseFile,Eof, andIOResultshould make the compiler loadlib/rtl/textfile.pasas if it were an implicit unit. - Keep
textfileas a normal RTL unit backed by PAL. Do not make host file I/O part of the frozen builtin payload; bare/embedded targets must remain able to stub or omit the platform backend cleanly. - Ensure
Text,Assign/AssignFile,Reset,Rewrite,Append,Close/CloseFile,Eof, andIOResultresolve for FPC-style demo code. - Detect
ReadLn/WriteLn/Writewith first argument of typeText. - Lower those forms to the RTL text-file routines, or relax keyword interception enough that overload resolution can bind them normally.
- Preserve existing console
ReadLn/WriteLnbehavior andWriteLn(StdErr, …).
Acceptance
- A program using
var f: Text; Assign(f, path); Rewrite(f); WriteLn(f, 'x'); Close(f); Reset(f); ReadLn(f, s);without a special local wrapper round-trips through the PAL-backed RTL. - Existing console read/write tests continue to pass.
examples/adventuregets past the current file-IO keyword forms.
Log
- 2026-06-20 - Opened while adding
lib/rtl/textfile.pas. The Track B RTL primitives exist with explicitTextReadLn/TextWriteLn; this ticket covers the remaining default-surface and compiler-dispatch work needed for FPC-style syntax. - 2026-06-21 - Direction clarified: keep implementation in
lib/rtl/textfile.pasand add a compiler-owned implicit-RTL import table for the classic text-file surface. Avoid moving textfile intocompiler/builtin; only keyword dispatch and default-name visibility belong in the compiler. - 2026-06-21 - DONE (Track A). Implemented per the clarified direction;
lib/rtl/textfile.pasunchanged (still PAL-backed, never frozen into builtin). Compiler side:- Implicit import: the
ParseProgramtoken pre-scan setsneedsTextfilewhen aText/TextFiletoken appears in TYPE position (preceded by:, soobj.Textfield access andText: AnsiStringfield decls do NOT trigger — the latter matters because the compiler's ownTStrEntry.Textfield would otherwise pull textfile into the self-host). When set,ParseUsesUnit ('textfile')runs up front alongside the builtin/heap units, BEFORE declaration parsing — sovar f: Textresolves and a proc-local Text gets normal managed-field (Name: AnsiString) zero-init. (An earlier attempt that loaded textfile mid-parse fromParseTypeKindcorrupted the enclosing proc's codegen state and skipped that zero-init → segfault; the up-front load is the fix.)needsTextfilealso forcesneedsBuiltin(numeric file writes format via StrInt/StrFloat). ESP (xtensa/riscv32) excluded. - Keyword dispatch:
TextIOFileSymdetects a BARE Text handle as the first Write/WriteLn/Read/ReadLn argument (next token,/)—WriteLn(f.Name)stays a console write).write[ln](f, ...)lowers to a sequence ofTextWritecalls with the final value viaTextWriteLn(single trailing newline); strings pass through, integers/floats go via StrInt/StrFloat.read[ln](f, ...)lowers toTextReadLnper destination. Console Write/WriteLn/WriteLn(StdErr,…)paths are untouched when no Text record is in scope (IsRecordType('text') = REC_NONE), so self-host is byte-identical. - Acceptance met:
var f: Text; Assign/Rewrite/WriteLn(f,…)/Close/Reset/ ReadLn(f,s)round-trips through PAL (regressiontest/test_textfile.pas, built-Fulib/rtl/platform/posix); console read/write tests stay green.make testgreen; self-host + threadsafe fixedpoint byte-identical. - Note:
examples/adventureadvances past all the file-IO keyword forms andfor sp in Player.Spells; it now stops in the posix PAL backend at anSYS_openatarch-define resolution (a Track B platform-axis gap, separate from this ticket).
- Implicit import: the
Resolved-in: 0fe2e9c (finalizing commit)