diff options
Diffstat (limited to 'lib/rites/rite_ef/precedence_ef.ml')
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.ml | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index b69ee52..36aea83 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -600,6 +600,61 @@ let is_bvm_office (c : Vocab_ef.rank Precedence.candidate) = (ยง6) rather than guessed. *) let is_omissible_vigil (rank : Vocab_ef.rank) = rank = Vocab_ef.Class2 || rank = Vocab_ef.Class3 +(* RG 33's THIRD omission trigger, the clause [is_omissible_vigil]'s own + comment above records as unimplemented: "vel si festum cui praemittitur in + alium diem transferri aut ad commemorationem reduci contingat" -- "or if + the feast it precedes happens to be transferred to another day or reduced + to a commemoration". + + Each pair is (vigil slug, the slug of the feast it precedes). RG 34 fixes + the feast on the day AFTER the vigil ("Vigiliae... celebrantur die + praecedenti festum"), so the kernel needs no date here, only the identity + -- and it must be given the identity rather than deriving it, because + these slugs do not derive from one another: two of the five share a stem + with their feast and three do not (see {!Precedence.rules.vigil_feast}). + + The five are exhaustive for the 1962 universal calendar. The four + sanctoral ones are the only entries in data/ef/sanctoral.sexp matching + [is_vigil]; the fifth is temporal_ef's own Ascension vigil. The two + I-class vigils (Nativity Eve, Pentecost Vigil) are deliberately ABSENT: + RG 33 governs only "Vigilia II aut III classis", and RG 30 puts a I-class + vigil beyond losing in the first place -- the same argument + [is_omissible_vigil] already makes for the other two triggers. + + A user overlay adding a diocesan vigil is NOT covered by this table and + its vigil will not be omitted. That is a data limit, not an architectural + one -- the kernel hook takes any candidate -- and it is stated here rather + than papered over with a slug-shape heuristic that would be wrong for + three of the five universal cases it can already be checked against. *) +(* Built with [Slug.of_string_exn], deliberately, and at module initialisation + rather than per call. A malformed literal here is a PROGRAMMING error in a + static table, not untrusted input, and the alternative found by mutation + testing is worse than a crash: with [Slug.of_string] and [Result.to_option] + a typo (an uppercase letter is enough -- slugs are lowercase-only) collapses + to [None], which this function's own contract reads as "not a vigil", and + the whole rule switches itself off for that entry in silence. That failure + was reproduced: mutating "lawrence" to "lawrence-WRONG" reddened FOUR tests, + all of them the ones that notice the rule missing, and none that notice it + pointing at the wrong feast -- indistinguishable from deleting the row. + The same mutation with a well-formed "lawrence-wrong" reddens eighteen. + Raising at startup keeps a typo loud; [Rite_ef]'s own bundle is constructed + at initialisation too, so the failure surfaces before any calendar is + resolved. *) +let vigil_feast_table = + List.map + (fun (vigil, feast) -> (vigil, Slug.of_string_exn feast)) + [ ("ef-ascension-vigil", "ef-ascension"); + ("vigil-of-the-nativity-of-st-john-the-baptist", "nativity-of-st-john-the-baptist"); + ("vigil-of-sts-peter-paul", "sts-peter-paul"); + ("vigil-of-st-lawrence", "lawrence"); + ("vigil-of-the-assumption", "assumption-of-the-blessed-virgin-mary") ] + +let vigil_feast (c : Vocab_ef.rank Precedence.candidate) : Slug.t option = + let cel = c.Precedence.cel in + if not (is_omissible_vigil cel.Celebration.rank) then None + else List.assoc_opt (Slug.to_string cel.Celebration.slug) vigil_feast_table + + (* Every Sunday slug this rite's temporal cycle produces -- named (temporal_ef.ml's [named], e.g. "ef-easter-sunday") or the generic "ef-<season>-sunday-<n>" fallback ([sunday_slug]) -- contains this |
