1use crate::quotes::{read_reference, DelimitedReferences, Reading};
2use nom::{
3 branch::alt,
4 bytes::complete::{tag, take_while, take_while1},
5 character::complete::{char, line_ending},
6 combinator::eof,
7 multi::{many0, many1},
8 sequence::{preceded, terminated},
9 IResult, Parser,
10};
11use std::cell::{OnceCell, RefCell};
12
13#[derive(Debug, Clone, PartialEq)]
14pub struct Link {
15 pub id: Option<String>,
16 pub values: Vec<Link>,
17 pub children: Vec<Link>,
18 pub is_indented_id: bool,
19 pub nested: Option<Vec<Link>>,
22}
23
24impl Link {
25 pub fn new_singlet(id: String) -> Self {
26 Link {
27 id: Some(id),
28 values: vec![],
29 children: vec![],
30 is_indented_id: false,
31 nested: None,
32 }
33 }
34
35 pub fn new_indented_id(id: String) -> Self {
36 Link {
37 id: Some(id),
38 values: vec![],
39 children: vec![],
40 is_indented_id: true,
41 nested: None,
42 }
43 }
44
45 pub fn new_value(values: Vec<Link>) -> Self {
46 Link {
47 id: None,
48 values,
49 children: vec![],
50 is_indented_id: false,
51 nested: None,
52 }
53 }
54
55 pub fn new_link(id: Option<String>, values: Vec<Link>) -> Self {
56 Link {
57 id,
58 values,
59 children: vec![],
60 is_indented_id: false,
61 nested: None,
62 }
63 }
64
65 pub fn new_nested(body: Vec<Link>) -> Self {
68 Link {
69 id: None,
70 values: vec![],
71 children: vec![],
72 is_indented_id: false,
73 nested: Some(body),
74 }
75 }
76
77 pub fn with_children(mut self, children: Vec<Link>) -> Self {
78 self.children = children;
79 self
80 }
81}
82
83pub struct ParserState {
84 indentation_stack: RefCell<Vec<usize>>,
85 base_indentation: RefCell<Option<usize>>,
86 nested_depth: RefCell<usize>,
87 furthest: RefCell<FurthestFailure>,
88 references: OnceCell<DelimitedReferences>,
90}
91
92#[derive(Debug, Clone, Default)]
102struct FurthestFailure {
103 address: Option<usize>,
106 expected: Vec<&'static str>,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct ParseFailure {
112 pub offset: usize,
114 pub expected: Vec<&'static str>,
118 pub kind: Option<nom::error::ErrorKind>,
122}
123
124pub struct SavedContext {
126 indentation_stack: Vec<usize>,
127 base_indentation: Option<usize>,
128}
129
130impl Default for ParserState {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136impl ParserState {
137 pub fn new() -> Self {
138 ParserState {
139 indentation_stack: RefCell::new(vec![0]),
140 base_indentation: RefCell::new(None),
141 nested_depth: RefCell::new(0),
142 furthest: RefCell::new(FurthestFailure::default()),
143 references: OnceCell::new(),
144 }
145 }
146
147 pub fn set_base_indentation(&self, indent: usize) {
148 let mut base = self.base_indentation.borrow_mut();
149 if base.is_none() {
150 *base = Some(indent);
151 }
152 }
153
154 pub fn get_base_indentation(&self) -> usize {
155 self.base_indentation.borrow().unwrap_or(0)
156 }
157
158 pub fn normalize_indentation(&self, indent: usize) -> usize {
159 let base = self.get_base_indentation();
160 indent.saturating_sub(base)
161 }
162
163 pub fn push_indentation(&self, indent: usize) {
164 self.indentation_stack.borrow_mut().push(indent);
165 }
166
167 pub fn pop_indentation(&self) {
168 let mut stack = self.indentation_stack.borrow_mut();
169 if stack.len() > 1 {
170 stack.pop();
171 }
172 }
173
174 pub fn current_indentation(&self) -> usize {
175 *self.indentation_stack.borrow().last().unwrap_or(&0)
176 }
177
178 pub fn check_indentation(&self, indent: usize) -> bool {
179 indent >= self.current_indentation()
180 }
181
182 pub fn enter_nested_context(&self) -> SavedContext {
185 let saved = SavedContext {
186 indentation_stack: self.indentation_stack.replace(vec![0]),
187 base_indentation: self.base_indentation.replace(None),
188 };
189 *self.nested_depth.borrow_mut() += 1;
190 saved
191 }
192
193 pub fn exit_nested_context(&self, saved: SavedContext) {
195 *self.indentation_stack.borrow_mut() = saved.indentation_stack;
196 *self.base_indentation.borrow_mut() = saved.base_indentation;
197 let mut depth = self.nested_depth.borrow_mut();
198 if *depth > 0 {
199 *depth -= 1;
200 }
201 }
202
203 pub fn is_inside_nested_context(&self) -> bool {
204 *self.nested_depth.borrow() > 0
205 }
206
207 fn expected_at(&self, at: &str, what: &'static str) {
211 let address = at.as_ptr() as usize;
212 let mut furthest = self.furthest.borrow_mut();
213 match furthest.address {
214 Some(recorded) if recorded > address => {}
215 Some(recorded) if recorded == address => {
216 if !furthest.expected.contains(&what) {
217 furthest.expected.push(what);
218 }
219 }
220 _ => {
221 furthest.address = Some(address);
222 furthest.expected = vec![what];
223 }
224 }
225 }
226
227 fn failure(&self, document: &str, error: &nom::Err<nom::error::Error<&str>>) -> ParseFailure {
233 let base = document.as_ptr() as usize;
234 let (nom_offset, kind) = match error {
235 nom::Err::Error(e) | nom::Err::Failure(e) => (
236 (e.input.as_ptr() as usize).saturating_sub(base),
237 Some(e.code),
238 ),
239 nom::Err::Incomplete(_) => (document.len(), None),
240 };
241 let furthest = self.furthest.borrow();
242 let tracked = furthest
243 .address
244 .map(|address| address.saturating_sub(base))
245 .unwrap_or(0);
246 let offset = tracked.max(nom_offset).min(document.len());
247 let expected = if tracked == offset {
248 furthest.expected.clone()
249 } else {
250 Vec::new()
253 };
254 ParseFailure {
255 offset,
256 expected,
257 kind,
258 }
259 }
260}
261
262fn expected<'a, T>(
264 input: &'a str,
265 state: &ParserState,
266 what: &'static str,
267 kind: nom::error::ErrorKind,
268) -> IResult<&'a str, T> {
269 state.expected_at(input, what);
270 Err(nom::Err::Error(nom::error::Error::new(input, kind)))
271}
272
273pub(crate) fn is_whitespace_char(c: char) -> bool {
274 c == ' ' || c == '\t' || c == '\n' || c == '\r'
275}
276
277fn is_horizontal_whitespace(c: char) -> bool {
278 c == ' ' || c == '\t'
279}
280
281fn is_reference_char(c: char) -> bool {
282 !is_whitespace_char(c) && c != '(' && c != ':' && c != ')'
283}
284
285fn horizontal_whitespace(input: &str) -> IResult<&str, &str> {
286 take_while(is_horizontal_whitespace)(input)
287}
288
289fn whitespace(input: &str) -> IResult<&str, &str> {
290 take_while(is_whitespace_char)(input)
291}
292
293fn simple_reference(input: &str) -> IResult<&str, String> {
294 take_while1(is_reference_char)
295 .map(|s: &str| s.to_string())
296 .parse(input)
297}
298
299pub fn quoted_reference_end(document: &str, start: usize) -> Option<usize> {
306 let reading = read_reference(document.get(start..)?)?;
307 Some(start + reading.length)
308}
309
310fn delimited_reference<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, String> {
314 let references = state
315 .references
316 .get_or_init(|| DelimitedReferences::new(input));
317 match references.read(input) {
318 Some(Reading { value, length }) => Ok((&input[length..], value)),
319 None => Err(nom::Err::Error(nom::error::Error::new(
320 input,
321 nom::error::ErrorKind::Tag,
322 ))),
323 }
324}
325
326fn reference<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, String> {
327 let parsed = alt((|i| delimited_reference(i, state), simple_reference)).parse(input);
330 if parsed.is_err() {
331 state.expected_at(input, "a reference");
332 }
333 parsed
334}
335
336fn eol<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, &'a str> {
337 let parsed = alt((
338 preceded(horizontal_whitespace, alt((line_ending, tag("\r")))),
339 preceded(horizontal_whitespace, eof),
340 |i| nested_group_end(i, state),
341 ))
342 .parse(input);
343 if parsed.is_err() {
344 state.expected_at(input, "end of line");
345 }
346 parsed
347}
348
349fn nested_group_end<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, &'a str> {
352 if !state.is_inside_nested_context() {
353 return Err(nom::Err::Error(nom::error::Error::new(
354 input,
355 nom::error::ErrorKind::Verify,
356 )));
357 }
358 let (rest, _) = horizontal_whitespace(input)?;
359 if rest.starts_with(')') {
360 Ok((rest, ""))
361 } else {
362 expected(rest, state, "\")\"", nom::error::ErrorKind::Char)
363 }
364}
365
366fn skip_empty_lines(input: &str) -> &str {
369 let mut rest = input;
370 loop {
371 let line_start = rest.trim_start_matches(is_horizontal_whitespace);
372 match strip_line_ending(line_start) {
373 Some(next) => rest = next,
374 None => return rest,
375 }
376 }
377}
378
379fn strip_line_ending(input: &str) -> Option<&str> {
380 input
381 .strip_prefix("\r\n")
382 .or_else(|| input.strip_prefix('\n'))
383 .or_else(|| input.strip_prefix('\r'))
384}
385
386fn reference_or_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
387 alt((
388 |i| nested_group(i, state),
389 (|i| reference(i, state)).map(Link::new_singlet),
390 ))
391 .parse(input)
392}
393
394fn single_line_value_and_whitespace<'a>(
395 input: &'a str,
396 state: &ParserState,
397) -> IResult<&'a str, Link> {
398 preceded(horizontal_whitespace, |i| reference_or_link(i, state)).parse(input)
399}
400
401fn single_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
402 many1(|i| single_line_value_and_whitespace(i, state)).parse(input)
403}
404
405fn single_line_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
406 let (input, _) = horizontal_whitespace(input)?;
407 let (input, id) = reference(input, state)?;
408 let (input, _) = horizontal_whitespace(input)?;
409 let (input, _) = colon(input, state)?;
410 let (input, values) = single_line_values(input, state)?;
411 Ok((input, Link::new_link(Some(id), values)))
412}
413
414fn colon<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, char> {
416 character(':', input, state, "\":\"")
417}
418
419fn character<'a>(
421 wanted: char,
422 input: &'a str,
423 state: &ParserState,
424 what: &'static str,
425) -> IResult<&'a str, char> {
426 let parsed: IResult<&'a str, char> = char(wanted).parse(input);
427 match parsed {
428 Ok(parsed) => Ok(parsed),
429 Err(_) => expected(input, state, what, nom::error::ErrorKind::Char),
430 }
431}
432
433fn single_line_value_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
434 (|i| single_line_values(i, state))
435 .map(Link::new_value)
436 .parse(input)
437}
438
439fn indented_id_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
440 let (input, id) = reference(input, state)?;
441 let (input, _) = horizontal_whitespace(input)?;
442 let (input, _) = colon(input, state)?;
443 let (input, _) = eol(input, state)?;
444 Ok((input, Link::new_indented_id(id)))
445}
446
447fn nested_group<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
451 let (body_input, _) = character('(', input, state, "\"(\"")?;
452 let saved = state.enter_nested_context();
453 let result = nested_group_body(body_input, state);
454 state.exit_nested_context(saved);
455 result
456}
457
458fn nested_group_body<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
459 if let Ok((rest, body)) = links(skip_empty_lines(input), state) {
460 let (rest, _) = whitespace(rest)?;
461 let (rest, _) = closing_parenthesis(rest, state)?;
462 return Ok((rest, Link::new_nested(body)));
463 }
464 let (rest, _) = whitespace(input)?;
465 let (rest, _) = closing_parenthesis(rest, state)?;
466 Ok((rest, Link::new_nested(vec![])))
467}
468
469fn closing_parenthesis<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, char> {
471 character(')', input, state, "\")\"")
472}
473
474fn single_line_any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
475 alt((
476 terminated(|i| single_line_link(i, state), |i| eol(i, state)),
477 terminated(|i| single_line_value_link(i, state), |i| eol(i, state)),
478 ))
479 .parse(input)
480}
481
482fn any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
483 alt((
484 terminated(|i| nested_group(i, state), |i| eol(i, state)),
485 |i| indented_id_link(i, state),
486 |i| single_line_any_link(i, state),
487 ))
488 .parse(input)
489}
490
491fn count_indentation(input: &str) -> IResult<&str, usize> {
492 take_while(|c| c == ' ').map(|s: &str| s.len()).parse(input)
493}
494
495fn push_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
496 let (input, spaces) = count_indentation(skip_empty_lines(input))?;
497 let normalized_spaces = state.normalize_indentation(spaces);
498 let current = state.current_indentation();
499
500 if normalized_spaces > current {
501 state.push_indentation(normalized_spaces);
502 Ok((input, ()))
503 } else {
504 Err(nom::Err::Error(nom::error::Error::new(
505 input,
506 nom::error::ErrorKind::Verify,
507 )))
508 }
509}
510
511fn check_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
512 let (input, spaces) = count_indentation(input)?;
513 let normalized_spaces = state.normalize_indentation(spaces);
514
515 if state.check_indentation(normalized_spaces) {
516 Ok((input, ()))
517 } else {
518 Err(nom::Err::Error(nom::error::Error::new(
519 input,
520 nom::error::ErrorKind::Verify,
521 )))
522 }
523}
524
525fn element<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
526 let (input, link) = any_link(input, state)?;
527
528 let indentation = state.indentation_stack.borrow().clone();
529 if let Ok((child_input, _)) = push_indentation(input, state) {
530 if let Ok((rest, children)) = links(child_input, state) {
531 return Ok((rest, link.with_children(children)));
532 }
533 state.indentation_stack.replace(indentation);
536 }
537 Ok((input, link))
538}
539
540fn first_line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
541 let (input, spaces) = count_indentation(input)?;
544 state.set_base_indentation(spaces);
545 element(input, state)
546}
547
548fn line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
549 preceded(|i| check_indentation(i, state), |i| element(i, state)).parse(skip_empty_lines(input))
551}
552
553fn links<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
554 let (input, first) = first_line(input, state)?;
555 let (input, rest) = many0(|i| line(i, state)).parse(input)?;
556
557 state.pop_indentation();
558
559 let mut result = vec![first];
560 result.extend(rest);
561 Ok((input, result))
562}
563
564pub fn parse_document(input: &str) -> IResult<&str, Vec<Link>> {
565 let state = ParserState::new();
566 document(input, &state)
567}
568
569pub fn parse_document_with_diagnostics(input: &str) -> Result<Vec<Link>, ParseFailure> {
576 let state = ParserState::new();
577 match document(input, &state) {
578 Ok((_, links)) => Ok(links),
579 Err(error) => Err(state.failure(input, &error)),
580 }
581}
582
583fn document<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
584 state
587 .references
588 .get_or_init(|| DelimitedReferences::new(input));
589
590 let document = skip_empty_lines(input);
592
593 if document.trim_matches(is_whitespace_char).is_empty() {
595 return Ok(("", vec![]));
596 }
597
598 let (rest, result) = links(document, state)?;
599 let (rest, _) = whitespace(rest)?;
600 let end: IResult<&'a str, &'a str> = eof(rest);
601 let (rest, _) = match end {
602 Ok(parsed) => parsed,
603 Err(_) => return expected(rest, state, "end of input", nom::error::ErrorKind::Eof),
604 };
605
606 Ok((rest, result))
607}