Definite Clause Grammars (DCGs) are ideally suited for reasoning about text, including parsing and also describing it.
For example, in this case, we can generate the lyrics from a declarative description:
:- use_module(library(dcgs)).
:- use_module(library(format)).
day_gift("first", "A partridge in a pear tree.").
day_gift("second", "Two turtle doves").
day_gift("third", "Three french hens").
day_gift("fourth", "Four calling birds").
day_gift("fifth", "Five golden rings").
day_gift("sixth", "Six geese a-laying").
day_gift("seventh", "Seven swans a-swimming").
day_gift("eight", "Eight maids a-milking").
day_gift("ninth", "Nine ladies dancing").
day_gift("tenth", "Ten lords a-leaping").
day_gift("eleventh", "Eleven pipers piping").
day_gift("twelth", "Twelve drummers drumming").
lyrics -->
{ findall(D-G, day_gift(D, G), DGs) },
stanzas(DGs, []).
stanzas([], _) --> [].
stanzas([Day-Gift|DGs], Prev) -->
format_("On the ~s day of Christmas~n", [Day]),
"My true love gave to me:\n",
format_("~s", [Gift]),
previous_gifts(Prev),
stanzas(DGs, [Gift|Prev]).
previous_gifts([]) --> "\n\n".
previous_gifts([G|Gs]) --> previous_(Gs, G).
previous_([], G) -->
format_(" and~n~s~n~n", [G]).
previous_([G|Gs], Current) -->
format_("~n~s", [Current]),
previous_(Gs, G).
Sample query:
?- phrase(lyrics, Ls), format("~s", [Ls]).
On the first day of Christmas
My true love gave to me:
...
Including the binding Ls = "On the first day of ...", which can be used for reasoning about the text.
Thank you for this interesting puzzle, and Merry Christmas!