From 020a5bd81500a8bc34f37c1e0cda2ec1e25b55c4 Mon Sep 17 00:00:00 2001 From: funkpower Date: Sun, 20 Sep 2026 17:23:11 -0400 Subject: [PATCH] citefind: a reprint's two years, an editor in the author's place, and a bare page range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three shapes out of the 20% of records that still named no work: Arnauld, A., and P. Nicole. 1662/1965. La logique ou l'art de penser. Barratt, Alexandra, ed. Women's Writing in Middle English. London, 1992. Schleiermacher, Friedrich. 1813/1963. "Ueber die verschiedenen Methoden des Uebersezens". In Das Problem des Uebersetzens, edited by H.J. Störig, 38-70. Darmstadt: Wissenschaftliche Buchgesellschaft. A reprint gives the year it was written and the year of the edition used, and the author-date opening stopped at the slash — so the title went to the container and the container to nothing. `ed.` is an abbreviation rather than a sentence stop, which is right, and meant the editor's name and the work's title stayed one segment and neither was read. An editor is a person and the credit is theirs, not part of either. And the Schleiermacher entry was refused outright for running to six sentences, because German titles do. Its page range says plainly that it is a chapter in a book — but `38-70` carries no `pp.`, and only `pp.` counted. A page range after a comma counts now. The passage of body prose that started all this is still refused: its numbers are a date, not a range, and nothing precedes them but a verb. --- crates/citefind/src/parse.rs | 74 ++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/crates/citefind/src/parse.rs b/crates/citefind/src/parse.rs index 83634cd..7219dad 100644 --- a/crates/citefind/src/parse.rs +++ b/crates/citefind/src/parse.rs @@ -239,10 +239,23 @@ static GLOSS_SIGNATURE: LazyLock = LazyLock::new(|| { .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 +/// segment and neither is read. +static EDITOR_FORM: LazyLock = LazyLock::new(|| { + Regex::new(r"(?i)^([^.]{2,60}),\s+(?:eds?|trans|comps?|rev)\.\s+(\S.*)$") + .expect("a valid pattern") +}); + /// A page reference or a bracketed imprint: what a citation has and a remark /// about the argument does not. static CITATION_ANCHOR: LazyLock = LazyLock::new(|| { - Regex::new(r"(?i)\bpp?\.\s*\d|\bvol\.\s*\d|\bno\.\s*\d").expect("a valid pattern") + // A page range after a comma counts even with no `pp.` in front of it: + // `edited by H.J. Störig, 38-70. Darmstadt:` is a chapter in a book, and + // six sentences long because German titles are. + Regex::new(r"(?i)\bpp?\.\s*\d|\bvol\.\s*\d|\bno\.\s*\d|,\s*\d{1,5}\s*[-–—]\s*\d{1,5}\b") + .expect("a valid pattern") }); /// How many words a reference runs to before it is prose. @@ -484,7 +497,8 @@ pub fn one(text: &str, marker: Option<&str>) -> Option { out.title = Some(title); out.authors = authors(&text[..at.0]); out.container = container(text, &text[at.1..]); - } else if let Some(found) = note_form(text) + } else if let Some(found) = editor_form(text) + .or_else(|| note_form(text)) .or_else(|| comma_form(text)) .or_else(|| short_note(text)) { @@ -543,7 +557,10 @@ fn span(pattern: &Regex, text: &str) -> (Option, Option<(usize, usize)>) /// 1951, A Study of Behaviour`. Common in the sciences and in older German /// and Russian bibliographies. static NAME_YEAR: LazyLock = LazyLock::new(|| { - Regex::new(r"^(.{2,80}?)[,.]?\s+((?:1[4-9]|20)\d{2})[a-z]?\s*[,.:]\s+") + // `Schleiermacher, Friedrich. 1813/1963.` gives the year it was written + // and the year of the edition used. The first is the one that dates the + // work; the second still has to be got past. + Regex::new(r"^(.{2,80}?)[,.]?\s+((?:1[4-9]|20)\d{2})[a-z]?(?:\s*/\s*(?:1[4-9]|20)\d{2}[a-z]?)?\s*[,.:]\s+") .expect("a valid pattern") }); @@ -679,6 +696,25 @@ fn comma_form(text: &str) -> Option<(Vec, Option, Option Some((names, Some(title), publisher)) } +/// Read an entry whose author is credited as its editor. +/// +/// `Barratt, Alexandra, ed. Women's Writing in Middle English. London: +/// Longman, 1992.` — `ed.` is an abbreviation rather than a sentence stop, +/// so the name and the title are one segment until the role is taken out. +fn editor_form(text: &str) -> Option<(Vec, Option, Option)> { + let found = EDITOR_FORM.captures(text.trim())?; + let names = authors(&found[1]); + if names.is_empty() { + return None; + } + let rest = found[2].trim(); + let title = tidy_title(&strip_markup(sentences(rest).first()?)); + if title.chars().count() < 4 { + return None; + } + Some((names, Some(title), container(text, rest))) +} + /// Read a short note: a surname, a short title, and where in it. /// /// `Unger, Dutch Shipbuilding, 33-4` and `Sjoerds, Algemene Beschryvinge, i, @@ -1441,6 +1477,38 @@ mod tests { assert_eq!(found.year, Some(1941)); } + #[test] + fn a_reprint_gives_two_years_and_an_editor_takes_the_author_slot() { + // `1813/1963` is the year it was written and the year of the + // edition used; the page range says it is a chapter in a book, which + // is why six sentences of German do not make it prose. + let reprint = read( + "Schleiermacher, Friedrich. 1813/1963. \"Ueber die verschiedenen \ + Methoden des Uebersezens\". In Das Problem des Uebersetzens, \ + edited by H.J. Stoerig, 38-70. Darmstadt: Wissenschaftliche \ + Buchgesellschaft.", + ); + assert_eq!(reprint.authors, ["Schleiermacher, Friedrich"]); + assert_eq!( + reprint.title.as_deref(), + Some("Ueber die verschiedenen Methoden des Uebersezens") + ); + assert_eq!(reprint.year, Some(1813)); + + // `ed.` is an abbreviation rather than a stop, so the name and the + // title stayed one segment and neither was read. + let editor = read( + "Barratt, Alexandra, ed. Women's Writing in Middle English. \ + London: Longman, 1992.", + ); + assert_eq!(editor.authors, ["Barratt, Alexandra"]); + assert_eq!( + editor.title.as_deref(), + Some("Women's Writing in Middle English") + ); + assert_eq!(editor.container.as_deref(), Some("Longman")); + } + #[test] fn an_author_date_entry_need_not_bracket_its_year() { let found = read(