citefind: hold a guessed title to a bar

A title read off markup was vouched for by the entry. A title guessed at by
cutting the entry at its sentence stops was not, and where nothing else came
off the entry — no author, no identifier, no container — that guess was the
whole of what got stored. Two things kept arriving that way.

A note that remarks on the argument rather than citing anything: "The
Commentator seems to have based himself principally on the Bible", "Dewey
remarks that we experience most things as temporal". A page of endnotes is
mostly these. They are noun phrases nowhere: what separates them from
"Quaestiones Parisienses" is a finite verb, so that is what is asked.

A siglum and a page — `H, 272`, `FR,120-122`, `GNO` — whose key is a page of
its own the entry does not carry. An atlas of Middle-earth is a thousand of
them, and the heading each sits under is not its title, so a siglum is
refused whatever else came off the entry.

Also refuses the navigation strip a PDF reader leaves in the text, `572 next
page > < previous page`, and the local path it points at.

On 400 flagged body chunks the junk rate falls from 3.5% to 0.5% of stored
rows; on the 202-file reference sample the share naming a work holds at
97.2% across 42 fewer rows, all of them sigla, scripture or prose.
This commit is contained in:
funkpower
2026-09-20 17:48:45 -04:00
parent a8889941b0
commit c268ce5e46
+121 -4
View File
@@ -171,10 +171,16 @@ static BIOGRAPHY: LazyLock<Regex> = LazyLock::new(|| {
/// reference to anything.
static PRINT_FURNITURE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(
r"(?ix) ^ https?://\S+
(?: \s* \( \s* \d+ \s+ of \s+ \d+ \s* \) )? # (54 of 100)
(?: \s* \[ [^\]]{0,40} \] )? # [4/7/2002 1:48:08 PM]
\s* $",
r"(?ix)
^ https?://\S+
(?: \s* \( \s* \d+ \s+ of \s+ \d+ \s* \) )? # (54 of 100)
(?: \s* \[ [^\]]{0,40} \] )? # [4/7/2002 1:48:08 PM]
\s* $
# The navigation strip a PDF reader leaves in the text, and the
# local path it points at.
| (?: < \s* previous \s+ page | next \s+ page \s* > )
| file:///
",
)
.expect("a valid pattern")
});
@@ -254,6 +260,28 @@ static GLOSS_SIGNATURE: LazyLock<Regex> = LazyLock::new(|| {
.expect("a valid pattern")
});
/// A finite verb, which a remark has and the name of a work does not.
/// `Quaestiones Parisienses` and `The Lord of the Rings` are noun phrases;
/// `The Commentator seems to have based himself on the Bible` is a note
/// about the argument, and a page of endnotes is mostly those.
static PREDICATE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(
r"(?ix) \b (?:
is|are|was|were|be|been|has|have|had|does|did|do
| can|could|may|might|must|shall|should|will|would
| seems?|says?|remarks?|argues?|holds?|means?|refers?|thinks?
| shows?|gives?|takes?|makes?|enters?|shed|follows?|explains?
) \b",
)
.expect("a valid pattern")
});
/// An abbreviation standing for a work, and where in it: `H, 272`,
/// `FR,120-122`, `GNO`, `Kgs 17:21`. The key that expands it is a page of
/// its own that the entry does not carry, so on its own it names nothing.
static SIGLUM: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[A-Z][A-Za-z]{0,4}[\s,.]*(?:\d|$)").expect("a valid pattern"));
/// An editor's or translator's credit where an author's name would be:
/// `Barratt, Alexandra, ed. Women's Writing in Middle English.` The role is
/// not a sentence stop, so the name and the title run together into one
@@ -442,6 +470,12 @@ pub fn one(text: &str, marker: Option<&str>) -> Option<Reference> {
out.year = year(text);
out.pages = pages(text);
// Whether the title was read off something the entry marked as one, or
// guessed at by cutting the entry at its sentence stops. The guess is
// held to a higher bar below, because nothing in the entry vouched for
// it.
let mut guessed = false;
let (quoted, quoted_at) = span(&QUOTED, text);
let (emphasised, emphasised_at) = span(&EMPHASISED, text);
let dated = author_date(text);
@@ -529,6 +563,7 @@ pub fn one(text: &str, marker: Option<&str>) -> Option<Reference> {
out.container = found.2;
} else {
let (found_authors, found_title, found_container) = by_sentence(text);
guessed = found_title.is_some();
out.authors = found_authors;
out.title = found_title;
out.container = found_container;
@@ -554,6 +589,22 @@ pub fn one(text: &str, marker: Option<&str>) -> Option<Reference> {
}
}
}
// A guessed title is only as good as the entry being a reference at all.
// Where nothing else was read off it — no author, no identifier, no
// container — the guess is the whole of what the record would carry, and
// a remark about the argument or a siglum dressed as a title is worse
// than no answer, because it is stored and looks settled.
if guessed && out.authors.is_empty() && out.ids.is_empty() {
let refused = out.title.as_deref().is_some_and(|title| {
// A siglum is not the name of a work whatever else the entry
// gave up; a remark is only recognisable as one when nothing
// else was read, because a container vouches for the rest.
SIGLUM.is_match(title) || (out.container.is_none() && PREDICATE.is_match(title))
});
if refused {
out.title = None;
}
}
out.kind = kind(text, &out);
Some(out)
@@ -2123,4 +2174,70 @@ mod tests {
["Tolkien, J. R. R. The Hobbit, rev. ed. London, 1951."]
);
}
#[test]
fn a_note_that_remarks_on_the_argument_names_no_work() {
// The commonest thing on a page of endnotes, and until the guessed
// title was held to a bar, every one of them was stored as a work.
for remark in [
"Admittedly, the lot may enter into the appointment of priests \
(759b-c). But priesthood is not in itself a qualification.",
"The Commentator seems to have based himself on the Bible. \
Compare the passage at Isidore, Etym. XIX.xxi.5.",
"Dewey remarks that we experience most things as temporal. \
He is aware of the practical aspects of the thought of Plato.",
] {
let named = one(remark, Some("12")).and_then(|read| read.title);
assert_eq!(named, None, "stored a remark as a title: {remark}");
}
}
#[test]
fn a_title_the_entry_marked_up_is_taken_at_its_word() {
// The other side of the same bar, and why it is drawn at the guess
// rather than at the title: an entry that marked its own title is
// not guessing, so nothing here is asked of it. Patristic and
// medieval works are cited by title alone, and holding those to the
// bar would cost more than it saved.
for (text, want) in [
(
"*Quaestiones Parisienses* (LW I:37-83 \\[Questions 1-5\\])",
"Quaestiones Parisienses",
),
(
"H, 302. \\ ***The Lord of the Rings*** \\ INTRODUCTION (p. 117)",
"The Lord of the Rings",
),
] {
let read = one(text, None).expect("a record");
assert_eq!(read.title.as_deref(), Some(want));
}
}
#[test]
fn a_siglum_and_a_page_name_nothing() {
// `H, 272` is an abbreviation whose key is a page of its own that the
// entry does not carry. An atlas of Middle-earth is a thousand of
// them, and the heading each one sits under is not its title.
for siglum in [
"H, 272. THE BATTLE OF FIVE ARMIES (p. 112)",
"FR,120-122. ON THE BARROW-DOWNS (p. 122)",
] {
let named = one(siglum, None).and_then(|read| read.title);
assert_eq!(named, None, "stored a siglum as a title: {siglum}");
}
}
#[test]
fn the_readers_own_navigation_strip_is_not_a_reference() {
for furniture in [
"572 next page > < previous page page",
"574 next page > file:///C:/Users/User/AppData/Local/Temp/Rar$EX00/files/page",
] {
assert!(
one(furniture, None).is_none(),
"read furniture: {furniture}"
);
}
}
}