← board

Pascal corpus: fcl-passrc — ENDGAME. Deep class hierarchy + resolver (60k src, 40k tests)

2026-09-06 — THIS RUNG IS UNDER ATTEMPT RIGHT NOW, FROM THE OTHER DOOR. Do not take this ticket.

frankD is on fcl-passrc as RUNG 7 of [[feature-pascal-corpus-expansion]], reported directly to the coordinator 2026-09-06. Wall 1 (resourcestring ending a const section) is cleared, the rung moved +819 lines, and wall 2 is filed as [[bug-p-array-of-const-in-a-method-pointer-type-is-refused-and-parsing-it-is-the-trap]]. Wall 3 (the property default clause) is next and uncharacterised.

THIS TICKET IS THE SECOND DOOR INTO ONE TOPIC, AND IT IS THE ONE THAT READS AS FREE. The ladder ticket is in unfinished/ (a status hint, not a lock) with an empty owner:, and this one sits in backlog-pascal/ at prio 30 with Owner: — — which is a folder ready --track P scans and hands out. Two tickets, two folders, two priorities, one rung, and neither named the other until now. That is the collision git cannot see: both diffs would apply cleanly and no track letter would notice.

Whoever attempts fcl-passrc should work the ladder ticket and message frankD first. The source for this rung is on the box at /usr/share/fpcsrc, NOT in library_candidates/, so the gitignored-corpus caveat that applies to the other rungs does not apply here.

Recorded by the coordinator, from frankD's own report — this is a RELAY, not an assignment, and the owner: field is deliberately left alone: a claim is the holder's act.

Sequencing (user call, 2026-07-12)

Not the first candidate. At 60k LOC of source over a 200-class hierarchy, bring-up would hit a dozen unrelated walls simultaneously and give no clean bisect signal — you cannot tell a metaclass bug from a refcount bug from an RTL gap when they all fire in the same build. Land the small ones first ([[feature-pascal-corpus-fpcunit]], then [[feature-pascal-corpus-fpjson]]) so the OOP surface is already shaken out, then come here for the depth. Re-rate prio: upward once those are green.

What it is (verified 2026-07-12, do not re-derive)

fcl-passrc is NOT the FPC compiler's internal parser. It is a standalone library (packages/fcl-passrc) that parses Pascal source into an AST and resolves it — used by fpdoc, pas2js (it is pas2js's actual frontend), and conversion tools. The compiler proper has its own separate scanner/parser under compiler/. So passrc has no coupling to compiler internals — it is a consumable library, text in, AST out.

Local checkouts: /home/rene/src/fpc-source/packages/fcl-passrc and /usr/share/fpcsrc/3.2.2/packages/fcl-passrc.

Sizes (src/, 60,696 LOC total):

unit LOC what
pasresolver.pp 29,660 name/type resolution — the monster
pparser.pp 7,823 recursive-descent parser
pasresolveeval.pas 6,004 const evaluation
pastree.pp 5,947 the AST class hierarchy (~200 classes, refcounted)
pscanner.pp 5,333 lexer + directives
pasuseanalyzer.pas 3,296 reachability analysis
paswrite.pp 1,584 AST → source (roundtrip oracle!)
pastounittest.pp, passrcutil.pp 1,049 extras

Its own suite (tests/, 40,477 LOC, fpcunit, driver testpassrc.lpr): tcresolver.pas (18,649) · tctypeparser (3,681) · tcuseanalyzer (3,478) · tcresolvegenerics (2,889) · tcclasstype (2,143) · tcstatements (1,903) · tcscanner (1,825) · tcprocfunc (1,399) · tcexprparser (1,323) · tcbaseparser (925) · tconstparser (679) · tcpassrcutil (422) · tcmoduleparser (408) · tcvarparser (388) · tcgenerics.pp (365). Assertion-based — oracle is built in, no FPC needed at run time.

Why this one first

Densest OOP-per-line in the survey and structurally the right kind: deep inheritance (TPasElement → everything), abstract + virtual dispatch on every node, manual refcounting (AddRef/Release) on an object graph, visitor traversal, class-typed maps/lists, class of metaclass construction. It is also deterministic and text-shaped: parse a file, paswrite it back, diff → a cheap end-to-end oracle independent of the test suite.

Plan

  1. Vendor pinned fcl-passrc src + tests (PROVENANCE.md w/ FPC tag/commit) via tools/install_lib_candidates.sh. Read-only vendor; never fork the source.
  2. Compile src/ with $(PXX_STABLE). Bring-up in dependency order: pscannerpastreepparserpasresolveevalpasresolver. Expect walls in: class-hierarchy breadth, class of / virtual class methods, refcounted object graphs, TFPList/TStringList breadth, sets of enums, nested types.
  3. First runnable milestone (before the suite): a ~40-line host that parses a Pascal file and paswrites it back. Feed it our own compiler sources and diff the roundtrip — self-referential and free.
  4. Then make test-passrc: build testpassrc against our fpcunit and run the suite. Start with tcscanner + tcbaseparser, widen to tctypeparser/tcclasstype, and treat tcresolver + tcresolvegenerics as the endgame.
  5. Each failure → minimal repro vs FPC → fix ONE in the owning lane → bXXX regression → land green.

Acceptance

make test-passrc green on scanner + parser + classtype + typeparser groups; roundtrip host reproduces its input on our own sources. Resolver groups tracked as a follow-up if they cascade.

Gate

Frontend/IR changed → make test + self-host byte-identical → make stabilize && make pin.

Log