1use nom::{
2 branch::alt,
3 bytes::complete::{tag, 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
269pub(crate) fn 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 !is_whitespace_char(c) {
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, alt((line_ending, tag("\r")))),
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 .or_else(|| input.strip_prefix('\r'))
502}
503
504fn reference_or_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
505 alt((
506 |i| nested_group(i, state),
507 (|i| reference(i, state)).map(Link::new_singlet),
508 ))
509 .parse(input)
510}
511
512fn single_line_value_and_whitespace<'a>(
513 input: &'a str,
514 state: &ParserState,
515) -> IResult<&'a str, Link> {
516 preceded(horizontal_whitespace, |i| reference_or_link(i, state)).parse(input)
517}
518
519fn single_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
520 many1(|i| single_line_value_and_whitespace(i, state)).parse(input)
521}
522
523fn single_line_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
524 let (input, _) = horizontal_whitespace(input)?;
525 let (input, id) = reference(input, state)?;
526 let (input, _) = horizontal_whitespace(input)?;
527 let (input, _) = colon(input, state)?;
528 let (input, values) = single_line_values(input, state)?;
529 Ok((input, Link::new_link(Some(id), values)))
530}
531
532fn colon<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, char> {
534 character(':', input, state, "\":\"")
535}
536
537fn character<'a>(
539 wanted: char,
540 input: &'a str,
541 state: &ParserState,
542 what: &'static str,
543) -> IResult<&'a str, char> {
544 let parsed: IResult<&'a str, char> = char(wanted).parse(input);
545 match parsed {
546 Ok(parsed) => Ok(parsed),
547 Err(_) => expected(input, state, what, nom::error::ErrorKind::Char),
548 }
549}
550
551fn single_line_value_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
552 (|i| single_line_values(i, state))
553 .map(Link::new_value)
554 .parse(input)
555}
556
557fn indented_id_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
558 let (input, id) = reference(input, state)?;
559 let (input, _) = horizontal_whitespace(input)?;
560 let (input, _) = colon(input, state)?;
561 let (input, _) = eol(input, state)?;
562 Ok((input, Link::new_indented_id(id)))
563}
564
565fn nested_group<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
569 let (body_input, _) = character('(', input, state, "\"(\"")?;
570 let saved = state.enter_nested_context();
571 let result = nested_group_body(body_input, state);
572 state.exit_nested_context(saved);
573 result
574}
575
576fn nested_group_body<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
577 if let Ok((rest, body)) = links(skip_empty_lines(input), state) {
578 let (rest, _) = whitespace(rest)?;
579 let (rest, _) = closing_parenthesis(rest, state)?;
580 return Ok((rest, Link::new_nested(body)));
581 }
582 let (rest, _) = whitespace(input)?;
583 let (rest, _) = closing_parenthesis(rest, state)?;
584 Ok((rest, Link::new_nested(vec![])))
585}
586
587fn closing_parenthesis<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, char> {
589 character(')', input, state, "\")\"")
590}
591
592fn single_line_any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
593 alt((
594 terminated(|i| single_line_link(i, state), |i| eol(i, state)),
595 terminated(|i| single_line_value_link(i, state), |i| eol(i, state)),
596 ))
597 .parse(input)
598}
599
600fn any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
601 alt((
602 terminated(|i| nested_group(i, state), |i| eol(i, state)),
603 |i| indented_id_link(i, state),
604 |i| single_line_any_link(i, state),
605 ))
606 .parse(input)
607}
608
609fn count_indentation(input: &str) -> IResult<&str, usize> {
610 take_while(|c| c == ' ').map(|s: &str| s.len()).parse(input)
611}
612
613fn push_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
614 let (input, spaces) = count_indentation(skip_empty_lines(input))?;
615 let normalized_spaces = state.normalize_indentation(spaces);
616 let current = state.current_indentation();
617
618 if normalized_spaces > current {
619 state.push_indentation(normalized_spaces);
620 Ok((input, ()))
621 } else {
622 Err(nom::Err::Error(nom::error::Error::new(
623 input,
624 nom::error::ErrorKind::Verify,
625 )))
626 }
627}
628
629fn check_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
630 let (input, spaces) = count_indentation(input)?;
631 let normalized_spaces = state.normalize_indentation(spaces);
632
633 if state.check_indentation(normalized_spaces) {
634 Ok((input, ()))
635 } else {
636 Err(nom::Err::Error(nom::error::Error::new(
637 input,
638 nom::error::ErrorKind::Verify,
639 )))
640 }
641}
642
643fn element<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
644 let (input, link) = any_link(input, state)?;
645
646 let indentation = state.indentation_stack.borrow().clone();
647 if let Ok((child_input, _)) = push_indentation(input, state) {
648 if let Ok((rest, children)) = links(child_input, state) {
649 return Ok((rest, link.with_children(children)));
650 }
651 state.indentation_stack.replace(indentation);
654 }
655 Ok((input, link))
656}
657
658fn first_line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
659 let (input, spaces) = count_indentation(input)?;
662 state.set_base_indentation(spaces);
663 element(input, state)
664}
665
666fn line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
667 preceded(|i| check_indentation(i, state), |i| element(i, state)).parse(skip_empty_lines(input))
669}
670
671fn links<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
672 let (input, first) = first_line(input, state)?;
673 let (input, rest) = many0(|i| line(i, state)).parse(input)?;
674
675 state.pop_indentation();
676
677 let mut result = vec![first];
678 result.extend(rest);
679 Ok((input, result))
680}
681
682pub fn parse_document(input: &str) -> IResult<&str, Vec<Link>> {
683 let state = ParserState::new();
684 document(input, &state)
685}
686
687pub fn parse_document_with_diagnostics(input: &str) -> Result<Vec<Link>, ParseFailure> {
694 let state = ParserState::new();
695 match document(input, &state) {
696 Ok((_, links)) => Ok(links),
697 Err(error) => Err(state.failure(input, &error)),
698 }
699}
700
701fn document<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
702 let document = skip_empty_lines(input);
704
705 if document.trim_matches(is_whitespace_char).is_empty() {
707 return Ok(("", vec![]));
708 }
709
710 let (rest, result) = links(document, state)?;
711 let (rest, _) = whitespace(rest)?;
712 let end: IResult<&'a str, &'a str> = eof(rest);
713 let (rest, _) = match end {
714 Ok(parsed) => parsed,
715 Err(_) => return expected(rest, state, "end of input", nom::error::ErrorKind::Eof),
716 };
717
718 Ok((rest, result))
719}