From 124d7e2261c721c4a49f7203efc276088c7cb217 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Sat, 15 Aug 2026 00:16:35 +0200 Subject: kernel+ef: resolve readings, chain steps 1 and 2 Liturgical_day.citations has read "always empty until Plan 4" since Plan 3; it is now filled. Rite.t gains a readings function, rite-supplied for the same reason transfer_target is: what a day with no proper falls back to is a rubric, not a universal. Calendar calls it and passes its own temporal function as the callback the rite needs to reach another date. Steps 1 and 2 only: the observed celebration's own proper, else the day's own temporal slug. Nothing encodes "Lent has daily propers" -- the presence of an entry is the discriminator. --- lib/kernel/calendar.ml | 4 +++- lib/kernel/rite.ml | 6 ++++++ lib/kernel/rite.mli | 14 +++++++++++++ lib/rites/rite_ef/lectionary_ef.ml | 40 +++++++++++++++++++++++++++++++++++++ lib/rites/rite_ef/lectionary_ef.mli | 15 ++++++++++++++ lib/rites/rite_ef/rite_ef.ml | 4 +++- lib/rites/rite_ef/rite_ef.mli | 4 ++++ 7 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 lib/rites/rite_ef/lectionary_ef.ml create mode 100644 lib/rites/rite_ef/lectionary_ef.mli (limited to 'lib') diff --git a/lib/kernel/calendar.ml b/lib/kernel/calendar.ml index cffdf69..724641d 100644 --- a/lib/kernel/calendar.ml +++ b/lib/kernel/calendar.ml @@ -435,7 +435,9 @@ let build_day (rite : ('s, 'r) Rite.t) (idx : 'r Layer.by_date) transferred_in; transferred_out; omitted; - citations = []; + citations = + rite.Rite.readings ~observed:resolution.Precedence.observed.Precedence.cel ~temporal ~date + ~temporal_at:rite.Rite.temporal; } let year (rite : ('s, 'r) Rite.t) (layer : 'r Layer.t) (y : int) : diff --git a/lib/kernel/rite.ml b/lib/kernel/rite.ml index 89fceb6..91326a6 100644 --- a/lib/kernel/rite.ml +++ b/lib/kernel/rite.ml @@ -11,4 +11,10 @@ type ('s, 'r) t = { season_runs : 's list; transfer_target : 'r Precedence.candidate -> Date.t -> (Date.t -> 'r Celebration.t) -> Date.t; + readings : + observed:'r Celebration.t -> + temporal:('s, 'r) Temporal.t -> + date:Date.t -> + temporal_at:(Date.t -> ('s, 'r) Temporal.t) -> + Citation.t list; } diff --git a/lib/kernel/rite.mli b/lib/kernel/rite.mli index ffe9471..a41e034 100644 --- a/lib/kernel/rite.mli +++ b/lib/kernel/rite.mli @@ -59,4 +59,18 @@ type ('s, 'r) t = { [transfer_target] for a concrete termination argument (a structural step bound, not an appeal to the real calendar's own structure). *) + readings : + observed:'r Celebration.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. + + [temporal_at] is a callback so the rite can reach another date's + temporal identity (the preceding Sunday's, for the ferial rule) + without re-implementing the temporal cycle -- the same shape + [transfer_target]'s own [occupant] callback established. *) } diff --git a/lib/rites/rite_ef/lectionary_ef.ml b/lib/rites/rite_ef/lectionary_ef.ml new file mode 100644 index 0000000..f9ce9ab --- /dev/null +++ b/lib/rites/rite_ef/lectionary_ef.ml @@ -0,0 +1,40 @@ +open Colitur_kernel + +(* [data/ef/lectionary.sexp] is located relative to the BUILD TREE, not the + process's own cwd -- the same reasoning bin/main.ml's own [data_dir] + documents at length: cwd varies with how the binary that eventually links + this library is invoked (a user's shell for `dune exec colitur --`, a + dune cram test's own sandboxed temp directory, `_build/default/test/` for + `dune test`), and nothing in this project's build pins it to the + repository root. [Sys.executable_name] resolves through /proc/self/exe on + Linux, so it reports the executable's own canonical absolute path even + when launched through a symlink (dune's cram sandbox places exactly one; + confirmed working there already by bin/main.ml). Every executable that + links this library -- bin/main.exe, test/test_colitur.exe, + tools/*.exe -- sits exactly one directory below _build/default/, so + climbing up twice and back down into data/ef always finds the file, + regardless of the caller's own cwd. *) +let data_path () = + Filename.dirname (Filename.dirname Sys.executable_name) ^ "/data/ef/lectionary.sexp" + +(* Loaded once, at module initialisation: the data is year-independent and + build_day is called 3 074 246 times over the full domain. *) +let lectionary = + match Lectionary.load (data_path ()) with + | Ok l -> l + | Error e -> failwith ("lectionary_ef: " ^ e) + +(* Step 1: the observed celebration's own proper. + Step 2: the day's own temporal slug. + + Nothing here encodes "Lent has daily propers": the presence of an entry is + the discriminator. Verified against lectio -- Lent 1 Monday returns its own + Ezech 34:11-16, while Advent, Christmas and post-Pentecost Mondays return + their Sunday's Mass. *) +let readings ~observed ~temporal ~date:_ ~temporal_at:_ = + match observed.Celebration.citations with + | _ :: _ as cs -> cs + | [] -> ( + match Lectionary.find lectionary temporal.Temporal.office.Celebration.slug with + | Some cs -> cs + | None -> []) diff --git a/lib/rites/rite_ef/lectionary_ef.mli b/lib/rites/rite_ef/lectionary_ef.mli new file mode 100644 index 0000000..fe9359e --- /dev/null +++ b/lib/rites/rite_ef/lectionary_ef.mli @@ -0,0 +1,15 @@ +open Colitur_kernel + +(** The EF lectionary resolution chain. All rubric knowledge about what a day + with no proper falls back to lives here, not in the kernel. + + Steps 1 and 2 only (Task 4): the observed celebration's own proper, else + the day's own temporal slug in the lectionary. A day matching neither + gets [] for now -- the ferial fallback to the preceding Sunday (Task 5) + and the Commons (Task 6) are not built here. *) +val readings : + observed:Vocab_ef.rank Celebration.t -> + temporal:(Vocab_ef.season, Vocab_ef.rank) Temporal.t -> + date:Date.t -> + temporal_at:(Date.t -> (Vocab_ef.season, Vocab_ef.rank) Temporal.t) -> + Citation.t list diff --git a/lib/rites/rite_ef/rite_ef.ml b/lib/rites/rite_ef/rite_ef.ml index 7a29421..4cf52a6 100644 --- a/lib/rites/rite_ef/rite_ef.ml +++ b/lib/rites/rite_ef/rite_ef.ml @@ -7,6 +7,7 @@ module Vocab_ef = Vocab_ef module Temporal_ef = Temporal_ef module Precedence_ef = Precedence_ef +module Lectionary_ef = Lectionary_ef open Colitur_kernel @@ -21,4 +22,5 @@ let context : (Vocab_ef.season, Vocab_ef.rank) Rite.t = disposition = Precedence_ef.disposition; admit = Precedence_ef.admit }; season_runs = Vocab_ef.seasons; - transfer_target = Precedence_ef.transfer_target } + transfer_target = Precedence_ef.transfer_target; + readings = Lectionary_ef.readings } diff --git a/lib/rites/rite_ef/rite_ef.mli b/lib/rites/rite_ef/rite_ef.mli index e2b3e6d..710db1d 100644 --- a/lib/rites/rite_ef/rite_ef.mli +++ b/lib/rites/rite_ef/rite_ef.mli @@ -9,6 +9,7 @@ module Vocab_ef = Vocab_ef module Temporal_ef = Temporal_ef module Precedence_ef = Precedence_ef +module Lectionary_ef = Lectionary_ef (** The EF rite, bundled (design spec's [RITE] signature, realised as a {!Colitur_kernel.Rite.t} value rather than a functor -- see rite.mli): @@ -24,6 +25,9 @@ module Precedence_ef = Precedence_ef - [transfer_target]: {!Precedence_ef.transfer_target}, RG 96 (see that value's own documentation for the termination and forward-progress argument {!Colitur_kernel.Rite.t.transfer_target}'s contract requires). + - [readings]: {!Lectionary_ef.readings} -- the observed celebration's own + proper, else the day's own temporal slug in data/ef/lectionary.sexp + (chain steps 1-2; the ferial fallback and the Commons are later work). Deliberately carries no [sanctoral]/[lectionary] fields the way the original design-doc sketch of [RITE] does: {!Colitur_kernel.Rite.t} (the -- cgit v1.3