summaryrefslogtreecommitdiff
path: root/lib/citation/parse.ml
Commit message (Collapse)AuthorAgeFilesLines
* fix(citation): represent chapter-crossing verse ranges (W4)Lukasz Kasprzak2026-08-261-15/+61
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Parse.verse_range's [last] endpoint gains an optional chapter (Parse.verse_end: { chapter : int option; verse : verse_num }), so a hyphen range whose two endpoints lie in different chapters ("1 John 1:5-2:2") can be represented at all. Rejected: a bare [int option] living alongside [last] as a second field on verse_range -- that would let 'a chapter with no verse' exist as a constructible value. parse_part now splits a part's leading "chapter:" at the FIRST colon only (not every colon), so the verses side can itself carry a second colon from a crossing range. parse_range detects a crossing by checking whether the range's right-hand side contains ':', and skips the same-chapter descending-range guard for that case (a later chapter is always "ahead", whatever its own verse numbers are). Handles the compound shape too -- a crossing range followed by further, same-chapter verse references in the same comma list ("Matthew 9:35-10:1,5a,6-8") -- since those trailing pieces parse as ordinary bare verses/ranges, unaffected by the preceding crossing. Render's one_range renders a crossing [last] through the same chapter_verse template one_part already uses for the part's own leading "chapter:verses", so a style that reconfigures the chapter/verse separator renders a crossing endpoint in that same convention rather than a hardcoded ':'. This closes the W4 known-wrong: 41 (now 49, after an intervening Second-reading extraction) of the OF lectionary's citations printed unconverted, every one this exact shape. test_citation_coverage_of.ml's pinned residual is now empty and asserted exactly, over the full 1725-field data/of/lectionary.sexp population, including the round-trip check (parse -> render -> parse structural equality). test_citation.ml gains direct parse-suite cases for the basic crossing, the compound shape, a mid-list crossing, a crossing with a sub-verse letter, and a malformed-crossing rejection. data/of/lectionary.sexp is regenerated via its own generator (tools/bootstrap_lectionary_of.ml, whose own embedded header text is updated to match); only comment lines change, confirmed by diff -- no lectionary entry differs. test_lectionary_of.ml's whole-file SHA-256 pin is updated to match. EF is unaffected: data/ef/ is untouched since v1.0.0, and a direct byte comparison of `colitur day`/`colitur readings` for 2026, 1583 and 9999 against a git-worktree build of 1c0137d is identical on all six outputs.
* fix(citation): recognise English-canonical OF books, verse sub-lettersLukasz Kasprzak2026-08-261-29/+76
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 35% of OF citation fields (259/730 on colitur readings --rite of 2026) printed unconverted -- 1 John renders on 2 January but not 3 January. Two independent causes, both in the citation/siglum path, neither in the OF data itself: 1. Book.table only ever surveyed the three EF citation-bearing files, so 345 references citing a book no EF file happens to use (Job, Ruth, Judges, 1/2 Samuel, 1/2 Chronicles, 1/2 Maccabees, Baruch, Ecclesiastes, Habakkuk, Haggai, Nahum, Zechariah, Zephaniah, Deuteronomy, Amos, Micah, Lamentations, Ezra, Joshua, 2/3 John, Jude, Philemon, plus "Isaiah"/"Jeremiah"/"Ezekiel"/"Malachi"/"Mat"/ "The Acts"/"Tobit"/"Song of Solomon" spelling variants of books EF already knows) failed as "unknown book". Added as a new, separate of_lectionary_table rather than folded into the EF-surveyed table: none of these 25 new books is attested in the EF's own scans, and lang/la.ini's own header refuses to fabricate an uncited Latin title, so they are resolvable (parse + render, falling back to their own English spelling) but deliberately excluded from Book.all -- test_lang_coverage.ml's la.ini-completeness promise is preserved exactly for the ids it already covered, not silently weakened. 2. Parse's grammar could not read a verse number carrying a lectionary sub-verse letter ("11a", "1bcde") at all -- the dominant remaining failure shape once (1) was fixed. verse_range now carries a verse_num { n; suffix } on each boundary, PRESERVED through rendering rather than dropped (dropping would silently lose real precision the source text carries). Chapter numbers are untouched (nothing in the data ever attaches a letter to one). A third, subtler bug surfaced by (1): registering "jude"/"philemon"/ "2 John"/"3 John" exposed Parse's existing "leading comma-number is a chapter" heuristic misreading a single-chapter book's bare verse list ("Jude 17,20b-25") as chapter 17 -- a wrong PARSE, worse than the previous safe "unknown book" failure. Book.is_single_chapter now tells Parse to skip that heuristic for the four one-chapter books and default to chapter 1. Residual, honestly enumerated rather than forced to zero: 41 distinct references (of 1540) are hyphenated ranges crossing a chapter boundary ("2:29-3:6") -- a Parse.t shape verse_range/part do not represent, a type restructuring deliberately not attempted this task. Pinned exactly by the new test_citation_coverage_of.ml, both directions (a new failure or one of these 41 starting to convert both go red), and disclosed in data/of/lectionary.sexp's own regenerated provenance header (tools/ bootstrap_lectionary_of.ml now runs the same parser at generation time and names the count and the set). Verified EF-unaffected: git diff v1.0.0..HEAD -- lib/kernel/ lib/rites/rite_ef/ data/ef/ is empty, and `colitur day`/`readings` output for 2027 is byte-identical against the pre-fix binary.
* fix: audit findings — parser strictness, name ambiguity, and errorsLukasz Kasprzak2026-08-201-2/+52
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Found by auditing the shipped program rather than the diff. The parser accepted OCaml integer-literal syntax, so "Luke 1_1:5" read as chapter ELEVEN and "+5" as 5 -- a typo silently becoming a different chapter, reachable through any user overlay. Numbers are now plain digits and positive, and a descending range is rejected: 1:20-10 is always a transcription error. No shipped citation changed. FOUR PAIRS OF DIFFERENT BOOKS SHARED A FULL TITLE. 1 and 2 Corinthians both rendered "Epistola ad Corinthios", as did Thessalonians, Timothy and Peter -- 108 citations in 2027 alone that a reader cannot resolve to a book. This is the Kings defect fixed earlier and not generalised. The titles now carry their volume numeral, marked CONSTRUCTED, and a test asserts no two books share a name -- while allowing the case where two ids ARE the same book under different numbering, which a tradition relates. Spec section 8.5 is now delivered rather than merely recorded. Shipped styles did not re-parse their own output: 32 of 52 Latin abbreviations and 49 of 52 full titles failed, so a citation copied from colitur's own output into an overlay was passed through untouched and printed in the wrong language, silently. Every shipped name is registered as a spelling and split_book learned multi-word titles by longest-token match. Now 0 of 52 fail beyond the same-book aliases. Overlay errors were written for a compiler author: they named an OCaml source file the reader does not have and buried the useful token. The existing five-path rewriter is replaced by a generic one, applied to every load path rather than one, so "rank: is not one of the allowed values (at Class9)" replaces the raw Of_sexp_error dump. Also: the new-overlay scaffold documented citations and layer without showing them, and its comment implied the wrong nesting -- the single easiest thing to get wrong; error messages echoed whole file lines, copying an unrelated file's contents into stderr when a flag pointed at one; and config --show validated partway down its table, exiting 2 after writing five rows to stdout.
* feat(citation): parse citations into structureLukasz Kasprzak2026-08-201-0/+110
The parsed form is a book and a LIST of chapter-parts: the data cites across chapters and lists disjoint verse ranges within one. Two rules the shipped data forces and that are not obvious: a semicolon-separated part may inherit the previous chapter rather than restate it, and a chapter may be separated from its verses by a comma.