1use std::collections::BTreeMap;
20
21use crate::error::{BibParseError, BibParseErrorKind};
22use crate::record::{BibEntry, Bibliography};
23
24pub fn parse_bibtex(input: &str) -> Result<Bibliography, BibParseError> {
53 let mut parser = Parser::new(input);
54 let mut entries = BTreeMap::new();
55 parser.skip_whitespace();
56 while !parser.at_end() {
57 let parsed = parser.parse_entry()?;
58 if entries.contains_key(&parsed.entry.key) {
59 return Err(BibParseError::new(
60 BibParseErrorKind::DuplicateKey,
61 parsed.key_offset,
62 ));
63 }
64 entries.insert(parsed.entry.key.clone(), parsed.entry);
65 parser.skip_whitespace();
66 }
67 Ok(Bibliography { entries })
68}
69
70struct ParsedEntry {
71 entry: BibEntry,
72 key_offset: usize,
73}
74
75struct ParsedKey {
76 text: String,
77 offset: usize,
78}
79
80struct Parser<'a> {
85 src: &'a str,
86 bytes: &'a [u8],
87 pos: usize,
88}
89
90impl<'a> Parser<'a> {
91 const fn new(src: &'a str) -> Self {
92 Self {
93 src,
94 bytes: src.as_bytes(),
95 pos: 0,
96 }
97 }
98
99 const fn at_end(&self) -> bool {
100 self.pos >= self.bytes.len()
101 }
102
103 fn peek(&self) -> Option<u8> {
104 self.bytes.get(self.pos).copied()
105 }
106
107 const fn bump(&mut self) {
108 self.pos += 1;
109 }
110
111 fn skip_whitespace(&mut self) {
112 while let Some(b) = self.peek() {
113 if b.is_ascii_whitespace() {
114 self.bump();
115 } else {
116 break;
117 }
118 }
119 }
120
121 const fn error_here(&self, kind: BibParseErrorKind) -> BibParseError {
122 BibParseError::new(kind, self.pos)
123 }
124
125 const fn error_at(offset: usize, kind: BibParseErrorKind) -> BibParseError {
126 BibParseError::new(kind, offset)
127 }
128
129 fn expect_byte(&mut self, byte: u8, kind: BibParseErrorKind) -> Result<(), BibParseError> {
131 if self.peek() == Some(byte) {
132 self.bump();
133 Ok(())
134 } else {
135 Err(self.error_here(kind))
136 }
137 }
138
139 fn take_identifier(&mut self) -> Option<String> {
142 let start = self.pos;
143 while let Some(b) = self.peek() {
144 if is_identifier_byte(b) {
145 self.bump();
146 } else {
147 break;
148 }
149 }
150 if self.pos == start {
151 None
152 } else {
153 Some(self.src[start..self.pos].to_ascii_lowercase())
154 }
155 }
156
157 fn parse_entry(&mut self) -> Result<ParsedEntry, BibParseError> {
158 self.expect_byte(b'@', BibParseErrorKind::ExpectedAt)?;
159 self.skip_whitespace();
160 let entry_type = self
161 .take_identifier()
162 .ok_or_else(|| self.error_here(BibParseErrorKind::ExpectedEntryType))?;
163 self.skip_whitespace();
164 self.expect_byte(b'{', BibParseErrorKind::ExpectedOpenBrace)?;
165 self.skip_whitespace();
166 let key = self.parse_key()?;
167 self.skip_whitespace();
168 let mut fields = BTreeMap::new();
169 match self.peek() {
170 Some(b'}') => self.bump(),
171 Some(b',') => {
172 self.bump();
173 self.parse_fields(&mut fields)?;
174 }
175 Some(_) => return Err(self.error_here(BibParseErrorKind::ExpectedCommaOrCloseBrace)),
176 None => return Err(self.error_here(BibParseErrorKind::UnterminatedEntry)),
177 }
178 let key_span = key.offset..key.offset + key.text.len();
179 Ok(ParsedEntry {
180 entry: BibEntry {
181 entry_type,
182 key: key.text,
183 key_span,
184 fields,
185 },
186 key_offset: key.offset,
187 })
188 }
189
190 fn parse_key(&mut self) -> Result<ParsedKey, BibParseError> {
193 let start = self.pos;
194 while let Some(b) = self.peek() {
195 if is_key_byte(b) {
196 self.bump();
197 } else {
198 break;
199 }
200 }
201 if self.pos == start {
202 return Err(self.error_here(BibParseErrorKind::ExpectedKey));
203 }
204 Ok(ParsedKey {
205 text: self.src[start..self.pos].to_owned(),
206 offset: start,
207 })
208 }
209
210 fn parse_fields(&mut self, fields: &mut BTreeMap<String, String>) -> Result<(), BibParseError> {
214 let mut saw_field = false;
215 loop {
216 self.skip_whitespace();
217 match self.peek() {
218 Some(b'}') if saw_field => {
222 self.bump();
223 return Ok(());
224 }
225 Some(b'}') => return Err(self.error_here(BibParseErrorKind::ExpectedFieldName)),
226 None => return Err(self.error_here(BibParseErrorKind::UnterminatedEntry)),
227 _ => {}
228 }
229 let name = self
230 .take_identifier()
231 .ok_or_else(|| self.error_here(BibParseErrorKind::ExpectedFieldName))?;
232 self.skip_whitespace();
233 self.expect_byte(b'=', BibParseErrorKind::ExpectedEquals)?;
234 self.skip_whitespace();
235 let value = self.parse_value()?;
236 fields.insert(name, value);
238 saw_field = true;
239 self.skip_whitespace();
240 match self.peek() {
241 Some(b',') => self.bump(),
242 Some(b'}') => {
243 self.bump();
244 return Ok(());
245 }
246 None => return Err(self.error_here(BibParseErrorKind::UnterminatedEntry)),
247 Some(_) => {
248 return Err(self.error_here(BibParseErrorKind::ExpectedCommaOrCloseBrace));
249 }
250 }
251 }
252 }
253
254 fn parse_value(&mut self) -> Result<String, BibParseError> {
255 match self.peek() {
256 Some(b'{') => self.parse_braced(),
257 Some(b'"') => self.parse_quoted(),
258 Some(b) if is_bare_value_byte(b) => Ok(self.take_bare_value()),
259 _ => Err(self.error_here(BibParseErrorKind::ExpectedValue)),
260 }
261 }
262
263 fn parse_braced(&mut self) -> Result<String, BibParseError> {
266 let open_offset = self.pos;
267 self.bump(); let mut depth = 1_usize;
269 while let Some(b) = self.peek() {
270 match b {
271 b'{' => depth += 1,
272 b'}' => {
273 depth -= 1;
274 if depth == 0 {
275 self.bump(); return Ok(self.src[open_offset..self.pos].to_owned());
277 }
278 }
279 _ => {}
280 }
281 self.bump();
282 }
283 Err(Self::error_at(
284 open_offset,
285 BibParseErrorKind::UnterminatedValue,
286 ))
287 }
288
289 fn parse_quoted(&mut self) -> Result<String, BibParseError> {
294 let open_offset = self.pos;
295 self.bump(); let mut depth = 0_usize;
297 while let Some(b) = self.peek() {
298 match b {
299 b'\\' => {
300 self.bump();
301 if !self.at_end() {
302 self.bump();
303 }
304 continue;
305 }
306 b'{' => depth += 1,
307 b'}' if depth > 0 => depth -= 1,
308 b'"' if depth == 0 => {
309 self.bump(); return Ok(self.src[open_offset..self.pos].to_owned());
311 }
312 _ => {}
313 }
314 self.bump();
315 }
316 Err(Self::error_at(
317 open_offset,
318 BibParseErrorKind::UnterminatedValue,
319 ))
320 }
321
322 fn take_bare_value(&mut self) -> String {
326 let start = self.pos;
327 while let Some(b) = self.peek() {
328 if is_bare_value_byte(b) {
329 self.bump();
330 } else {
331 break;
332 }
333 }
334 self.src[start..self.pos].to_owned()
335 }
336}
337
338const fn is_identifier_byte(b: u8) -> bool {
340 b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-' | b'+' | b'.' | b':' | b'/')
341}
342
343const fn is_key_byte(b: u8) -> bool {
346 !b.is_ascii_whitespace() && !matches!(b, b',' | b'{' | b'}' | b'"' | b'=' | b'@')
347}
348
349const fn is_bare_value_byte(b: u8) -> bool {
351 b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-' | b'+' | b'.' | b':' | b'/')
352}