diff options
Diffstat (limited to 'lib/rites/rite_ef')
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.ml | 95 | ||||
| -rw-r--r-- | lib/rites/rite_ef/precedence_ef.mli | 46 |
2 files changed, 141 insertions, 0 deletions
diff --git a/lib/rites/rite_ef/precedence_ef.ml b/lib/rites/rite_ef/precedence_ef.ml index 0716177..d8d64a8 100644 --- a/lib/rites/rite_ef/precedence_ef.ml +++ b/lib/rites/rite_ef/precedence_ef.ml @@ -214,3 +214,98 @@ let band (ctx : Vocab_ef.season Precedence.context) (c : Vocab_ef.rank Precedenc "feria" that is also a vigil is not a feria RG 91 describes. *) else if (not is_vigil) && rank = Class4 then 28 else unclassified + +(* Task 8: what happens to the day's LOSING candidate (docs/research/ + rules-register.md §4, "Occurrence" RG 92-95 and "Vigils" RG 33, plus RG + 94). [band] above decides who wins; this decides the loser's fate, which + turns on the LOSER's own rank and status (RG 95), except RG 33's vigil + omission, which also has to read the winner. Nothing here ever returns + [Precedence.Repose]: that disposition denotes RG 100-102's *repositio* + (perpetual impediment from a proper/diocesan calendar), out of this + plan's scope -- see calendar.mli's own note that nothing in the EF + ruleset currently emits it. *) + +(* RG 33 (register line 383-384): a I- or II-class vigil falling on any + Sunday or a I-class feast is entirely omitted. 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 marker; nothing else [band] classifies + does. Not an RG citation itself -- see [universal_layer]'s note on this + file's own naming conventions -- exposed for the same reason as + {!vigil_suffix}: a future rename of temporal_ef's Sunday-slug format has + somewhere to be caught other than a silently-wrong RG 33 disposition. *) +let sunday_marker = "-sunday" + +let contains_substring s ~needle = + let ls = String.length s and ln = String.length needle in + let rec at i = i + ln <= ls && (String.sub s i ln = needle || at (i + 1)) in + ln = 0 || at 0 + +let is_sunday_slug slug = contains_substring slug ~needle:sunday_marker + +(* RG 33's "any Sunday or a I-class feast": every RG 91 entry that can ever + outrank a II-class vigil (entry 21) without itself being a Sunday is I + class by the table's own structure (entries 1-13), so [rank = Class1] + alone already covers every way a I-class vigil (entries 5, 9 -- Nativity, + Pentecost) can be impeded at all; the slug check is what a II-class vigil + impeded by an ordinary Sunday (entry 15, rank Class2) needs, since that + winner's own rank is not Class1. *) +let impedes_vigil (winner : Vocab_ef.rank Precedence.candidate) = + let cel = winner.Precedence.cel in + cel.Celebration.rank = Vocab_ef.Class1 + || is_sunday_slug (Slug.to_string cel.Celebration.slug) + +(* Not yet RG 109 (Task 9's job: the closed list of privileged commemorations + and RG 108-111's admission counts). Every [Commemorate] this function + returns carries this one placeholder rather than a silent default, so the + choice is visible and grep-able. [Ordinary] chosen over [Privileged] + deliberately: it grants no admission entitlement RG 111 has not earned, + so code that trusts this value before Task 9 replaces it under-privileges + a commemoration rather than over-privileges one -- the safer direction to + be wrong in. *) +let interim_privilege = Precedence.Ordinary + +let disposition ~(winner : Vocab_ef.rank Precedence.candidate) + ~(loser : Vocab_ef.rank Precedence.candidate) : Precedence.disposition = + let open Vocab_ef in + let cel = loser.Precedence.cel in + if cel.Celebration.status = Celebration.Commemoration_only then + (* Always -- checked before RG 33's omission and RG 95's transfer so + neither can override it: a Commemoration_only entry can never win + (Precedence.resolve holds it out of the band contest entirely, see + that module's [resolve]) and, per the brief, can never transfer + either. *) + Precedence.Commemorate interim_privilege + else if + (cel.Celebration.rank = Class1 || cel.Celebration.rank = Class2) + && is_vigil (Slug.to_string cel.Celebration.slug) + && impedes_vigil winner + then + (* RG 33. Checked before the generic Class1 -> Transfer rule below, or a + I-class vigil (Nativity, Pentecost) impeded on its own Sunday/ + I-class-feast terms would wrongly transfer instead of vanishing. *) + Precedence.Omit + else if cel.Celebration.rank = Class1 then + (* RG 95: only I-class feasts have the right of translation. This is the + branch that completes Task 7's All Souls fix (register line 334, RG + 91 entry 8): All Souls is I class and not a vigil, so once it loses + to an occurring Sunday it reaches here and transfers -- to 3 + November per the register, but WHERE it lands is + Rite.transfer_target's job (RG 96), not this function's; disposition + only says THAT it moves. *) + Precedence.Transfer + else + (* RG 95's other branch, for everything below I class: "aut + commemorantur aut penitus omittuntur" -- commemorated or wholly + omitted. Which of the two survives is RG 108-111's admission count + (Task 9's [admit]), not this function's decision; this only opens the + commemoration. + + RG 94 (a fixed-day commemoration is not carried along with a + transferred feast) needs no code here: [Precedence.resolve] calls + this function once per loser, always against the day's actual + [observed] winner -- never against a fellow loser that itself + transferred away -- so no mechanism exists by which a commemoration + could ride along with a departing feast in the first place; there is + nothing to suppress. *) + Precedence.Commemorate interim_privilege diff --git a/lib/rites/rite_ef/precedence_ef.mli b/lib/rites/rite_ef/precedence_ef.mli index 63fb0a1..9fe8789 100644 --- a/lib/rites/rite_ef/precedence_ef.mli +++ b/lib/rites/rite_ef/precedence_ef.mli @@ -56,3 +56,49 @@ val unclassified : int including shapes the 1962 table itself does not describe (see {!unclassified}). *) val band : Vocab_ef.season Precedence.context -> Vocab_ef.rank Precedence.candidate -> int + +(** RG 33's marker: every Sunday slug this rite's temporal cycle produces + (temporal_ef.ml's [named] and [sunday_slug]) contains this substring; + nothing else {!band} classifies does. Also colitur's own convention, not + an RG citation -- see {!universal_layer} -- exposed for the same reason + as {!vigil_suffix}: a rename of temporal_ef's Sunday-slug format has + somewhere to be caught other than a silently-wrong RG 33 disposition. *) +val sunday_marker : string + +(** The placeholder {!Precedence.privilege} every [Commemorate] disposition + below carries until Task 9 implements RG 109's closed list of privileged + commemorations and RG 108-111's admission counts. Exposed so Task 9 (and + any test wanting to assert on it explicitly) does not have to duplicate + the literal [Precedence.Ordinary]. *) +val interim_privilege : Precedence.privilege + +(** [disposition ~winner ~loser]: RG 92-95, 33, 94 (docs/research/ + rules-register.md §4, "Occurrence" and "Vigils"). What becomes of a + losing candidate, decided by the LOSER's own rank and status (RG 95), + except RG 33's vigil omission, which also reads the winner: + - a {!Celebration.status} of [Commemoration_only] is always + [Commemorate] (checked first: it can never win -- see + {!Precedence.resolve} -- and, by that same status's own definition, + already denotes an office with nothing left to translate, so it never + transfers either; not itself a further RG citation beyond RG 93's + general four-mechanism statement above); + - a [Class1] or [Class2] loser whose slug marks it a vigil ({!vigil_suffix}) + is [Omit] when the winner is any Sunday ({!sunday_marker}) or itself + [Class1] (RG 33 -- entirely omitted, not merely commemorated); + - any other [Class1] loser is [Transfer] (RG 95 -- only I class has the + right of translation; this is also what moves All Souls, register + line 334, once it loses to an occurring Sunday -- WHERE it lands is + {!Rite.t.transfer_target}'s job, not this function's); + - everything else is [Commemorate], carrying {!interim_privilege} until + Task 9 replaces it with RG 109's real per-day computation. + + Total over every winner/loser pair {!Precedence.resolve} or {!Calendar} + can construct: [Vocab_ef.rank] (RG 8) and {!Celebration.status} are both + closed variants, so the four cases above exhaust every representable + shape -- there is no fifth, "unclassified" case the way {!band} needs + one, because this function's own return type has no such slot to fall + into by accident. *) +val disposition : + winner:Vocab_ef.rank Precedence.candidate -> + loser:Vocab_ef.rank Precedence.candidate -> + Precedence.disposition |
