aboutsummaryrefslogtreecommitdiff
path: root/lib/rites/rite_of/precedence_of.ml
blob: bca6ca9b97105bba4ff52c809f610a9243cd34a4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
(* The Tabula dierum liturgicorum secundum ordinem praecedentiae disposita
   (Missale Romanum, editio typica tertia 2002; "Normae universales de anno
   liturgico et de calendario", printed immediately before it) -- 13 numbered
   entries in three parts. This module transcribes it, one [band] branch per
   entry, the same discipline Rite_ef.Precedence_ef applies to RG 91's 28.

   CITATION DISCIPLINE: the 2002 Missal PDF carries BOTH the Normae universales
   AND the Institutio Generalis Missalis Romani, each independently numbered
   from 1. Every citation below therefore names its document: "Normae n. N" or
   "IGMR n. N". A bare "n. N" is ambiguous and must not appear.

   ENTRY VALUES ARE THE TABLE'S OWN NUMBER TIMES TEN (entry 1 -> 10, entry 13
   -> 130). Identical to Precedence_ef's own convention and adopted for the
   identical reason: a future sub-rank (a movable/fixed split of one row, the
   shape RG 91 entry 14 forced on the EF side) becomes an ordinary position
   BETWEEN neighbours, with no rescale of every other branch. Do NOT express a
   sub-rank as a negative sentinel: Precedence_ef's own history records that as
   a defect, because a negative value avoids collision but inverts ORDERING. *)

open Colitur_kernel

let unclassified = max_int

(* Not a Normae citation. The Tabula distinguishes universal-calendar entries
   (3, 7, 10) from proper-calendar ones (4, 8, 11), and nothing in
   Celebration.t marks that distinction except [layer]. Same mechanism, and
   same naming, as Precedence_ef.{universal_layer,indult_prefix}. *)
let universal_layer = "of-universal"
let proper_prefix = "proper:"
let is_universal layer = String.equal layer universal_layer
let is_proper layer = String.starts_with ~prefix:proper_prefix layer

(* Days measured from Easter, so the Triduum and the Easter Octave are
   identified STRUCTURALLY rather than by slug. Temporal_of emits Good Friday
   and Holy Saturday as ordinary Lent week-6 ferias
   ("of-lent-6-friday"/"of-lent-6-saturday"), so there is no slug to key on --
   the same situation Precedence_ef.band faces for RG 91 entry 2, resolved the
   same way. *)
let easter_offset (ctx : Vocab_of.season Precedence.context) =
  let y = Date.year ctx.date in
  Date.to_rata ctx.date - Date.to_rata (Computus.gregorian_easter y)

let slug_of (c : Vocab_of.rank Precedence.candidate) = Slug.to_string c.cel.slug

(* Tabula I.1: "Triduum paschale Passionis et Resurrectionis Domini."

   HOLY THURSDAY IS DELIBERATELY NOT HERE. The Tabula's own entry 2 reads
   "Feriae Hebdomadae sanctae, a feria II ad feriam V inclusive" -- Monday
   through THURSDAY inclusive -- and the Paschal Triduum begins with the
   evening Mass of the Lord's Supper, an event INSIDE Holy Thursday's civil
   day. At colitur's civil-day granularity (one office per date, never a
   sub-day boundary) the day itself therefore stays with the Holy Week ferias
   at entry 2. This is the SAME granularity decision, on the precedence axis,
   that data/of/expected-divergences-litcal.sexp's own L1 records on the season
   axis (Normae nn. 28/30) -- one decision with two visible consequences, not
   two independent judgements. *)
let is_triduum ctx = List.mem (easter_offset ctx) [ -2; -1; 0 ]

(* Tabula I.2, five clauses in one entry. *)
let entry_2_named =
  [ "of-nativity"; "of-epiphany"; "of-ascension"; "of-pentecost"; "of-ash-wednesday" ]

let is_privileged_sunday (ctx : Vocab_of.season Precedence.context) c =
  ctx.weekday = Date.Sun
  && (match ctx.season with
     | Vocab_of.Advent | Vocab_of.Lent | Vocab_of.Easter -> true
     | Vocab_of.Christmas | Vocab_of.Ordinary_time -> false)
  && c.Precedence.origin = Precedence.Temporal

(* "Feriae Hebdomadae sanctae, a feria II ad feriam V inclusive": Easter-6
   (Monday) through Easter-3 (Thursday). Palm Sunday (Easter-7) is a Sunday of
   Lent, covered by [is_privileged_sunday] in the same entry. *)
let is_holy_week_feria ctx = List.mem (easter_offset ctx) [ -6; -5; -4; -3 ]

(* "Dies infra octavam Paschae": Easter+1 .. Easter+7. Easter Sunday itself is
   entry 1. *)
let is_easter_octave_day ctx =
  let o = easter_offset ctx in
  o >= 1 && o <= 7

let band (ctx : Vocab_of.season Precedence.context)
    (c : Vocab_of.rank Precedence.candidate) : int =
  let temporal = c.Precedence.origin = Precedence.Temporal in
  let layer = c.cel.layer in
  if temporal && is_triduum ctx then 10 (* Tabula I.1 *)
  else if
    temporal
    && (List.mem (slug_of c) entry_2_named
       || is_privileged_sunday ctx c
       || is_holy_week_feria ctx
       || is_easter_octave_day ctx)
  then 20 (* Tabula I.2 *)
  else if c.cel.rank = Vocab_of.Sollemnitas && is_universal layer then
    30 (* Tabula I.3 *)
  else if c.cel.rank = Vocab_of.Sollemnitas && is_proper layer then
    40 (* Tabula I.4 *)
  else unclassified