diff options
Diffstat (limited to 'lib/kernel')
| -rw-r--r-- | lib/kernel/calendar.ml | 9 | ||||
| -rw-r--r-- | lib/kernel/liturgical_day.ml | 3 | ||||
| -rw-r--r-- | lib/kernel/liturgical_day.mli | 5 | ||||
| -rw-r--r-- | lib/kernel/rite.ml | 2 | ||||
| -rw-r--r-- | lib/kernel/rite.mli | 20 | ||||
| -rw-r--r-- | lib/kernel/validate.ml | 29 |
6 files changed, 60 insertions, 8 deletions
diff --git a/lib/kernel/calendar.ml b/lib/kernel/calendar.ml index 98e9032..7fe9f67 100644 --- a/lib/kernel/calendar.ml +++ b/lib/kernel/calendar.ml @@ -543,6 +543,10 @@ let build_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.index) |> List.map (fun c -> (c.Precedence.cel, reason_for c))) @ rg33_omitted in + let formulary, citations = + rite.Rite.readings ~observed:resolution.Precedence.observed.Precedence.cel ~temporal ~date + ~temporal_at:rite.Rite.temporal + in { Liturgical_day.date; rite = rite.Rite.id; @@ -553,9 +557,8 @@ let build_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.index) transferred_in; transferred_out; omitted; - citations = - rite.Rite.readings ~observed:resolution.Precedence.observed.Precedence.cel ~temporal ~date - ~temporal_at:rite.Rite.temporal; + citations; + formulary; } let year (rite : ('s, 'r) Rite.t) (layer : 'r Layer.t) (y : int) : diff --git a/lib/kernel/liturgical_day.ml b/lib/kernel/liturgical_day.ml index bbb52b8..709f878 100644 --- a/lib/kernel/liturgical_day.ml +++ b/lib/kernel/liturgical_day.ml @@ -23,5 +23,8 @@ type ('s, 'r) t = { (** with the reason, never silent -- Task 12's no-celebration-lost invariant reads this *) citations : Citation.t list; (** always empty until Plan 4 *) + formulary : Mass_formulary.t option; + (** which Mass the day says, and how that was decided; [None] only for + a rite with no lectionary -- see {!Mass_formulary} *) } [@@deriving sexp] diff --git a/lib/kernel/liturgical_day.mli b/lib/kernel/liturgical_day.mli index f0ea3d9..4a71d6d 100644 --- a/lib/kernel/liturgical_day.mli +++ b/lib/kernel/liturgical_day.mli @@ -28,5 +28,10 @@ type ('s, 'r) t = { ["citations"] / ["citations-unresolved"] checks. (This said "always empty until Plan 4" until Task 10; the lectionary landed before Plan 4 did, and the comment outlived its truth.) *) + formulary : Mass_formulary.t option; + (** Which Mass this day says, and how that was decided -- see + {!Mass_formulary}. [None] only for a rite with no lectionary; for + EF it is [Some] on every day of every year 1583..9999, asserted by + {!Validate}. *) } [@@deriving sexp] diff --git a/lib/kernel/rite.ml b/lib/kernel/rite.ml index 600a691..a7d21d3 100644 --- a/lib/kernel/rite.ml +++ b/lib/kernel/rite.ml @@ -17,5 +17,5 @@ type ('s, 'r) t = { temporal:('s, 'r) Temporal.t -> date:Date.t -> temporal_at:(Date.t -> ('s, 'r) Temporal.t) -> - Citation.t list; + Mass_formulary.t option * Citation.t list; } diff --git a/lib/kernel/rite.mli b/lib/kernel/rite.mli index 0324bf2..45f3ed6 100644 --- a/lib/kernel/rite.mli +++ b/lib/kernel/rite.mli @@ -71,10 +71,22 @@ type ('s, 'r) t = { temporal:('s, 'r) Temporal.t -> date:Date.t -> temporal_at:(Date.t -> ('s, 'r) Temporal.t) -> - Citation.t list; - (** The day's Epistle and Gospel citations, or []. Rite-supplied for the - same reason [transfer_target] is: what a day with no proper of its - own falls back to is a rubric of a particular rite, not a universal. + Mass_formulary.t option * Citation.t list; + (** The Mass actually said -- which formulary, and how that was decided + -- paired with its Epistle and Gospel citations. Rite-supplied for + the same reason [transfer_target] is: what a day with no proper of + its own falls back to is a rubric of a particular rite, not a + universal. + + The [Mass_formulary.t option] is [None] exactly when the rite's + lectionary is not built at all (the citation list is then also + []): a rite that HAS a lectionary is expected to resolve [Some] on + every day it covers, the same total-coverage discipline + {!Validate}'s own ["formulary"] check holds it to. [None] is never + a per-day "no Mass today" answer for a rite that otherwise + resolves readings -- that shape is coverage FAILURE, not a + legitimate outcome, which is exactly what makes the [Validate] + check meaningful. [temporal_at] is a callback so the rite can reach another date's temporal identity (the preceding Sunday's, for the ferial rule) diff --git a/lib/kernel/validate.ml b/lib/kernel/validate.ml index c9d4263..ce48615 100644 --- a/lib/kernel/validate.ml +++ b/lib/kernel/validate.ml @@ -453,5 +453,34 @@ let run (rite : ('s, 'r) Rite.t) (layer : 'r Layer.t) ~year = fail date "citations" (Printf.sprintf "expected exactly one First and one Gospel, got [%s]" (String.concat "," (List.map Citation.part_to_string sorted)))) + resolved; + (* ---- Formulary invariant (Task 3, celebrant-rubrics-phase1) ---- + + Same rite-agnostic gating as the citation checks immediately + above, and for the same reason: a rite whose lectionary is not + built returns [(None, [])] from {!Rite.readings} on every day, so + [year_has_formulary] is false and this check never fires for it. + A rite that resolves a formulary AT ALL is held to resolving one + on every day of the year -- a day that says no Mass at all is a + defect, not a gap, the same discipline the ["citations-unresolved"] + check above already holds for the citations themselves. One check + name, not two: unlike [citations], there is no separate + "well-formed but wrong" shape to distinguish -- [Mass_formulary.t + option] is either the day's answer or it is missing, so + [year_has_formulary] gates a single ["formulary"] label. *) + let year_has_formulary = + Array.exists + (fun (d : ('s, 'r) Liturgical_day.t) -> d.Liturgical_day.formulary <> None) + resolved + in + if year_has_formulary then + Array.iter + (fun (d : ('s, 'r) Liturgical_day.t) -> + match d.Liturgical_day.formulary with + | Some _ -> () + | None -> + fail d.Liturgical_day.date "formulary" + "no Mass formulary resolved for this day: the lectionary chain fell through \ + every step") resolved); List.rev !failures |
