diff options
author | Norbert Preining <norbert@preining.info> | 2019-12-03 03:01:24 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-12-03 03:01:24 +0000 |
commit | b8d4bb76703bcb15578e2b23c5d256532180b894 (patch) | |
tree | bedd1df7a00521a2bd986b3c0289d6556a59e39b /support/texlab/src/citeproc/name | |
parent | 02e4625a78a5029e8b5dc2a4ec70193b232f497e (diff) |
CTAN sync 201912030301
Diffstat (limited to 'support/texlab/src/citeproc/name')
-rw-r--r-- | support/texlab/src/citeproc/name/mod.rs | 275 | ||||
-rw-r--r-- | support/texlab/src/citeproc/name/parser.lalrpop | 152 |
2 files changed, 427 insertions, 0 deletions
diff --git a/support/texlab/src/citeproc/name/mod.rs b/support/texlab/src/citeproc/name/mod.rs new file mode 100644 index 0000000000..7668d0fff8 --- /dev/null +++ b/support/texlab/src/citeproc/name/mod.rs @@ -0,0 +1,275 @@ +// Ported from: https://github.com/michel-kraemer/citeproc-java/blob/master/citeproc-java/grammars/InternalName.g4 +// Michel Kraemer +// Apache License 2.0 +mod parser; + +use self::parser::NamesParser; +use citeproc_io::Name; + +pub fn parse(input: &str) -> Vec<Name> { + let parser = NamesParser::new(); + parser.parse(input).unwrap_or_else(|_| { + vec![Name::Literal { + literal: input.into(), + }] + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use citeproc_io::PersonName; + + #[test] + fn test_family_only() { + let name = Name::Person(PersonName { + family: Some("Thompson".into()), + given: None, + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Thompson"), vec![name]); + } + + #[test] + fn test_simple() { + let name = Name::Person(PersonName { + family: Some("Thompson".into()), + given: Some("Ken".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Ken Thompson"), vec![name]); + } + + #[test] + fn test_middle_name() { + let name = Name::Person(PersonName { + family: Some("Ritchie".into()), + given: Some("Dennis M.".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Dennis M. Ritchie"), vec![name]); + } + + #[test] + fn test_initials() { + let name = Name::Person(PersonName { + family: Some("Johnson".into()), + given: Some("S. C.".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("S. C. Johnson"), vec![name]); + } + + #[test] + fn test_non_dropping_particle() { + let name = Name::Person(PersonName { + family: Some("Gerwen".into()), + given: Some("Michael".into()), + non_dropping_particle: Some("van".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Michael van Gerwen"), vec![name]); + } + + #[test] + fn test_non_dropping_particle_family_only() { + let name = Name::Person(PersonName { + family: Some("Gerwen".into()), + given: None, + non_dropping_particle: Some("van".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("van Gerwen"), vec![name]); + } + + #[test] + fn test_comma() { + let name = Name::Person(PersonName { + family: Some("Thompson".into()), + given: Some("Ken".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Thompson, Ken"), vec![name]); + } + + #[test] + fn test_comma_junior() { + let name = Name::Person(PersonName { + family: Some("Friedman".into()), + given: Some("George".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: Some("Jr.".into()), + }); + assert_eq!(parse("Friedman, Jr., George"), vec![name]); + } + + #[test] + fn test_comma_no_junior() { + let name = Name::Person(PersonName { + family: Some("Familya Familyb".into()), + given: Some("Given".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Familya Familyb, Given"), vec![name]); + } + + #[test] + fn test_comma_initials() { + let name = Name::Person(PersonName { + family: Some("Ritchie".into()), + given: Some("Dennis M.".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Ritchie, Dennis M."), vec![name]); + } + + #[test] + fn test_comma_non_dropping_particle() { + let name = Name::Person(PersonName { + family: Some("Gerwen".into()), + given: Some("Michael".into()), + non_dropping_particle: Some("van".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("van Gerwen, Michael"), vec![name]); + } + + #[test] + fn test_comma_non_dropping_particles() { + let name = Name::Person(PersonName { + family: Some("Voort".into()), + given: Some("Vincent".into()), + non_dropping_particle: Some("Van der".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!(parse("Van der Voort, Vincent"), vec![name]); + } + + #[test] + fn test_and() { + let name1 = Name::Person(PersonName { + family: Some("Gerwen".into()), + given: Some("Michael".into()), + non_dropping_particle: Some("van".into()), + dropping_particle: None, + suffix: None, + }); + let name2 = Name::Person(PersonName { + family: Some("Voort".into()), + given: Some("Vincent".into()), + non_dropping_particle: Some("van der".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!( + parse("Michael van Gerwen and Vincent van der Voort"), + vec![name1, name2] + ); + } + + #[test] + fn test_and_comma1() { + let name1 = Name::Person(PersonName { + family: Some("Gerwen".into()), + given: Some("Michael".into()), + non_dropping_particle: Some("van".into()), + dropping_particle: None, + suffix: None, + }); + let name2 = Name::Person(PersonName { + family: Some("Voort".into()), + given: Some("Vincent".into()), + non_dropping_particle: Some("Van der".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!( + parse("van Gerwen, Michael and Van der Voort, Vincent"), + vec![name1, name2] + ); + } + + #[test] + fn test_and_comma2() { + let name1 = Name::Person(PersonName { + family: Some("Gerwen".into()), + given: Some("Michael".into()), + non_dropping_particle: Some("van".into()), + dropping_particle: None, + suffix: None, + }); + let name2 = Name::Person(PersonName { + family: Some("Voort".into()), + given: Some("Vincent".into()), + non_dropping_particle: Some("van der".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!( + parse("van Gerwen, Michael and van der Voort, Vincent"), + vec![name1, name2] + ); + } + + #[test] + fn test_and_comma_mix() { + let name1 = Name::Person(PersonName { + family: Some("Gerwen".into()), + given: Some("Michael".into()), + non_dropping_particle: Some("van".into()), + dropping_particle: None, + suffix: None, + }); + let name2 = Name::Person(PersonName { + family: Some("Voort".into()), + given: Some("Vincent".into()), + non_dropping_particle: Some("van der".into()), + dropping_particle: None, + suffix: None, + }); + assert_eq!( + parse("van Gerwen, Michael and Vincent van der Voort"), + vec![name1, name2] + ); + } + + #[test] + fn test_junior() { + let name = Name::Person(PersonName { + family: Some("Friedman".into()), + given: Some("George".into()), + non_dropping_particle: None, + dropping_particle: None, + suffix: Some("Jr.".into()), + }); + assert_eq!(parse("George Friedman, Jr."), vec![name]); + } + + #[test] + fn test_non_parseable() { + let literal = "Jerry Peek and Tim O'Reilly and Mike Loukides and other authors of the Nutshell handbooks"; + let name = Name::Literal { + literal: literal.into(), + }; + assert_eq!(parse(literal), vec![name]); + } +} diff --git a/support/texlab/src/citeproc/name/parser.lalrpop b/support/texlab/src/citeproc/name/parser.lalrpop new file mode 100644 index 0000000000..556e3bd361 --- /dev/null +++ b/support/texlab/src/citeproc/name/parser.lalrpop @@ -0,0 +1,152 @@ +// Ported from: https://github.com/michel-kraemer/citeproc-java/blob/master/citeproc-java/grammars/InternalName.g4 +// Michel Kraemer +// Apache License 2.0 +use citeproc_io::{Name, PersonName}; +use itertools::Itertools; + +grammar; + +pub Names: Vec<Name> = And<Name>; + +And<T>: Vec<T> = { + <v:(<T> "and")*> <e:T?> => match e { + None => v, + Some(e) => { + let mut v = v; + v.push(e); + v + } + } +}; + +Name: Name = { + <np1:UWord+> <np2:Von> <fam:Last> "," <fst:First> => { + let (fst1, fst2) = fst; + let name = PersonName { + family: Some(fam.join(" ")), + given: fst2, + non_dropping_particle: Some(format!("{} {}", np1.join(" "), np2)), + dropping_particle: None, + suffix: fst1, + }; + Name::Person(name) + }, + <np:Von> <fam:Last> "," <fst:First> => { + let (fst1, fst2) = fst; + let name = PersonName { + family: Some(fam.join(" ")), + given: fst2, + non_dropping_particle: Some(np), + dropping_particle: None, + suffix: fst1, + }; + Name::Person(name) + }, + <last:Last> "," <fst:First> => { + let (fst1, fst2) = fst; + let first_text = format!( + "{} {}", + fst1.as_ref().map(|s| s.as_str()).unwrap_or_default(), + fst2.as_ref().map(|s| s.as_str()).unwrap_or_default(), + ).trim().to_owned(); + + let name = if first_text == "Jr." { + if last.len() == 1 { + PersonName { + family: Some(last.join(" ")), + given: None, + non_dropping_particle: None, + dropping_particle: None, + suffix: Some(first_text), + } + } else { + let mut last = last.into_iter(); + let given = last.next().map(ToOwned::to_owned); + PersonName { + family: Some(last.join(" ")), + given, + non_dropping_particle: None, + dropping_particle: None, + suffix: Some(first_text), + } + } + } else { + PersonName { + family: Some(last.join(" ")), + given: fst2, + non_dropping_particle: None, + dropping_particle: None, + suffix: fst1, + } + }; + Name::Person(name) + }, + <np:Von> <fam:Last> => { + let name = PersonName { + family: Some(fam.join(" ")), + given: None, + non_dropping_particle: Some(np), + dropping_particle: None, + suffix: None, + }; + Name::Person(name) + }, + <giv:UWord+> <np:Von> <fam:Last> => { + let name = PersonName { + family: Some(fam.join(" ")), + given: Some(giv.join(" ")), + non_dropping_particle: Some(np), + dropping_particle: None, + suffix: None, + }; + Name::Person(name) + }, + <giv:UWord+> <fam:Word> => { + let name = PersonName { + family: Some(fam.into()), + given: Some(giv.join(" ")), + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }; + Name::Person(name) + }, + <fam:Word> => { + let name = PersonName { + family: Some(fam.into()), + given: None, + non_dropping_particle: None, + dropping_particle: None, + suffix: None, + }; + Name::Person(name) + }, +}; + +First: (Option<String>, Option<String>) = { + <a:Word*> "," <b:Word*> => (Some(a.join(" ")), Some(b.join(" "))), + <a:Word*> => (None, Some(a.join(" "))), +}; + +Last: Vec<&'input str> = { + LWord => vec![(<>)], + UWord+ => (<>), +}; + +Von: String = { + LWord => String::from(<>), + <a:Von> <b:LWord> => format!("{} {}", a, b), + <a:Von> <b:UWord+> <c:LWord> => format!("{} {} {}", a, b.join(" "), c), +}; + +Word: &'input str = { + UWord => (<>), + LWord => (<>), +}; + +UWord: &'input str = r"[A-Z\u00C0-\uFFFF(?][A-Z\u00C0-\uFFFF(?a-z\-)&/.]+" => (<>); + +LWord: &'input str = r"[a-z\-)&/.][A-Z\u00C0-\uFFFF(?a-z\-)&/.]+" => (<>); + + + |