Skip to content

refactor: 2026 modernization pass — builtin macro migration, file splits, dead-code removal, test hygiene - #20

Merged
takeokunn merged 6 commits into
mainfrom
feat/2026-modernization
Aug 5, 2026
Merged

refactor: 2026 modernization pass — builtin macro migration, file splits, dead-code removal, test hygiene#20
takeokunn merged 6 commits into
mainfrom
feat/2026-modernization

Conversation

@takeokunn

Copy link
Copy Markdown
Collaborator

Summary

  • Splits prover.lisp/tabling.lisp/data.lisp/collection.lisp/arithmetic.lisp/control.lisp into 8 new files along data/logic and responsibility boundaries, each verified by exact top-level-form reconciliation against the originals.
  • Migrates 12 more builtin sites to the define-iso-builtin macro; documents and deliberately leaves 21 sites hand-written for concrete reasons (meta-goals, branch-local preambles, one ASDF load-order constraint).
  • Removes verified-dead exports (call-graph-p) and two compiler-proven-unreachable branches; investigates and deliberately keeps a third candidate that only looked dead from one of three call sites.
  • Fixes stale doc references (README links, flake-input version pin, architecture load-order inventory regenerated to 65 entries) and one dropped-API mention.
  • Bumps cl-weave to v1.2.0 (metadata-only change, confirmed via compare API).
  • Test suite: converts ~40 longhand test groups to table-driven macros, fixes one genuinely vacuous test (abolish-validates-predicate-indicators), removes 24 no-op wrapper forms, adds coverage-closing tests. Suite: 1732 → 1765 passing, coverage 95.40% → 95.92% expression / 92.40% → 93.39% branch.

No public API additions or removals; all changes are internal refactors, dead-code removal, dependency bump, and doc/test fixes.

Test plan

  • Local test suite passing (1765/1765, see final commit)
  • CI (nix flake check) green on this PR

…boundaries

prover.lisp/tabling.lisp/data.lisp/collection.lisp/arithmetic.lisp/control.lisp
were split into 8 new files along natural module seams (pure spec data vs.
evaluator logic, static graph analysis vs. runtime dispatch, declaration
registry vs. clause storage, sort/aggregate helpers vs. solutions machinery),
each verified by exact top-level-form reconciliation against the originals
and a full-text-multiset diff, so no form was lost, duplicated, or altered.

prover.lisp's core CPS dispatch chain (the direct-dispatch lane documented
in Serena memory architecture/rule_program_materialization_free_dispatch)
was deliberately left untouched as one compilation unit; only the
goal/module-resolution cluster it doesn't depend on was extracted ahead of
it as src/goal-resolution.lisp.

control.lisp also carries one define-iso-builtin migration
(call_with_depth_limit/3) from the same session's macro-adoption pass,
bundled here because it was applied sequentially on top of this file's
split without an intermediate commit.

cl-prolog.asd's :components list is reordered to respect the new load-order
constraints this introduces (e.g. left-recursion-analysis.lisp and
goal-resolution.lisp must precede prover.lisp).
Migrates 12 of the remaining hand-written argument-resolution preambles to
the existing define-iso-builtin macro: atom_string, string_to_atom,
number_string, term_string, sub_string, split_string (string.lisp),
char_type, code_type (char-type.lisp, both using :raw to preserve the
original's guard-then-resolve ordering), term_to_atom,
read_term_from_atom (term-io.lisp), numlist, permutation (list-extra.lisp).

Every :raw marking was checked individually against the original's
resolution order so no argument that was previously resolved
conditionally/lazily became eagerly resolved -- eager resolution has a real
cost (a per-call environment index) that this project's prior
performance-tuning pass specifically minimized.

21 other candidate sites were investigated and left as hand-written for
concrete reasons recorded in this session's report: meta-goals that must
stay unresolved for backtracking, branch-local preambles the macro cannot
wrap, and one case (list.lisp length/2) where the hand-written code uses a
shallow variable chase rather than the macro's deep resolution -- migrating
would have silently changed behavior. Two sites (term-sorting.lisp sort/4,
predsort/3) were attempted and reverted: they load before core.lisp, which
defines the macro, an ASDF ordering constraint now recorded in Serena
memory architecture/define_iso_builtin_load_order_constraint.
A dead-code sweep found this codebase already had almost none. Removed:

- call-graph-p (callgraph/package.lisp export, callgraph/call-graph.lisp
  defstruct): auto-generated by defstruct with zero references anywhere
  outside its own export line, unlike every sibling callgraph export.
  Added (:predicate nil) so defstruct stops interning it at all, rather
  than just removing the export and leaving an unused internal function.
- One compiler-proven-unreachable branch in text-conversion.lisp's
  %text-of (an empty-code-list case that %term-atom-p's earlier clause
  always intercepts first for NIL).
- One unreachable branch in clause.lisp's %compile-rule-program (a
  not-consp check on a slot declared :type list, whose only other
  possible value is already caught by the preceding clause).

A third candidate in term-compare.lisp's %compare-strings was investigated
and deliberately NOT removed: the "unreachable" claim held for only one of
its three call sites, a live NaN edge case in the other two could not be
ruled out, and removing the arm would have violated %compare-terms's
declaimed return type under (optimize (safety 1)). Recorded in Serena
memory architecture/trichotomy_comparator_equality_arm_not_dead.
- README.md: 4 documentation links pointed at paths that don't exist under
  the current MkDocs site (installation/, quick-start/, api-reference/,
  architecture/ instead of /getting-started/, /guide/first-program/,
  /reference/api/, /reference/architecture/); the flake-input example
  still pinned v1.0.1 against a current v1.4.2 release.
- docs/src/reference/architecture.md: the numbered load-order file
  inventory was stale twice over -- missing 8 pre-existing builtin
  modules, then made staler still by this session's own file-split work
  adding 8 more files to cl-prolog.asd. Regenerated to match the current
  :components list exactly (65 entries), and updated three prose bullets
  describing file responsibilities that the split changed.
- docs/src/reference/callgraph.md: removed the now-nonexistent call-graph-p
  from the public API summary (see the paired dead-code removal commit).
- docs/src/guide/builtin-goals.md: added the export-completeness caveat
  api.md already carries, for the predicate group where it was missing.
Confirmed via the GitHub compare API (v1.1.4...v1.2.0) that the only
changes are cl-weave.asd/flake.nix/flake.lock -- no Lisp source changed,
so cl-weave run and cl-weave:run-all are behaviorally identical. paredit-cli
(v1.4.0) and cl-nix-forge (v0.5.0) were already at their latest tags.

Note: cl-weave's newest tag has no attached GitHub Release, so `gh release
list` alone reports v1.1.4 as latest; the tags API is what actually
surfaces v1.2.0.
Converted ~40 longhand-repeated deftest groups to deftest-table/
deftest-queries/deftest-io-queries (the latter new, added to
t/support/core.lisp alongside a :signals-condition table spec kind so
class-checked assertions don't get silently downgraded to "any throw
accepted"). Three groups were deliberately left un-collapsed because their
cases are sequentially state-coupled (splitting them would make each case
pass while testing nothing).

Fixed a genuinely vacuous test: abolish-validates-predicate-indicators
passed an unquoted loop variable to assert-query, which quotes its
argument internally, so the test queried the literal atom GOAL every
iteration rather than the intended terms -- and passed only because that
atom reliably signals an existence-error. A full sweep of the remaining
325+ quoting-macro call sites found no other instance. Recorded in Serena
memory testing/vacuous_assert_query_unquoted_goal.

Removed 24 stray no-op top-level (progn ...) wrappers (merge artifacts, no
semantic effect -- CL propagates top-level-ness through progn) and
consolidated a duplicate signals-condition macro (33 call sites) into the
existing signals-prolog-condition helper.

Added coverage-closing tests for the largest gaps a fresh sb-cover run
identified, concentrated in rulebase-compaction.lisp, builtins/format.lisp,
term-compare.lisp, and text-conversion.lisp, verified with a non-vacuity
control run (5 deliberately-wrong expectations produced exactly 5
failures, nothing else). Determined most of the remaining gap is
structurally uncoverable by sb-cover -- declaration forms, defstruct
slot defaults, and inline function bodies whose out-of-line definition is
never entered even when called -- rather than a real test gap; recorded
in Serena memory testing/sb_cover_structurally_uncoverable_expressions.

Suite: 1732 -> 1765 passing, 0 failed/errored throughout. Coverage:
95.40% -> 95.92% expression, 92.40% -> 93.39% branch.
@takeokunn
takeokunn merged commit 5943a96 into main Aug 5, 2026
1 check passed
@takeokunn
takeokunn deleted the feat/2026-modernization branch August 5, 2026 08:29
@takeokunn takeokunn mentioned this pull request Aug 5, 2026
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