1use nom::{
2 branch::alt,
3 bytes::complete::{take_while, take_while1},
4 character::complete::{char, line_ending},
5 combinator::eof,
6 multi::{many0, many1},
7 sequence::{preceded, terminated},
8 IResult, Parser,
9};
10use std::cell::RefCell;
11
12#[derive(Debug, Clone, PartialEq)]
13pub struct Link {
14 pub id: Option<String>,
15 pub values: Vec<Link>,
16 pub children: Vec<Link>,
17 pub is_indented_id: bool,
18 pub nested: Option<Vec<Link>>,
21}
22
23impl Link {
24 pub fn new_singlet(id: String) -> Self {
25 Link {
26 id: Some(id),
27 values: vec![],
28 children: vec![],
29 is_indented_id: false,
30 nested: None,
31 }
32 }
33
34 pub fn new_indented_id(id: String) -> Self {
35 Link {
36 id: Some(id),
37 values: vec![],
38 children: vec![],
39 is_indented_id: true,
40 nested: None,
41 }
42 }
43
44 pub fn new_value(values: Vec<Link>) -> Self {
45 Link {
46 id: None,
47 values,
48 children: vec![],
49 is_indented_id: false,
50 nested: None,
51 }
52 }
53
54 pub fn new_link(id: Option<String>, values: Vec<Link>) -> Self {
55 Link {
56 id,
57 values,
58 children: vec![],
59 is_indented_id: false,
60 nested: None,
61 }
62 }
63
64 pub fn new_nested(body: Vec<Link>) -> Self {
67 Link {
68 id: None,
69 values: vec![],
70 children: vec![],
71 is_indented_id: false,
72 nested: Some(body),
73 }
74 }
75
76 pub fn with_children(mut self, children: Vec<Link>) -> Self {
77 self.children = children;
78 self
79 }
80}
81
82pub struct ParserState {
83 indentation_stack: RefCell<Vec<usize>>,
84 base_indentation: RefCell<Option<usize>>,
85 nested_depth: RefCell<usize>,
86 furthest: RefCell<FurthestFailure>,
87}
88
89#[derive(Debug, Clone, Default)]
99struct FurthestFailure {
100 address: Option<usize>,
103 expected: Vec<&'static str>,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct ParseFailure {
109 pub offset: usize,
111 pub expected: Vec<&'static str>,
115 pub kind: Option<nom::error::ErrorKind>,
119}
120
121pub struct SavedContext {
123 indentation_stack: Vec<usize>,
124 base_indentation: Option<usize>,
125}
126
127impl Default for ParserState {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl ParserState {
134 pub fn new() -> Self {
135 ParserState {
136 indentation_stack: RefCell::new(vec![0]),
137 base_indentation: RefCell::new(None),
138 nested_depth: RefCell::new(0),
139 furthest: RefCell::new(FurthestFailure::default()),
140 }
141 }
142
143 pub fn set_base_indentation(&self, indent: usize) {
144 let mut base = self.base_indentation.borrow_mut();
145 if base.is_none() {
146 *base = Some(indent);
147 }
148 }
149
150 pub fn get_base_indentation(&self) -> usize {
151 self.base_indentation.borrow().unwrap_or(0)
152 }
153
154 pub fn normalize_indentation(&self, indent: usize) -> usize {
155 let base = self.get_base_indentation();
156 indent.saturating_sub(base)
157 }
158
159 pub fn push_indentation(&self, indent: usize) {
160 self.indentation_stack.borrow_mut().push(indent);
161 }
162
163 pub fn pop_indentation(&self) {
164 let mut stack = self.indentation_stack.borrow_mut();
165 if stack.len() > 1 {
166 stack.pop();
167 }
168 }
169
170 pub fn current_indentation(&self) -> usize {
171 *self.indentation_stack.borrow().last().unwrap_or(&0)
172 }
173
174 pub fn check_indentation(&self, indent: usize) -> bool {
175 indent >= self.current_indentation()
176 }
177
178 pub fn enter_nested_context(&self) -> SavedContext {
181 let saved = SavedContext {
182 indentation_stack: self.indentation_stack.replace(vec![0]),
183 base_indentation: self.base_indentation.replace(None),
184 };
185 *self.nested_depth.borrow_mut() += 1;
186 saved
187 }
188
189 pub fn exit_nested_context(&self, saved: SavedContext) {
191 *self.indentation_stack.borrow_mut() = saved.indentation_stack;
192 *self.base_indentation.borrow_mut() = saved.base_indentation;
193 let mut depth = self.nested_depth.borrow_mut();
194 if *depth > 0 {
195 *depth -= 1;
196 }
197 }
198
199 pub fn is_inside_nested_context(&self) -> bool {
200 *self.nested_depth.borrow() > 0
201 }
202
203 fn expected_at(&self, at: &str, what: &'static str) {
207 let address = at.as_ptr() as usize;
208 let mut furthest = self.furthest.borrow_mut();
209 match furthest.address {
210 Some(recorded) if recorded > address => {}
211 Some(recorded) if recorded == address => {
212 if !furthest.expected.contains(&what) {
213 furthest.expected.push(what);
214 }
215 }
216 _ => {
217 furthest.address = Some(address);
218 furthest.expected = vec![what];
219 }
220 }
221 }
222
223 fn failure(&self, document: &str, error: &nom::Err<nom::error::Error<&str>>) -> ParseFailure {
229 let base = document.as_ptr() as usize;
230 let (nom_offset, kind) = match error {
231 nom::Err::Error(e) | nom::Err::Failure(e) => (
232 (e.input.as_ptr() as usize).saturating_sub(base),
233 Some(e.code),
234 ),
235 nom::Err::Incomplete(_) => (document.len(), None),
236 };
237 let furthest = self.furthest.borrow();
238 let tracked = furthest
239 .address
240 .map(|address| address.saturating_sub(base))
241 .unwrap_or(0);
242 let offset = tracked.max(nom_offset).min(document.len());
243 let expected = if tracked == offset {
244 furthest.expected.clone()
245 } else {
246 Vec::new()
249 };
250 ParseFailure {
251 offset,
252 expected,
253 kind,
254 }
255 }
256}
257
258fn expected<'a, T>(
260 input: &'a str,
261 state: &ParserState,
262 what: &'static str,
263 kind: nom::error::ErrorKind,
264) -> IResult<&'a str, T> {
265 state.expected_at(input, what);
266 Err(nom::Err::Error(nom::error::Error::new(input, kind)))
267}
268
269fn is_whitespace_char(c: char) -> bool {
270 c == ' ' || c == '\t' || c == '\n' || c == '\r'
271}
272
273fn is_horizontal_whitespace(c: char) -> bool {
274 c == ' ' || c == '\t'
275}
276
277fn is_reference_char(c: char) -> bool {
278 !is_whitespace_char(c) && c != '(' && c != ':' && c != ')'
279}
280
281fn horizontal_whitespace(input: &str) -> IResult<&str, &str> {
282 take_while(is_horizontal_whitespace)(input)
283}
284
285fn whitespace(input: &str) -> IResult<&str, &str> {
286 take_while(is_whitespace_char)(input)
287}
288
289fn simple_reference(input: &str) -> IResult<&str, String> {
290 take_while1(is_reference_char)
291 .map(|s: &str| s.to_string())
292 .parse(input)
293}
294
295fn parse_multi_quote_string(
298 input: &str,
299 quote_char: char,
300 quote_count: usize,
301) -> IResult<&str, String> {
302 let open_close = quote_char.to_string().repeat(quote_count);
303 let escape_seq = quote_char.to_string().repeat(quote_count * 2);
304 let escape_val = quote_char.to_string().repeat(quote_count);
305
306 if !input.starts_with(&open_close) {
308 return Err(nom::Err::Error(nom::error::Error::new(
309 input,
310 nom::error::ErrorKind::Tag,
311 )));
312 }
313
314 let mut remaining = &input[open_close.len()..];
315 let mut content = String::new();
316
317 loop {
318 if remaining.is_empty() {
319 return Err(nom::Err::Error(nom::error::Error::new(
320 input,
321 nom::error::ErrorKind::Tag,
322 )));
323 }
324
325 if remaining.starts_with(&escape_seq) {
327 content.push_str(&escape_val);
328 remaining = &remaining[escape_seq.len()..];
329 continue;
330 }
331
332 if remaining.starts_with(&open_close) {
334 let after_close = &remaining[open_close.len()..];
335 if after_close.is_empty() || !after_close.starts_with(quote_char) {
337 return Ok((after_close, content));
338 }
339 }
340
341 let c = remaining.chars().next().unwrap();
343 content.push(c);
344 remaining = &remaining[c.len_utf8()..];
345 }
346}
347
348fn is_substantive_body(content: &str) -> bool {
353 let mut depth: isize = 0;
354 let mut has_visible = false;
355
356 for c in content.chars() {
357 match c {
358 '(' => depth += 1,
359 ')' => {
360 depth -= 1;
361 if depth < 0 {
362 return false;
363 }
364 }
365 _ => {}
366 }
367 if !c.is_whitespace() {
368 has_visible = true;
369 }
370 }
371
372 has_visible && depth == 0
373}
374
375fn parse_dynamic_quote_string(input: &str, quote_char: char) -> IResult<&str, String> {
382 let quote_count = input.chars().take_while(|&c| c == quote_char).count();
384
385 if quote_count == 0 {
386 return Err(nom::Err::Error(nom::error::Error::new(
387 input,
388 nom::error::ErrorKind::Tag,
389 )));
390 }
391
392 let is_even_run = quote_count % 2 == 0;
393
394 if let Ok((rest, content)) = parse_multi_quote_string(input, quote_char, quote_count) {
395 if !is_even_run || is_substantive_body(&content) {
396 return Ok((rest, content));
397 }
398 }
399
400 if is_even_run {
401 return Ok((&input[quote_count * quote_char.len_utf8()..], String::new()));
402 }
403
404 Err(nom::Err::Error(nom::error::Error::new(
405 input,
406 nom::error::ErrorKind::Tag,
407 )))
408}
409
410pub fn quoted_reference_end(document: &str, start: usize) -> Option<usize> {
417 let rest = document.get(start..)?;
418 let quote = rest.chars().next()?;
419 if !matches!(quote, '"' | '\'' | '`') {
420 return None;
421 }
422 let (remaining, _) = parse_dynamic_quote_string(rest, quote).ok()?;
423 Some(document.len() - remaining.len())
424}
425
426fn double_quoted_dynamic(input: &str) -> IResult<&str, String> {
427 parse_dynamic_quote_string(input, '"')
428}
429
430fn single_quoted_dynamic(input: &str) -> IResult<&str, String> {
431 parse_dynamic_quote_string(input, '\'')
432}
433
434fn backtick_quoted_dynamic(input: &str) -> IResult<&str, String> {
435 parse_dynamic_quote_string(input, '`')
436}
437
438fn reference<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, String> {
439 let parsed = alt((
442 double_quoted_dynamic,
443 single_quoted_dynamic,
444 backtick_quoted_dynamic,
445 simple_reference,
446 ))
447 .parse(input);
448 if parsed.is_err() {
449 state.expected_at(input, "a reference");
450 }
451 parsed
452}
453
454fn eol<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, &'a str> {
455 let parsed = alt((
456 preceded(horizontal_whitespace, line_ending),
457 preceded(horizontal_whitespace, eof),
458 |i| nested_group_end(i, state),
459 ))
460 .parse(input);
461 if parsed.is_err() {
462 state.expected_at(input, "end of line");
463 }
464 parsed
465}
466
467fn nested_group_end<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, &'a str> {
470 if !state.is_inside_nested_context() {
471 return Err(nom::Err::Error(nom::error::Error::new(
472 input,
473 nom::error::ErrorKind::Verify,
474 )));
475 }
476 let (rest, _) = horizontal_whitespace(input)?;
477 if rest.starts_with(')') {
478 Ok((rest, ""))
479 } else {
480 expected(rest, state, "\")\"", nom::error::ErrorKind::Char)
481 }
482}
483
484fn skip_empty_lines(input: &str) -> &str {
487 let mut rest = input;
488 loop {
489 let line_start = rest.trim_start_matches(is_horizontal_whitespace);
490 match strip_line_ending(line_start) {
491 Some(next) => rest = next,
492 None => return rest,
493 }
494 }
495}
496
497fn strip_line_ending(input: &str) -> Option<&str> {
498 input
499 .strip_prefix("\r\n")
500 .or_else(|| input.strip_prefix('\n'))
501}
502
503fn reference_or_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
504 alt((
505 |i| nested_group(i, state),
506 (|i| reference(i, state)).map(Link::new_singlet),
507 ))
508 .parse(input)
509}
510
511fn single_line_value_and_whitespace<'a>(
512 input: &'a str,
513 state: &ParserState,
514) -> IResult<&'a str, Link> {
515 preceded(horizontal_whitespace, |i| reference_or_link(i, state)).parse(input)
516}
517
518fn single_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
519 many1(|i| single_line_value_and_whitespace(i, state)).parse(input)
520}
521
522fn single_line_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
523 let (input, _) = horizontal_whitespace(input)?;
524 let (input, id) = reference(input, state)?;
525 let (input, _) = horizontal_whitespace(input)?;
526 let (input, _) = colon(input, state)?;
527 let (input, values) = single_line_values(input, state)?;
528 Ok((input, Link::new_link(Some(id), values)))
529}
530
531fn colon<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, char> {
533 character(':', input, state, "\":\"")
534}
535
536fn character<'a>(
538 wanted: char,
539 input: &'a str,
540 state: &ParserState,
541 what: &'static str,
542) -> IResult<&'a str, char> {
543 let parsed: IResult<&'a str, char> = char(wanted).parse(input);
544 match parsed {
545 Ok(parsed) => Ok(parsed),
546 Err(_) => expected(input, state, what, nom::error::ErrorKind::Char),
547 }
548}
549
550fn single_line_value_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
551 (|i| single_line_values(i, state))
552 .map(|values| {
553 if values.len() == 1
554 && values[0].id.is_some()
555 && values[0].values.is_empty()
556 && values[0].children.is_empty()
557 {
558 Link::new_singlet(values[0].id.clone().unwrap())
559 } else {
560 Link::new_value(values)
561 }
562 })
563 .parse(input)
564}
565
566fn indented_id_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
567 let (input, id) = reference(input, state)?;
568 let (input, _) = horizontal_whitespace(input)?;
569 let (input, _) = colon(input, state)?;
570 let (input, _) = eol(input, state)?;
571 Ok((input, Link::new_indented_id(id)))
572}
573
574fn nested_group<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
578 let (body_input, _) = character('(', input, state, "\"(\"")?;
579 let saved = state.enter_nested_context();
580 let result = nested_group_body(body_input, state);
581 state.exit_nested_context(saved);
582 result
583}
584
585fn nested_group_body<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
586 if let Ok((rest, body)) = links(skip_empty_lines(input), state) {
587 let (rest, _) = whitespace(rest)?;
588 let (rest, _) = closing_parenthesis(rest, state)?;
589 return Ok((rest, Link::new_nested(body)));
590 }
591 let (rest, _) = whitespace(input)?;
592 let (rest, _) = closing_parenthesis(rest, state)?;
593 Ok((rest, Link::new_nested(vec![])))
594}
595
596fn closing_parenthesis<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, char> {
598 character(')', input, state, "\")\"")
599}
600
601fn single_line_any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
602 alt((
603 terminated(|i| single_line_link(i, state), |i| eol(i, state)),
604 terminated(|i| single_line_value_link(i, state), |i| eol(i, state)),
605 ))
606 .parse(input)
607}
608
609fn any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
610 alt((
611 terminated(|i| nested_group(i, state), |i| eol(i, state)),
612 |i| indented_id_link(i, state),
613 |i| single_line_any_link(i, state),
614 ))
615 .parse(input)
616}
617
618fn count_indentation(input: &str) -> IResult<&str, usize> {
619 take_while(|c| c == ' ').map(|s: &str| s.len()).parse(input)
620}
621
622fn push_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
623 let (input, spaces) = count_indentation(skip_empty_lines(input))?;
624 let normalized_spaces = state.normalize_indentation(spaces);
625 let current = state.current_indentation();
626
627 if normalized_spaces > current {
628 state.push_indentation(normalized_spaces);
629 Ok((input, ()))
630 } else {
631 Err(nom::Err::Error(nom::error::Error::new(
632 input,
633 nom::error::ErrorKind::Verify,
634 )))
635 }
636}
637
638fn check_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
639 let (input, spaces) = count_indentation(input)?;
640 let normalized_spaces = state.normalize_indentation(spaces);
641
642 if state.check_indentation(normalized_spaces) {
643 Ok((input, ()))
644 } else {
645 Err(nom::Err::Error(nom::error::Error::new(
646 input,
647 nom::error::ErrorKind::Verify,
648 )))
649 }
650}
651
652fn element<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
653 let (input, link) = any_link(input, state)?;
654
655 if let Ok((input, _)) = push_indentation(input, state) {
656 let (input, children) = links(input, state)?;
657 Ok((input, link.with_children(children)))
658 } else {
659 Ok((input, link))
660 }
661}
662
663fn first_line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
664 let (input, spaces) = count_indentation(input)?;
667 state.set_base_indentation(spaces);
668 element(input, state)
669}
670
671fn line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
672 preceded(|i| check_indentation(i, state), |i| element(i, state)).parse(skip_empty_lines(input))
674}
675
676fn links<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
677 let (input, first) = first_line(input, state)?;
678 let (input, rest) = many0(|i| line(i, state)).parse(input)?;
679
680 state.pop_indentation();
681
682 let mut result = vec![first];
683 result.extend(rest);
684 Ok((input, result))
685}
686
687pub fn parse_document(input: &str) -> IResult<&str, Vec<Link>> {
688 let state = ParserState::new();
689 document(input, &state)
690}
691
692pub fn parse_document_with_diagnostics(input: &str) -> Result<Vec<Link>, ParseFailure> {
699 let state = ParserState::new();
700 match document(input, &state) {
701 Ok((_, links)) => Ok(links),
702 Err(error) => Err(state.failure(input, &error)),
703 }
704}
705
706fn document<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
707 let document = skip_empty_lines(input);
709
710 if document.trim().is_empty() {
712 return Ok(("", vec![]));
713 }
714
715 let (rest, result) = links(document, state)?;
716 let (rest, _) = whitespace(rest)?;
717 let end: IResult<&'a str, &'a str> = eof(rest);
718 let (rest, _) = match end {
719 Ok(parsed) => parsed,
720 Err(_) => return expected(rest, state, "end of input", nom::error::ErrorKind::Eof),
721 };
722
723 Ok((rest, result))
724}