diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/bootstrap_lectionary_of.ml | 136 |
1 files changed, 107 insertions, 29 deletions
diff --git a/tools/bootstrap_lectionary_of.ml b/tools/bootstrap_lectionary_of.ml index f94a7db..83019b1 100644 --- a/tools/bootstrap_lectionary_of.ml +++ b/tools/bootstrap_lectionary_of.ml @@ -130,25 +130,51 @@ let parse_ini path = path (List.length other)); List.rev !sections -(* ---- Citation extraction -- First + Gospel only --------------------- *) +(* ---- Citation extraction -- First (+ Second on Sundays/solemnities) + Gospel --- *) -(* Psalm and Second are deliberately dropped, even though lectio's ini - carries [psalm] (every entry) and [second] (Sundays/solemnities): - Colitur_kernel.Validate's own ["citations"] check (kernel, off limits to - this task) hard-asserts every day's citation-part list is EXACTLY - [[First; Gospel]] -- the identical scope boundary - tools/bootstrap_lectionary.ml's own [convert] already holds for EF - ("Nothing here encodes... the chants (Psalm, Second, Tract, Alleluia, - Sequence) are deliberately unbuilt"). Carrying Psalm/Second through here - would not add capability today (nothing reads them) and would set up a - guaranteed collision with that kernel check the moment a future task - wires Validate over OF data. *) -let cite sec = +(* Fix wave I7 (final-review.md, 2026-08-25-colitur-of-phases-3-5): Psalm is + still dropped -- it is a CHANT (sung, with a congregational refrain, OLM + 1981 Praenotanda n. 71's own "psalmi et versus inter lectiones + occurrentes", listed apart from the readings proper), not a reading, and + {!Colitur_kernel.Rite.t.citation_shapes} (see that field's own doc + comment) still declares no shape carrying it for either rite -- nothing + here would read a Psalm citation even if extracted. See the fix wave's + own report for the fuller argument and what was rejected. + + Second is now extracted, but ONLY from a SUNDAY-CYCLE section (its own + [~is_sunday_cycle] argument, [`A]/[`B]/[`C] at the call site below) -- + never from a WEEKDAY-CYCLE one ([`I]/[`II]), even on the one occasion + lectio's own snapshot carries a stray [second] field there + (advent-3-sat-II, "Zephaniah 3:14-17" -- content-verified as a + scraping-pipeline artifact, not a real OLM assignment: that citation is + the THIRD Sunday of Advent's own Year C first reading, evidently bled + into an unrelated Saturday ferial section by niedziela.pl's own + generator). This is not merely a guard against that one artifact; it is + the general rule OLM 1981 Praenotanda n. 66.1 vs n. 69.1 states (see + {!Rite_of.Rite_of.context}'s own citation): a ferial Mass has two + readings, never three, so a weekday-cycle section can never legitimately + carry a Second reading regardless of what stray data a snapshot exposes. + {!Colitur_kernel.Validate}'s own rite-supplied ["citations"] check (no + longer off limits to this task -- the kernel change this fix wave itself + makes) would reject an emitted weekday-cycle entry carrying [Second] + outright, since neither of the OF's two declared shapes admits it + alongside a non-Sunday/solemnity office; extracting it here regardless + would just move the failure from "silently wrong" to "loudly rejected at + test time" rather than avoiding it. *) +let cite ~is_sunday_cycle sec = let get k = match List.assoc_opt k sec.fields with None | Some "" -> None | Some v -> Some v in match (get "first", get "gospel") with | Some first, Some gospel -> - Some [ { Citation.part = Citation.First; reference = first }; - { Citation.part = Citation.Gospel; reference = gospel } ] + let second = + if is_sunday_cycle then + Option.map (fun v -> { Citation.part = Citation.Second; reference = v }) (get "second") + else None + in + Some + (List.filter_map Fun.id + [ Some { Citation.part = Citation.First; reference = first }; + second; + Some { Citation.part = Citation.Gospel; reference = gospel } ]) | _ -> None (* ---- Cycle-family grouping ------------------------------------------ *) @@ -216,21 +242,25 @@ let collapse_sunday base va vb vc = Printf.eprintf "bootstrap_lectionary_of: %s: cycle C disagreed with A/B (scraping-year \ artifact, majority kept): %s\n" - base (match vc with [ f; _ ] -> f.Citation.reference | _ -> "?"); + (* [f :: _], not a fixed-arity [[f; _]]: [cite] now emits either a + 2-element ([First; Gospel]) or 3-element ([First; Second; Gospel]) + list, and [First] is its head either way -- a fixed-arity pattern + would silently fall through to "?" on the 3-element shape. *) + base (match vc with f :: _ -> f.Citation.reference | [] -> "?"); Flat va end else if va = vc then begin Printf.eprintf "bootstrap_lectionary_of: %s: cycle B disagreed with A/C (scraping-year \ artifact, majority kept): %s\n" - base (match vb with [ f; _ ] -> f.Citation.reference | _ -> "?"); + base (match vb with f :: _ -> f.Citation.reference | [] -> "?"); Flat va end else if vb = vc then begin Printf.eprintf "bootstrap_lectionary_of: %s: cycle A disagreed with B/C (scraping-year \ artifact, majority kept): %s\n" - base (match va with [ f; _ ] -> f.Citation.reference | _ -> "?"); + base (match va with f :: _ -> f.Citation.reference | [] -> "?"); Flat vb end else Sunday_cycle (va, vb, vc) @@ -271,7 +301,8 @@ let resolve_sections secs = match base_and_letter sec.name with | None -> die "%s: no recognised cycle suffix (-A/-B/-C/-I/-II)" sec.name | Some (base, letter) -> ( - match cite sec with + let is_sunday_cycle = match letter with `A | `B | `C -> true | `I | `II -> false in + match cite ~is_sunday_cycle sec with | None -> die "%s: no first/gospel field" sec.name | Some cs -> let cur = try Hashtbl.find tbl base with Not_found -> (None, None, None, None, None) in @@ -632,12 +663,18 @@ let slug_or_die name = match Slug.of_string name with Ok s -> s | Error e -> die the VIGIL, not the Day). Confirmed sourceable, not merely "cannot convert": docs/research/of/olm-1981-ocr.txt:4513-4520 ("16 Ad Missam in die"), the same primary authority this whole module already cites for - the cycle rules (lectionary_of.mli). First reading and Gospel only, - matching this file's own scope everywhere else (data/of/lectionary.sexp - :2's own header, and {!Colitur_kernel.Validate}'s [First; Gospel] - assertion) -- OLM's own Second Reading (Heb 1:1-6) and Psalm (Ps 97) are - out of scope for the identical reason every other day's Second Reading - and Psalm are (I7, a kernel-level limitation, not this file's own). + the cycle rules (lectionary_of.mli). + + UPDATED, fix wave I7 (final-review.md, 2026-08-25-colitur-of-phases-3-5, + 2026-08-26): Christmas Day is a solemnity, so its own Second Reading is + now in scope and included -- Hebr 1, 1-6 ("Locutus est nobis Deus in + Filio"), same source line (docs/research/of/olm-1981-ocr.txt:4517), + confirming the value this comment already named before this fix wave + built the capability to emit it. Psalm (Ps 97) remains out of scope, + deliberately, not merely by omission -- see [cite]'s own doc comment + above for the argument (a chant, not a reading; no declared + {!Colitur_kernel.Rite.t.citation_shapes} shape carries it for either + rite). The Gospel (Io 1, 1-18) carries OLM's own "longior/brevior" choice ("1-18 (longior) vel 1-5.9-14 (brevior)") -- the LONGER form is used, @@ -654,6 +691,7 @@ let slug_or_die name = match Slug.of_string name with Ok s -> s | Error e -> die let hand_authored = [ ( slug_or_die "of-nativity", [ { Citation.part = Citation.First; reference = "Isaiah 52:7-10" }; + { Citation.part = Citation.Second; reference = "Hebrews 1:1-6" }; { Citation.part = Citation.Gospel; reference = "John 1:1-18" } ] ) ] let entries_of (slug, r) = @@ -910,11 +948,25 @@ let () = temporal_day_gap ~lectionary:lect ~year_start:Rite_of.Temporal_of.year_start in let cit_total, cit_bad = citation_conversion_census lect in + (* Fix wave I7 (final-review.md, 2026-08-25-colitur-of-phases-3-5): how + many emitted citation FIELDS are a Second reading -- i.e. how many of + [entries]'s own Sunday-cycle entries actually carried one in lectio's + snapshot (Sunday-cycle sections without a NAMED Second in the ini, and + every weekday-cycle entry by [cite]'s own [~is_sunday_cycle] guard, + never get one). Printed in the header below alongside [cit_total] so a + reader can see the Second-reading count is a real, measured subset of + the total, not a guess. *) + let second_count = + List.concat_map snd entries + |> List.filter (fun (c : Citation.t) -> c.Citation.part = Citation.Second) + |> List.length + in let oc = open_out dst in Printf.fprintf oc "; data/of/lectionary.sexp -- OF (2002) temporal + sanctoral lectionary\n\ - ; (Epistle + Gospel citations, never scripture text), bootstrapped from\n\ - ; lectio.\n\ + ; (First, and on Sundays/solemnities also Second, and Gospel citations,\n\ + ; never scripture text -- \"Epistle\" is EF vocabulary, not used here),\n\ + ; bootstrapped from lectio.\n\ ;\n\ ; LINEAGE, stated loudly because it constrains what this file can show\n\ ; (design spec 2026-08-24-colitur-of-rite-module-design.md sec4.4/sec5):\n\ @@ -962,6 +1014,19 @@ let () = ; three genuinely different structural-unreachability reasons, none of\n\ ; them \"duplicate\" -- see [excluded_bases]'s own comment.\n\ ;\n\ + ; CORRECTED, fix wave I7 (final-review.md, 2026-08-25-colitur-of-\n\ + ; phases-3-5, 2026-08-26): this file used to carry First and Gospel\n\ + ; only, and called the First reading an \"Epistle\" above -- EF\n\ + ; vocabulary, wrong for the OF (its First reading is often Old\n\ + ; Testament or Acts, not an epistle at all; OLM 1981 Praenotanda\n\ + ; n. 66.1). Both fixed at the source: {!Colitur_kernel.Rite\n\ + ; .t.citation_shapes} (kernel change, {!Rite_of.Rite_of.context}'s own\n\ + ; citation) makes the well-formed citation-part shape RITE-supplied\n\ + ; rather than a kernel-hardcoded [[First; Gospel]], and [cite] above\n\ + ; now extracts a Second reading from every SUNDAY-CYCLE (Sunday/\n\ + ; solemnity) entry lectio's own snapshot carries one for. See\n\ + ; COVERAGE (5) below for the measured count.\n\ + ;\n\ ; Source: %s\n\ ; SHA-256: %s\n\ ; %d ini sections (988 expected) -> %d resolved (base, cycle-shape) \ @@ -991,7 +1056,7 @@ pairs -> %d colitur slugs mapped, %d ini bases genuinely excluded (structurally\ ; OLM norms working as designed for a saint with no proper of\n\ ; their own, not a gap in this data.\n\ ; (4) Citation-siglum conversion (fix wave I2, 2026-08-26 review): of\n\ - ; %d emitted (First, Gospel) citation fields, %d distinct\n\ + ; %d emitted (First, Second, Gospel) citation fields, %d distinct\n\ ; references (out of the full %d) do not parse -- every one a\n\ ; hyphen range that crosses a chapter boundary (\"2:29-3:6\"), a\n\ ; {!Colitur_citation.Parse.t} shape this parser's [part]/\n\ @@ -1004,6 +1069,19 @@ pairs -> %d colitur slugs mapped, %d ini bases genuinely excluded (structurally\ ; .format}'s own documented contract on a parse miss. Pinned\n\ ; exactly, both directions, by test/test_citation_coverage_of.ml.\n\ ; Named below.\n\ + ; (5) Second-reading coverage (fix wave I7, 2026-08-26): %d of the\n\ + ; %d emitted citation fields above are a Second reading -- every\n\ + ; one from a SUNDAY-CYCLE (-A/-B/-C, i.e. Sunday/solemnity)\n\ + ; entry, never a weekday-cycle one ([cite]'s own\n\ + ; [~is_sunday_cycle] guard), matching OLM 1981 Praenotanda\n\ + ; n. 66.1/n. 69.1 and n. 84(b)/(c) (docs/research/of/olm-1981\n\ + ; .pdf pp.32-33,37-38/\"XXXII-XXXIII\",\"XXXVII-XXXVIII\"): a\n\ + ; Sunday or solemnity Mass has three readings, a feria/feast/\n\ + ; memorial two. The Responsorial Psalm (lectio's own [psalm]\n\ + ; field, present on nearly every section) is deliberately NOT\n\ + ; extracted -- see [cite]'s own doc comment for the argument\n\ + ; (a chant, not a reading; declared in neither rite's\n\ + ; {!Colitur_kernel.Rite.t.citation_shapes}).\n\ ; Regenerate with:\n\ ; eval $(opam env) && dune exec tools/bootstrap_lectionary_of.exe -- %s %s\n" src (sha256 src) (List.length secs) (List.length resolved) report.mapped @@ -1011,7 +1089,7 @@ pairs -> %d colitur slugs mapped, %d ini bases genuinely excluded (structurally\ gap_total gap_count (List.length report.unmapped) (List.length resolved) (Hashtbl.length sanctoral_slugs) report.sanctoral_passthrough (Hashtbl.length sanctoral_slugs - report.sanctoral_passthrough) - cit_total (List.length cit_bad) cit_total src dst; + cit_total (List.length cit_bad) cit_total second_count cit_total src dst; Printf.fprintf oc "; Unmapped lectio bases (%d):\n" (List.length report.unmapped); List.iter (fun b -> Printf.fprintf oc "; %s\n" b) report.unmapped; Printf.fprintf oc "; Citations that do not parse (%d distinct, all chapter-crossing ranges):\n" |
