From 2386a4551aec94252168a8554269ee423d516882 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 01:35:35 +0200 Subject: cmd(lectio-ef-dump): new EF calendar dumper for colitur's differential oracle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prints one line per day, sorted ascending, for a civil-year range: date, weekday, season, week, observed slug, rank, colour, then zero or more +slug tokens for the day's other (losing) candidates. The line format matches colitur's `colitur day` output field-for-field so the two streams diff directly; every column carries lectio's own vocabulary (season names, slugs, rank/colour spellings) with no translation toward colitur's — that mapping belongs to the differential comparator, not this dumper. Uses calendar.Compute with Selection{Form: "old"} over the embedded tridentine layer (caldata.Tridentine()), mirroring the setup already used by oracle_ef_test.go. Lives in cmd/ because internal/calendar cannot be imported outside this module; colitur's build is untouched. Note for the comparator: lectio has no RG 111 commemoration-admission logic, so the trailing +slug tokens are the day's losing candidates, not admitted commemorations. --- cmd/lectio-ef-dump/main.go | 106 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 cmd/lectio-ef-dump/main.go (limited to 'cmd/lectio-ef-dump') diff --git a/cmd/lectio-ef-dump/main.go b/cmd/lectio-ef-dump/main.go new file mode 100644 index 0000000..08ff547 --- /dev/null +++ b/cmd/lectio-ef-dump/main.go @@ -0,0 +1,106 @@ +// Command lectio-ef-dump prints lectio's Extraordinary Form (1962) calendar, +// one line per day, for use as colitur's differential oracle. +// +// Output format matches colitur's `colitur day ` line for line (see +// colitur's bin/main.ml, day_line), so the two streams are diffable directly: +// +// YYYY-MM-DD weekday season week slug rank colour [+other-slug]... +// +// Every column carries lectio's OWN vocabulary (season names, slugs, rank and +// colour spellings) -- this dumper does not translate lectio's values into +// colitur's. The mapping between the two vocabularies is the differential +// comparator's job, not this dumper's; translating here would hide real +// divergences behind an already-reconciled view. +// +// The trailing "+other-slug" tokens are lectio's Others: the sanctoral +// candidates that lost the day's precedence contest. lectio has no RG 111 +// commemoration-admission logic, so this is the set of losing candidates, not +// the set of admitted commemorations -- the two are different things. See the +// task report for detail; the comparator does not diff this column. +// +// Usage: lectio-ef-dump +package main + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" + "time" + + "github.com/lukaszkasprzak/lectio/internal/caldata" + "github.com/lukaszkasprzak/lectio/internal/calendar" +) + +func main() { + os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) +} + +// run is lectio-ef-dump's testable entry point: parse args, compute, and +// write. Returns a process exit code (0 ok, 1 runtime error, 2 usage error). +func run(args []string, stdout, stderr io.Writer) int { + if len(args) != 2 { + fmt.Fprintln(stderr, "usage: lectio-ef-dump ") + return 2 + } + from, errFrom := strconv.Atoi(args[0]) + to, errTo := strconv.Atoi(args[1]) + if errFrom != nil || errTo != nil { + fmt.Fprintln(stderr, "lectio-ef-dump: from-year and to-year must be integers") + return 2 + } + if from > to { + fmt.Fprintf(stderr, "lectio-ef-dump: from-year %d is after to-year %d\n", from, to) + return 2 + } + + sel := calendar.DefaultSelection() + sel.Form = "old" + layers := []calendar.Layer{caldata.Tridentine()} + + w := bufio.NewWriter(stdout) + for y := from; y <= to; y++ { + start := time.Date(y, time.January, 1, 0, 0, 0, 0, time.UTC) + end := time.Date(y, time.December, 31, 0, 0, 0, 0, time.UTC) + for d := start; !d.After(end); d = d.AddDate(0, 0, 1) { + day := calendar.Compute(d, sel, layers) + if _, err := w.WriteString(dumpLine(day)); err != nil { + fmt.Fprintf(stderr, "lectio-ef-dump: %v\n", err) + return 1 + } + } + } + if err := w.Flush(); err != nil { + fmt.Fprintf(stderr, "lectio-ef-dump: %v\n", err) + return 1 + } + return 0 +} + +// dumpLine renders one LiturgicalDay as a colitur-format line. Week is +// lectio's own int (0 where no season week applies, e.g. named I class feasts +// and per annum green-season ferias); printed as "-" there so the field count +// stays fixed, matching colitur's own convention for an absent week. +func dumpLine(day calendar.LiturgicalDay) string { + week := "-" + if day.Week != 0 { + week = strconv.Itoa(day.Week) + } + var b strings.Builder + fmt.Fprintf(&b, "%s %s %s %s %s %s %s", + day.Date.Format("2006-01-02"), + strings.ToLower(day.Weekday.String()), + string(day.Season), + week, + day.Observed.Slug, + string(day.Observed.Rank), + string(day.Colour), + ) + for _, o := range day.Others { + fmt.Fprintf(&b, " +%s", o.Slug) + } + b.WriteByte('\n') + return b.String() +} -- cgit v1.3 From ef171295a5d7b7cc40ad7367e142de4234a4eb54 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 12 Aug 2026 14:52:02 +0200 Subject: lectio-ef-dump: add name_en/name_pl columns The verification instrument this task's own diff comparisons run against emitted season/week/slug/rank/colour but no display name at all, so it was structurally incapable of seeing the 322-line name.pl deletion (fix round 2) -- the season/rank/colour columns it already printed were all still correct, since name is a wholly separate field naming.CelebrationName reads independently, and no diff against this tool's own prior output could ever have caught the regression. Appends two fixed columns after colour (before the existing +other tokens): the observed celebration's own name.en and name.pl (spaces replaced with "_" so the line stays whitespace-delimited; "-" for an empty name, matching the week column's own convention). Format is documented in the package doc comment as a departure from colitur's matching day-line format: a straight diff against colitur's output must now ignore these two trailing-before-others fields. --- cmd/lectio-ef-dump/main.go | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) (limited to 'cmd/lectio-ef-dump') diff --git a/cmd/lectio-ef-dump/main.go b/cmd/lectio-ef-dump/main.go index 08ff547..e7d014a 100644 --- a/cmd/lectio-ef-dump/main.go +++ b/cmd/lectio-ef-dump/main.go @@ -1,10 +1,25 @@ // Command lectio-ef-dump prints lectio's Extraordinary Form (1962) calendar, // one line per day, for use as colitur's differential oracle. // -// Output format matches colitur's `colitur day ` line for line (see -// colitur's bin/main.ml, day_line), so the two streams are diffable directly: +// Output format is colitur's `colitur day ` line, PLUS two display-name +// columns colitur's own line does not carry (so a straight `diff` against +// colitur's output must ignore trailing fields, not compare them line for +// line): // -// YYYY-MM-DD weekday season week slug rank colour [+other-slug]... +// YYYY-MM-DD weekday season week slug rank colour name_en name_pl [+other-slug]... +// +// name_en and name_pl are the observed celebration's own display name in +// English and Polish (Celebration.Name["en"]/["pl"]), spaces replaced with +// "_" so the line stays whitespace-delimited; "-" where the name is empty +// (a bare feria with no proper name). Added after a regeneration of +// internal/caldata/tridentine-calendar.ini once deleted all 322 Polish +// names (name.pl 322 -> 0) with nothing in this repository's test suite OR +// this dumper noticing: the season/rank/colour columns this tool already +// printed were all still correct, since name is a wholly separate field +// naming.CelebrationName reads independently. This dumper is what the +// task's own before/after diff verification is run against, so it needed +// to be structurally capable of seeing a name regression, not just told to +// look harder next time. // // Every column carries lectio's OWN vocabulary (season names, slugs, rank and // colour spellings) -- this dumper does not translate lectio's values into @@ -79,17 +94,30 @@ func run(args []string, stdout, stderr io.Writer) int { return 0 } -// dumpLine renders one LiturgicalDay as a colitur-format line. Week is -// lectio's own int (0 where no season week applies, e.g. named I class feasts -// and per annum green-season ferias); printed as "-" there so the field count -// stays fixed, matching colitur's own convention for an absent week. +// dumpName renders a display-name field: spaces become "_" so the line stays +// whitespace-delimited (names routinely contain spaces, e.g. "St. Thomas +// Becket", "Wniebowzięcie N. M. P."); "-" for an empty name, matching the +// week column's own convention for "absent". +func dumpName(s string) string { + if s == "" { + return "-" + } + return strings.ReplaceAll(s, " ", "_") +} + +// dumpLine renders one LiturgicalDay as a colitur-format line, plus the +// name_en/name_pl columns colitur's own line does not carry (see this +// package's doc comment). Week is lectio's own int (0 where no season week +// applies, e.g. named I class feasts and per annum green-season ferias); +// printed as "-" there so the field count stays fixed, matching colitur's +// own convention for an absent week. func dumpLine(day calendar.LiturgicalDay) string { week := "-" if day.Week != 0 { week = strconv.Itoa(day.Week) } var b strings.Builder - fmt.Fprintf(&b, "%s %s %s %s %s %s %s", + fmt.Fprintf(&b, "%s %s %s %s %s %s %s %s %s", day.Date.Format("2006-01-02"), strings.ToLower(day.Weekday.String()), string(day.Season), @@ -97,6 +125,8 @@ func dumpLine(day calendar.LiturgicalDay) string { day.Observed.Slug, string(day.Observed.Rank), string(day.Colour), + dumpName(day.Observed.Name["en"]), + dumpName(day.Observed.Name["pl"]), ) for _, o := range day.Others { fmt.Fprintf(&b, " +%s", o.Slug) -- cgit v1.3