summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-14 17:05:51 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-14 17:05:51 +0200
commit6f5814ccdbc839b75c3e6de95a8ba9a755bc6ece (patch)
treefdb96f0b5da902e03d6aa1ae5e14d46755d9ea96
parent2c0187f42cc45864932aba2529f876be86ddba39 (diff)
downloadcolitur-6f5814ccdbc839b75c3e6de95a8ba9a755bc6ece.tar.gz
colitur-6f5814ccdbc839b75c3e6de95a8ba9a755bc6ece.zip
data(ef): bootstrap the temporal lectionary from lectio
119 entries, Epistle + Gospel only. The generator refuses a section carrying exactly one of the two: an Epistle without a Gospel is malformed and wants investigating, not shipping. SHA-256 of the source INI is in the provenance header, as with sanctoral.sexp.
-rw-r--r--data/ef/lectionary.sexp366
-rw-r--r--test/dune1
-rw-r--r--test/test_lectionary.ml16
-rw-r--r--tools/bootstrap_lectionary.ml105
-rw-r--r--tools/dune8
5 files changed, 495 insertions, 1 deletions
diff --git a/data/ef/lectionary.sexp b/data/ef/lectionary.sexp
new file mode 100644
index 0000000..24f3cf5
--- /dev/null
+++ b/data/ef/lectionary.sexp
@@ -0,0 +1,366 @@
+; data/ef/lectionary.sexp -- EF (1962) temporal lectionary (Epistle +
+; Gospel citations, never scripture text), bootstrapped from lectio.
+; Generator: tools/bootstrap_lectionary.ml -- do not hand-edit; re-run the
+; generator against a newer lectio and commit the diff instead.
+;
+; Source: ../lectio/internal/caldata/tridentine-lectionary.ini
+; SHA-256: fde533289fb620fea33386b0a3e733ada3c1d01f10a36d8a7f5a2b500c92740e
+; 119 entries. Regenerate with:
+; eval $(opam env) && dune exec tools/bootstrap_lectionary.exe -- ../lectio/internal/caldata/tridentine-lectionary.ini data/ef/lectionary.sexp
+((ef-advent-2-monday
+ (((part First) (reference "Rom 15:4-13"))
+ ((part Gospel) (reference "Matt 11:2-10"))))
+ (ef-advent-2-saturday
+ (((part First) (reference "Rom 15:4-13"))
+ ((part Gospel) (reference "Matt 11:2-10"))))
+ (ef-advent-2-thursday
+ (((part First) (reference "Rom 15:4-13"))
+ ((part Gospel) (reference "Matt 11:2-10"))))
+ (ef-advent-2-tuesday
+ (((part First) (reference "Rom 15:4-13"))
+ ((part Gospel) (reference "Matt 11:2-10"))))
+ (ef-advent-ember-fri
+ (((part First) (reference "Isa 11:1-5"))
+ ((part Gospel) (reference "Luke 1:39-47"))))
+ (ef-advent-ember-sat
+ (((part First) (reference "Eph 2:19-22"))
+ ((part Gospel) (reference "John 20:24-29"))))
+ (ef-advent-ember-wed
+ (((part First) (reference "Isa 7:10-15"))
+ ((part Gospel) (reference "Luke 1:26-38"))))
+ (ef-advent-sunday-1
+ (((part First) (reference "Rom 13:11-14"))
+ ((part Gospel) (reference "Luke 21:25-33"))))
+ (ef-advent-sunday-2
+ (((part First) (reference "Rom 15:4-13"))
+ ((part Gospel) (reference "Matt 11:2-10"))))
+ (ef-advent-sunday-3
+ (((part First) (reference "Phil 4:4-7"))
+ ((part Gospel) (reference "John 1:19-28"))))
+ (ef-advent-sunday-4
+ (((part First) (reference "1 Cor. 4:1-5"))
+ ((part Gospel) (reference "Luke 3:1-6"))))
+ (ef-ascension
+ (((part First) (reference "Acts 1:1-11"))
+ ((part Gospel) (reference "Mark 16:14-20"))))
+ (ef-ascension-vigil
+ (((part First) (reference "Eph. 4:7-13."))
+ ((part Gospel) (reference "John 17:1-11."))))
+ (ef-ash-wednesday
+ (((part First) (reference "Joel 2:12-19"))
+ ((part Gospel) (reference "Matt 6:16-21"))))
+ (ef-christ-the-king
+ (((part First) (reference "Col 1:12-20."))
+ ((part Gospel) (reference "John 18:33-37"))))
+ (ef-christmas-sunday-0
+ (((part First) (reference "Gal 4:1-7"))
+ ((part Gospel) (reference "Luke 2:33-40"))))
+ (ef-circumcision
+ (((part First) (reference "Titus 2:11-15"))
+ ((part Gospel) (reference "Luke 2:21"))))
+ (ef-easter-1-friday
+ (((part First) (reference "1 Pet 3:18-22"))
+ ((part Gospel) (reference "Matt 28:16-20"))))
+ (ef-easter-1-monday
+ (((part First) (reference "Acts 10:37-43."))
+ ((part Gospel) (reference "Luke 24:13-35"))))
+ (ef-easter-1-saturday
+ (((part First) (reference "1 Pet 2:1-10"))
+ ((part Gospel) (reference "John 20:1-9"))))
+ (ef-easter-1-thursday
+ (((part First) (reference "Acts 8:26-40"))
+ ((part Gospel) (reference "John 20:11-18"))))
+ (ef-easter-1-tuesday
+ (((part First) (reference "Acts 13:16; 13:26-33"))
+ ((part Gospel) (reference "Luke 24:36-47"))))
+ (ef-easter-1-wednesday
+ (((part First) (reference "Acts 3:13-15; 3:17-19"))
+ ((part Gospel) (reference "John 21:1-14"))))
+ (ef-easter-2-saturday
+ (((part First) (reference "Ecclus 24:14-16"))
+ ((part Gospel) (reference "John 19:25-27"))))
+ (ef-easter-3-saturday
+ (((part First) (reference "Ecclus 24:14-16"))
+ ((part Gospel) (reference "John 19:25-27"))))
+ (ef-easter-5-saturday
+ (((part First) (reference "Ecclus 24:14-16"))
+ ((part Gospel) (reference "John 19:25-27"))))
+ (ef-easter-6-saturday
+ (((part First) (reference "Ecclus 24:14-16"))
+ ((part Gospel) (reference "John 19:25-27"))))
+ (ef-easter-8-friday
+ (((part First) (reference "Joel 2:23-24; 26-27"))
+ ((part Gospel) (reference "Luke 5:17-26"))))
+ (ef-easter-8-monday
+ (((part First) (reference "Acts 10:34, 42-48"))
+ ((part Gospel) (reference "John 3:16-21"))))
+ (ef-easter-8-saturday
+ (((part First) (reference "Rom 5:1-5."))
+ ((part Gospel) (reference "Luke 4:38-44."))))
+ (ef-easter-8-thursday
+ (((part First) (reference "Acts 8:5-8"))
+ ((part Gospel) (reference "Luke 9:1-6"))))
+ (ef-easter-8-tuesday
+ (((part First) (reference "Acts 8:14-17."))
+ ((part Gospel) (reference "John 10:1-10."))))
+ (ef-easter-8-wednesday
+ (((part First) (reference "Acts 5:12-16"))
+ ((part Gospel) (reference "John 6:44-52."))))
+ (ef-easter-sunday
+ (((part First) (reference "1 Cor 5:7-8"))
+ ((part Gospel) (reference "Mark 16:1-7"))))
+ (ef-easter-sunday-3
+ (((part First) (reference "1 Pet 2:21-25"))
+ ((part Gospel) (reference "John 10:11-16"))))
+ (ef-easter-sunday-4
+ (((part First) (reference "1 Pet 2:11-19"))
+ ((part Gospel) (reference "John 16:16-22"))))
+ (ef-easter-sunday-5
+ (((part First) (reference "Jas 1:17-21"))
+ ((part Gospel) (reference "John 16:5-14"))))
+ (ef-easter-sunday-6
+ (((part First) (reference "Jas 1:22-27"))
+ ((part Gospel) (reference "John 16:23-30"))))
+ (ef-easter-sunday-7
+ (((part First) (reference "1 Pet 4:7-11."))
+ ((part Gospel) (reference "John 15:26-27; 16:1-4."))))
+ (ef-epiphany
+ (((part First) (reference "Isa 60:1-6"))
+ ((part Gospel) (reference "Matt 2:1-12"))))
+ (ef-lent-1-friday
+ (((part First) (reference "Ezech 18:20-28"))
+ ((part Gospel) (reference "John 5:1-15"))))
+ (ef-lent-1-monday
+ (((part First) (reference "Ezech 34:11-16"))
+ ((part Gospel) (reference "Matt 25:31-46"))))
+ (ef-lent-1-saturday
+ (((part First) (reference "1 Thess. 5:14-23"))
+ ((part Gospel) (reference "Matt 17:1-9"))))
+ (ef-lent-1-thursday
+ (((part First) (reference "Ezech 18:1-9"))
+ ((part Gospel) (reference "Matt 15:21-28"))))
+ (ef-lent-1-tuesday
+ (((part First) (reference "Isa 55:6-11"))
+ ((part Gospel) (reference "Matt 21:10-17"))))
+ (ef-lent-1-wednesday
+ (((part First) (reference "3 Kgs. 19:3-8"))
+ ((part Gospel) (reference "Matt 12:38-50"))))
+ (ef-lent-2-friday
+ (((part First) (reference "Gen 37:6-22"))
+ ((part Gospel) (reference "Matt 21:33-46"))))
+ (ef-lent-2-monday
+ (((part First) (reference "Dan 9:15-19"))
+ ((part Gospel) (reference "John 8:21-29"))))
+ (ef-lent-2-saturday
+ (((part First) (reference "Gen 27:6-40"))
+ ((part Gospel) (reference "Luke 15:11-32"))))
+ (ef-lent-2-thursday
+ (((part First) (reference "Jer 17:5-10"))
+ ((part Gospel) (reference "Luke 16:19-31"))))
+ (ef-lent-2-tuesday
+ (((part First) (reference "3 Kings 17:8-16"))
+ ((part Gospel) (reference "Matt 23:1-12"))))
+ (ef-lent-2-wednesday
+ (((part First) (reference "Esther 13:8-11; 15-17."))
+ ((part Gospel) (reference "Matt 20:17-28"))))
+ (ef-lent-3-friday
+ (((part First) (reference "Num 20:1, 3; 6-13."))
+ ((part Gospel) (reference "John 4:5-42"))))
+ (ef-lent-3-monday
+ (((part First) (reference "4 Kings 5:1-15"))
+ ((part Gospel) (reference "Luke 4:23-30"))))
+ (ef-lent-3-saturday
+ (((part First) (reference "Dan 13:1-9, 15-17, 19-30, 33-62."))
+ ((part Gospel) (reference "John 8:1-11"))))
+ (ef-lent-3-thursday
+ (((part First) (reference "Jer 7:1-7"))
+ ((part Gospel) (reference "Luke 4:38-44."))))
+ (ef-lent-3-tuesday
+ (((part First) (reference "4 Kings 4:1-7"))
+ ((part Gospel) (reference "Matt 18:15-22"))))
+ (ef-lent-3-wednesday
+ (((part First) (reference "Ex 20:12-24"))
+ ((part Gospel) (reference "Matt 15:1-20"))))
+ (ef-lent-4-friday
+ (((part First) (reference "3 Kings 17:17-24"))
+ ((part Gospel) (reference "John 11:1-45"))))
+ (ef-lent-4-monday
+ (((part First) (reference "3 Kings 3:16-28"))
+ ((part Gospel) (reference "John 2:13-25"))))
+ (ef-lent-4-saturday
+ (((part First) (reference "Isa 49:8-15"))
+ ((part Gospel) (reference "John 8:12-20"))))
+ (ef-lent-4-thursday
+ (((part First) (reference "4 Kings 4:25-38"))
+ ((part Gospel) (reference "Luke 7:11-16"))))
+ (ef-lent-4-tuesday
+ (((part First) (reference "Ex 32:7-14"))
+ ((part Gospel) (reference "John 7:14-31"))))
+ (ef-lent-4-wednesday
+ (((part First) (reference "Isa. 1:16-19"))
+ ((part Gospel) (reference "John 9:1-38"))))
+ (ef-lent-after-ashes-friday
+ (((part First) (reference "Isa 58:1-9"))
+ ((part Gospel) (reference "Matt 5:43-48; 6:1-4"))))
+ (ef-lent-after-ashes-saturday
+ (((part First) (reference "Isa 58:9-14"))
+ ((part Gospel) (reference "Mark 6:47-56"))))
+ (ef-lent-after-ashes-thursday
+ (((part First) (reference "Isa 38:1-6"))
+ ((part Gospel) (reference "Matt 8:5-13"))))
+ (ef-lent-sunday-1
+ (((part First) (reference "2 Cor. 6:1-10"))
+ ((part Gospel) (reference "Matt 4:1-11"))))
+ (ef-lent-sunday-2
+ (((part First) (reference "1 Thess. 4:1-7"))
+ ((part Gospel) (reference "Matt 17:1-9"))))
+ (ef-lent-sunday-3
+ (((part First) (reference "Eph 5:1-9"))
+ ((part Gospel) (reference "Luke 11:14-28"))))
+ (ef-lent-sunday-4
+ (((part First) (reference "Gal 4:22-31"))
+ ((part Gospel) (reference "John 6:1-15"))))
+ (ef-low-sunday
+ (((part First) (reference "1 John 5:4-10"))
+ ((part Gospel) (reference "John 20:19-31"))))
+ (ef-nativity
+ (((part First) (reference "Heb 1:1-12"))
+ ((part Gospel) (reference "John 1:1-14"))))
+ (ef-palm-sunday
+ (((part First) (reference "Phil 2:5-11"))
+ ((part Gospel) (reference "Matt. 26:36-75; 27:1-60."))))
+ (ef-passion-sunday
+ (((part First) (reference "Heb 9:11-15."))
+ ((part Gospel) (reference "John 8:46-59."))))
+ (ef-passiontide-0-friday
+ (((part First) (reference "Jer 17:13-18"))
+ ((part Gospel) (reference "John 11:47-54"))))
+ (ef-passiontide-0-monday
+ (((part First) (reference "Jonas 3:1-10"))
+ ((part Gospel) (reference "John 7:32-39"))))
+ (ef-passiontide-0-saturday
+ (((part First) (reference "Jer 18:18-23"))
+ ((part Gospel) (reference "John 12:10-36"))))
+ (ef-passiontide-0-thursday
+ (((part First) (reference "Dan 3:25, 34-45."))
+ ((part Gospel) (reference "Luke 7:36-50"))))
+ (ef-passiontide-0-tuesday
+ (((part First) (reference "Jer 11:18-20"))
+ ((part Gospel) (reference "Mark 14:32-72; 15, 1-46"))))
+ (ef-passiontide-0-wednesday
+ (((part First) (reference "Lev 19:1-2, 11-19, 25"))
+ ((part Gospel) (reference "John 10:22-38"))))
+ (ef-pentecost
+ (((part First) (reference "Acts 2:1-11."))
+ ((part Gospel) (reference "John 14:23-31."))))
+ (ef-pentecost-vigil
+ (((part First) (reference "Acts 19:1-8."))
+ ((part Gospel) (reference "John 14:15-21."))))
+ (ef-september-ember-fri
+ (((part First) (reference "Osee 14:2-10"))
+ ((part Gospel) (reference "Luke 7:36-50"))))
+ (ef-september-ember-sat
+ (((part First) (reference "Ezek 1:10-14"))
+ ((part Gospel) (reference "Matt 9:9-13"))))
+ (ef-september-ember-wed
+ (((part First) (reference "2 Esd. 8:1-10"))
+ ((part Gospel) (reference "Mark 9:16-28"))))
+ (ef-septuagesima-sunday-1
+ (((part First) (reference "1 Cor. 9:24-27; 10:1-5"))
+ ((part Gospel) (reference "Matt 20:1-16"))))
+ (ef-septuagesima-sunday-2
+ (((part First) (reference "2 Cor. 11:19-33; 12:1-9"))
+ ((part Gospel) (reference "Luke 8:4-15"))))
+ (ef-septuagesima-sunday-3
+ (((part First) (reference "1 Cor. 13:1-13"))
+ ((part Gospel) (reference "Luke 18:31-43"))))
+ (ef-time-after-epiphany-sunday-1
+ (((part First) (reference "Col 3:12-17"))
+ ((part Gospel) (reference "Luke 2:42-52"))))
+ (ef-time-after-epiphany-sunday-2
+ (((part First) (reference "Rom 12:6-16"))
+ ((part Gospel) (reference "John 2:1-11"))))
+ (ef-time-after-epiphany-sunday-3
+ (((part First) (reference "Rom 12:16-21"))
+ ((part Gospel) (reference "Matt 8:1-13"))))
+ (ef-time-after-epiphany-sunday-4
+ (((part First) (reference "Rom 13:8-10"))
+ ((part Gospel) (reference "Matt 8:23-27"))))
+ (ef-time-after-epiphany-sunday-5
+ (((part First) (reference "Col 3:12-17"))
+ ((part Gospel) (reference "Matt 13:24-30"))))
+ (ef-time-after-epiphany-sunday-6
+ (((part First) (reference "1 Thess 1:2-10"))
+ ((part Gospel) (reference "Matt 13:31-35"))))
+ (ef-time-after-pentecost-sunday-10
+ (((part First) (reference "1 Cor. 12:2-11"))
+ ((part Gospel) (reference "Luke 18:9-14"))))
+ (ef-time-after-pentecost-sunday-11
+ (((part First) (reference "1 Cor. 15:1-10"))
+ ((part Gospel) (reference "Mark 7:31-37"))))
+ (ef-time-after-pentecost-sunday-12
+ (((part First) (reference "2 Cor. 3:4-9"))
+ ((part Gospel) (reference "Luke 10:23-37"))))
+ (ef-time-after-pentecost-sunday-13
+ (((part First) (reference "Gal 3:16-22"))
+ ((part Gospel) (reference "Luke 17:11-19"))))
+ (ef-time-after-pentecost-sunday-14
+ (((part First) (reference "Gal 5:16-24"))
+ ((part Gospel) (reference "Matt 6:24-33"))))
+ (ef-time-after-pentecost-sunday-15
+ (((part First) (reference "Gal 5:25-26; 6:1-10"))
+ ((part Gospel) (reference "Luke 7:11-16"))))
+ (ef-time-after-pentecost-sunday-16
+ (((part First) (reference "Eph 3:13-21"))
+ ((part Gospel) (reference "Luke 14:1-11"))))
+ (ef-time-after-pentecost-sunday-17
+ (((part First) (reference "Eph 4:1-6"))
+ ((part Gospel) (reference "Matt 22:34-46"))))
+ (ef-time-after-pentecost-sunday-18
+ (((part First) (reference "1 Cor. 1:4-8"))
+ ((part Gospel) (reference "Matt 9:1-8"))))
+ (ef-time-after-pentecost-sunday-19
+ (((part First) (reference "Eph 4:23-28"))
+ ((part Gospel) (reference "Matt 22:1-14"))))
+ (ef-time-after-pentecost-sunday-2
+ (((part First) (reference "1 John 3:13-18."))
+ ((part Gospel) (reference "Luke 14:16-24."))))
+ (ef-time-after-pentecost-sunday-20
+ (((part First) (reference "Eph 5:15-21"))
+ ((part Gospel) (reference "John 4:46-53"))))
+ (ef-time-after-pentecost-sunday-21
+ (((part First) (reference "Eph 6:10-17"))
+ ((part Gospel) (reference "Matt 18:23-35"))))
+ (ef-time-after-pentecost-sunday-22
+ (((part First) (reference "Phil 1:6-11"))
+ ((part Gospel) (reference "Matt 22:15-21"))))
+ (ef-time-after-pentecost-sunday-23
+ (((part First) (reference "Phil 3:17-21; 4:1-3"))
+ ((part Gospel) (reference "Matt 9:18-26"))))
+ (ef-time-after-pentecost-sunday-24
+ (((part First) (reference "Col 1:9-14"))
+ ((part Gospel) (reference "Matt 24:15-35"))))
+ (ef-time-after-pentecost-sunday-3
+ (((part First) (reference "1 Pet. 5:6-11"))
+ ((part Gospel) (reference "Luke 15:1-10"))))
+ (ef-time-after-pentecost-sunday-4
+ (((part First) (reference "Rom 8:18-23"))
+ ((part Gospel) (reference "Luke 5:1-11"))))
+ (ef-time-after-pentecost-sunday-5
+ (((part First) (reference "1 Pet 3:8-15."))
+ ((part Gospel) (reference "Matt 5:20-24."))))
+ (ef-time-after-pentecost-sunday-6
+ (((part First) (reference "Rom 6:3-11."))
+ ((part Gospel) (reference "Mark 8:1-9"))))
+ (ef-time-after-pentecost-sunday-7
+ (((part First) (reference "Rom 6:19-23"))
+ ((part Gospel) (reference "Matt 7:15-21"))))
+ (ef-time-after-pentecost-sunday-8
+ (((part First) (reference "Rom 8:12-17"))
+ ((part Gospel) (reference "Luke 16:1-9"))))
+ (ef-time-after-pentecost-sunday-9
+ (((part First) (reference "1 Cor. 10:6-13"))
+ ((part Gospel) (reference "Luke 19:41-47"))))
+ (ef-trinity
+ (((part First) (reference "Rom 11:33-36."))
+ ((part Gospel) (reference "Matt 28:18-20")))))
diff --git a/test/dune b/test/dune
index 714ffb2..fe5e4cc 100644
--- a/test/dune
+++ b/test/dune
@@ -6,6 +6,7 @@
../data/ef/adjustments.sexp
../data/ef/expected-divergences.sexp
../data/ef/expected-divergences-missalemeum.sexp
+ ../data/ef/lectionary.sexp
fixtures/lectio-ef-2005-2050.txt
fixtures/missalemeum-ef-2026-2027.txt)
(preprocess
diff --git a/test/test_lectionary.ml b/test/test_lectionary.ml
index fba890a..c6a1cac 100644
--- a/test/test_lectionary.ml
+++ b/test/test_lectionary.ml
@@ -77,9 +77,23 @@ let test_load_never_raises () =
| Error _ -> ()
| Ok _ -> Alcotest.fail "missing file: expected Error, got Ok"
+let test_ef_data_file_loads () =
+ match Lectionary.load "../data/ef/lectionary.sexp" with
+ | Error e -> Alcotest.fail e
+ | Ok l ->
+ Alcotest.(check int) "entry count" 119
+ (List.length (Lectionary.entries l));
+ (match Lectionary.find l (slug "ef-lent-1-monday") with
+ | Some [ a; b ] ->
+ Alcotest.(check string) "epistle" "Ezech 34:11-16" a.Citation.reference;
+ Alcotest.(check string) "gospel" "Matt 25:31-46" b.Citation.reference
+ | Some _ -> Alcotest.fail "expected exactly two citations"
+ | None -> Alcotest.fail "ef-lent-1-monday missing")
+
let suite =
[ ("find present", `Quick, test_find_present);
("find absent", `Quick, test_find_absent);
("duplicate slug rejected", `Quick, test_duplicate_slug_rejected);
("sexp round-trip", `Quick, test_sexp_round_trip);
- ("load never raises", `Quick, test_load_never_raises) ]
+ ("load never raises", `Quick, test_load_never_raises);
+ ("ef data file loads", `Quick, test_ef_data_file_loads) ]
diff --git a/tools/bootstrap_lectionary.ml b/tools/bootstrap_lectionary.ml
new file mode 100644
index 0000000..52c7e50
--- /dev/null
+++ b/tools/bootstrap_lectionary.ml
@@ -0,0 +1,105 @@
+(* Bootstraps data/ef/lectionary.sexp from lectio's tridentine-lectionary.ini.
+ Lives in tools/ and not the kernel for the same reason
+ bootstrap_sanctoral.ml does: reading someone else's INI needs a reader,
+ which does not belong in a pure kernel.
+
+ Only `first` and `gospel` are mapped -- the plan's Global Constraints fix
+ the scope at Epistle + Gospel, and lectio's data carries nothing else. *)
+
+open Colitur_kernel
+
+let default_source = "../lectio/internal/caldata/tridentine-lectionary.ini"
+let default_dest = "data/ef/lectionary.sexp"
+
+let die fmt =
+ Printf.ksprintf
+ (fun s -> prerr_endline ("bootstrap_lectionary: " ^ s); exit 1)
+ fmt
+
+type section = { name : string; fields : (string * string) list }
+
+let parse_ini path =
+ let ic = try open_in path with Sys_error e -> die "%s" e in
+ let sections = ref [] and cur = ref None in
+ let flush () =
+ match !cur with
+ | Some (n, fs) -> sections := { name = n; fields = List.rev fs } :: !sections
+ | None -> ()
+ in
+ (try
+ while true do
+ let line = String.trim (input_line ic) in
+ if line = "" || line.[0] = ';' || line.[0] = '#' then ()
+ else if line.[0] = '[' then begin
+ flush ();
+ cur := Some (String.sub line 1 (String.length line - 2), [])
+ end
+ else
+ match String.index_opt line '=' with
+ | None -> die "%s: cannot parse line %S" path line
+ | Some i ->
+ let k = String.trim (String.sub line 0 i) in
+ let v =
+ String.trim (String.sub line (i + 1) (String.length line - i - 1))
+ in
+ cur :=
+ (match !cur with
+ | Some (n, fs) -> Some (n, (k, v) :: fs)
+ | None -> die "%s: field %S before any section" path k)
+ done
+ with End_of_file -> ());
+ flush ();
+ close_in ic;
+ List.rev !sections
+
+let convert sec =
+ let slug =
+ match Slug.of_string sec.name with
+ | Ok s -> s
+ | Error e -> die "bad slug %S: %s" sec.name e
+ in
+ let cite part key =
+ match List.assoc_opt key sec.fields with
+ | None | Some "" -> None
+ | Some reference -> Some { Citation.part; reference }
+ in
+ let cs = List.filter_map Fun.id [ cite Citation.First "first"; cite Citation.Gospel "gospel" ] in
+ if cs = [] then die "%s: no first/gospel field" sec.name;
+ if List.length cs = 1 then
+ die "%s: has one reading, not two -- an Epistle without a Gospel (or the \
+ reverse) is malformed and must be investigated, not silently shipped"
+ sec.name;
+ (slug, cs)
+
+let sha256 path =
+ let ic = Unix.open_process_in (Printf.sprintf "sha256sum %s" (Filename.quote path)) in
+ let line = try input_line ic with End_of_file -> die "sha256sum failed" in
+ ignore (Unix.close_process_in ic);
+ List.hd (String.split_on_char ' ' line)
+
+let () =
+ let src = if Array.length Sys.argv > 1 then Sys.argv.(1) else default_source in
+ let dst = if Array.length Sys.argv > 2 then Sys.argv.(2) else default_dest in
+ let secs = parse_ini src in
+ let entries = List.map convert secs in
+ let lect =
+ match Lectionary.of_entries entries with
+ | Ok l -> l
+ | Error e -> die "%s" e
+ in
+ let oc = open_out dst in
+ Printf.fprintf oc
+ "; data/ef/lectionary.sexp -- EF (1962) temporal lectionary (Epistle +\n\
+ ; Gospel citations, never scripture text), bootstrapped from lectio.\n\
+ ; Generator: tools/bootstrap_lectionary.ml -- do not hand-edit; re-run the\n\
+ ; generator against a newer lectio and commit the diff instead.\n\
+ ;\n\
+ ; Source: %s\n\
+ ; SHA-256: %s\n\
+ ; %d entries. Regenerate with:\n\
+ ; eval $(opam env) && dune exec tools/bootstrap_lectionary.exe -- %s %s\n"
+ src (sha256 src) (List.length entries) src dst;
+ Sexplib.Sexp.output_hum oc (Lectionary.sexp_of_t lect);
+ output_char oc '\n';
+ close_out oc;
+ Printf.printf "bootstrap_lectionary: %d entries -> %s\n" (List.length entries) dst
diff --git a/tools/dune b/tools/dune
index d53cd01..fa6943d 100644
--- a/tools/dune
+++ b/tools/dune
@@ -8,3 +8,11 @@
(executable
(name bootstrap_sanctoral)
(libraries colitur_kernel rite_ef unix sexplib))
+
+; Converts lectio's tridentine-lectionary.ini into data/ef/lectionary.sexp.
+; Run via `dune exec tools/bootstrap_lectionary.exe -- <source.ini> <dest.sexp>`.
+; Same `unix` usage as bootstrap_sanctoral above: shells out to `sha256sum`
+; for the provenance header only, never a new opam dependency.
+(executable
+ (name bootstrap_lectionary)
+ (libraries colitur_kernel unix sexplib))