summaryrefslogtreecommitdiff
path: root/support/texlab/src/citeproc/name/parser.lalrpop
blob: 556e3bd361532ad167918e6cc3a0725bfb977617 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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\-)&/.]+" => (<>);