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}
87
88pub struct SavedContext {
90 indentation_stack: Vec<usize>,
91 base_indentation: Option<usize>,
92}
93
94impl Default for ParserState {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100impl ParserState {
101 pub fn new() -> Self {
102 ParserState {
103 indentation_stack: RefCell::new(vec![0]),
104 base_indentation: RefCell::new(None),
105 nested_depth: RefCell::new(0),
106 }
107 }
108
109 pub fn set_base_indentation(&self, indent: usize) {
110 let mut base = self.base_indentation.borrow_mut();
111 if base.is_none() {
112 *base = Some(indent);
113 }
114 }
115
116 pub fn get_base_indentation(&self) -> usize {
117 self.base_indentation.borrow().unwrap_or(0)
118 }
119
120 pub fn normalize_indentation(&self, indent: usize) -> usize {
121 let base = self.get_base_indentation();
122 indent.saturating_sub(base)
123 }
124
125 pub fn push_indentation(&self, indent: usize) {
126 self.indentation_stack.borrow_mut().push(indent);
127 }
128
129 pub fn pop_indentation(&self) {
130 let mut stack = self.indentation_stack.borrow_mut();
131 if stack.len() > 1 {
132 stack.pop();
133 }
134 }
135
136 pub fn current_indentation(&self) -> usize {
137 *self.indentation_stack.borrow().last().unwrap_or(&0)
138 }
139
140 pub fn check_indentation(&self, indent: usize) -> bool {
141 indent >= self.current_indentation()
142 }
143
144 pub fn enter_nested_context(&self) -> SavedContext {
147 let saved = SavedContext {
148 indentation_stack: self.indentation_stack.replace(vec![0]),
149 base_indentation: self.base_indentation.replace(None),
150 };
151 *self.nested_depth.borrow_mut() += 1;
152 saved
153 }
154
155 pub fn exit_nested_context(&self, saved: SavedContext) {
157 *self.indentation_stack.borrow_mut() = saved.indentation_stack;
158 *self.base_indentation.borrow_mut() = saved.base_indentation;
159 let mut depth = self.nested_depth.borrow_mut();
160 if *depth > 0 {
161 *depth -= 1;
162 }
163 }
164
165 pub fn is_inside_nested_context(&self) -> bool {
166 *self.nested_depth.borrow() > 0
167 }
168}
169
170fn is_whitespace_char(c: char) -> bool {
171 c == ' ' || c == '\t' || c == '\n' || c == '\r'
172}
173
174fn is_horizontal_whitespace(c: char) -> bool {
175 c == ' ' || c == '\t'
176}
177
178fn is_reference_char(c: char) -> bool {
179 !is_whitespace_char(c) && c != '(' && c != ':' && c != ')'
180}
181
182fn horizontal_whitespace(input: &str) -> IResult<&str, &str> {
183 take_while(is_horizontal_whitespace)(input)
184}
185
186fn whitespace(input: &str) -> IResult<&str, &str> {
187 take_while(is_whitespace_char)(input)
188}
189
190fn simple_reference(input: &str) -> IResult<&str, String> {
191 take_while1(is_reference_char)
192 .map(|s: &str| s.to_string())
193 .parse(input)
194}
195
196fn parse_multi_quote_string(
199 input: &str,
200 quote_char: char,
201 quote_count: usize,
202) -> IResult<&str, String> {
203 let open_close = quote_char.to_string().repeat(quote_count);
204 let escape_seq = quote_char.to_string().repeat(quote_count * 2);
205 let escape_val = quote_char.to_string().repeat(quote_count);
206
207 if !input.starts_with(&open_close) {
209 return Err(nom::Err::Error(nom::error::Error::new(
210 input,
211 nom::error::ErrorKind::Tag,
212 )));
213 }
214
215 let mut remaining = &input[open_close.len()..];
216 let mut content = String::new();
217
218 loop {
219 if remaining.is_empty() {
220 return Err(nom::Err::Error(nom::error::Error::new(
221 input,
222 nom::error::ErrorKind::Tag,
223 )));
224 }
225
226 if remaining.starts_with(&escape_seq) {
228 content.push_str(&escape_val);
229 remaining = &remaining[escape_seq.len()..];
230 continue;
231 }
232
233 if remaining.starts_with(&open_close) {
235 let after_close = &remaining[open_close.len()..];
236 if after_close.is_empty() || !after_close.starts_with(quote_char) {
238 return Ok((after_close, content));
239 }
240 }
241
242 let c = remaining.chars().next().unwrap();
244 content.push(c);
245 remaining = &remaining[c.len_utf8()..];
246 }
247}
248
249fn is_substantive_body(content: &str) -> bool {
254 let mut depth: isize = 0;
255 let mut has_visible = false;
256
257 for c in content.chars() {
258 match c {
259 '(' => depth += 1,
260 ')' => {
261 depth -= 1;
262 if depth < 0 {
263 return false;
264 }
265 }
266 _ => {}
267 }
268 if !c.is_whitespace() {
269 has_visible = true;
270 }
271 }
272
273 has_visible && depth == 0
274}
275
276fn parse_dynamic_quote_string(input: &str, quote_char: char) -> IResult<&str, String> {
283 let quote_count = input.chars().take_while(|&c| c == quote_char).count();
285
286 if quote_count == 0 {
287 return Err(nom::Err::Error(nom::error::Error::new(
288 input,
289 nom::error::ErrorKind::Tag,
290 )));
291 }
292
293 let is_even_run = quote_count % 2 == 0;
294
295 if let Ok((rest, content)) = parse_multi_quote_string(input, quote_char, quote_count) {
296 if !is_even_run || is_substantive_body(&content) {
297 return Ok((rest, content));
298 }
299 }
300
301 if is_even_run {
302 return Ok((&input[quote_count * quote_char.len_utf8()..], String::new()));
303 }
304
305 Err(nom::Err::Error(nom::error::Error::new(
306 input,
307 nom::error::ErrorKind::Tag,
308 )))
309}
310
311fn double_quoted_dynamic(input: &str) -> IResult<&str, String> {
312 parse_dynamic_quote_string(input, '"')
313}
314
315fn single_quoted_dynamic(input: &str) -> IResult<&str, String> {
316 parse_dynamic_quote_string(input, '\'')
317}
318
319fn backtick_quoted_dynamic(input: &str) -> IResult<&str, String> {
320 parse_dynamic_quote_string(input, '`')
321}
322
323fn reference(input: &str) -> IResult<&str, String> {
324 alt((
327 double_quoted_dynamic,
328 single_quoted_dynamic,
329 backtick_quoted_dynamic,
330 simple_reference,
331 ))
332 .parse(input)
333}
334
335fn eol<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, &'a str> {
336 alt((
337 preceded(horizontal_whitespace, line_ending),
338 preceded(horizontal_whitespace, eof),
339 |i| nested_group_end(i, state),
340 ))
341 .parse(input)
342}
343
344fn nested_group_end<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, &'a str> {
347 if !state.is_inside_nested_context() {
348 return Err(nom::Err::Error(nom::error::Error::new(
349 input,
350 nom::error::ErrorKind::Verify,
351 )));
352 }
353 let (rest, _) = horizontal_whitespace(input)?;
354 if rest.starts_with(')') {
355 Ok((rest, ""))
356 } else {
357 Err(nom::Err::Error(nom::error::Error::new(
358 input,
359 nom::error::ErrorKind::Char,
360 )))
361 }
362}
363
364fn skip_empty_lines(input: &str) -> &str {
367 let mut rest = input;
368 loop {
369 let line_start = rest.trim_start_matches(is_horizontal_whitespace);
370 match strip_line_ending(line_start) {
371 Some(next) => rest = next,
372 None => return rest,
373 }
374 }
375}
376
377fn strip_line_ending(input: &str) -> Option<&str> {
378 input
379 .strip_prefix("\r\n")
380 .or_else(|| input.strip_prefix('\n'))
381}
382
383fn reference_or_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
384 alt((|i| nested_group(i, state), reference.map(Link::new_singlet))).parse(input)
385}
386
387fn single_line_value_and_whitespace<'a>(
388 input: &'a str,
389 state: &ParserState,
390) -> IResult<&'a str, Link> {
391 preceded(horizontal_whitespace, |i| reference_or_link(i, state)).parse(input)
392}
393
394fn single_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
395 many1(|i| single_line_value_and_whitespace(i, state)).parse(input)
396}
397
398fn single_line_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
399 (
400 horizontal_whitespace,
401 reference,
402 horizontal_whitespace,
403 char(':'),
404 |i| single_line_values(i, state),
405 )
406 .map(|(_, id, _, _, values)| Link::new_link(Some(id), values))
407 .parse(input)
408}
409
410fn single_line_value_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
411 (|i| single_line_values(i, state))
412 .map(|values| {
413 if values.len() == 1
414 && values[0].id.is_some()
415 && values[0].values.is_empty()
416 && values[0].children.is_empty()
417 {
418 Link::new_singlet(values[0].id.clone().unwrap())
419 } else {
420 Link::new_value(values)
421 }
422 })
423 .parse(input)
424}
425
426fn indented_id_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
427 (reference, horizontal_whitespace, char(':'), |i| {
428 eol(i, state)
429 })
430 .map(|(id, _, _, _)| Link::new_indented_id(id))
431 .parse(input)
432}
433
434fn nested_group<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
438 let (body_input, _) = char('(').parse(input)?;
439 let saved = state.enter_nested_context();
440 let result = nested_group_body(body_input, state);
441 state.exit_nested_context(saved);
442 result
443}
444
445fn nested_group_body<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
446 if let Ok((rest, body)) = links(skip_empty_lines(input), state) {
447 let (rest, _) = whitespace(rest)?;
448 let (rest, _) = char(')').parse(rest)?;
449 return Ok((rest, Link::new_nested(body)));
450 }
451 let (rest, _) = whitespace(input)?;
452 let (rest, _) = char(')').parse(rest)?;
453 Ok((rest, Link::new_nested(vec![])))
454}
455
456fn single_line_any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
457 alt((
458 terminated(|i| single_line_link(i, state), |i| eol(i, state)),
459 terminated(|i| single_line_value_link(i, state), |i| eol(i, state)),
460 ))
461 .parse(input)
462}
463
464fn any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
465 alt((
466 terminated(|i| nested_group(i, state), |i| eol(i, state)),
467 |i| indented_id_link(i, state),
468 |i| single_line_any_link(i, state),
469 ))
470 .parse(input)
471}
472
473fn count_indentation(input: &str) -> IResult<&str, usize> {
474 take_while(|c| c == ' ').map(|s: &str| s.len()).parse(input)
475}
476
477fn push_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
478 let (input, spaces) = count_indentation(skip_empty_lines(input))?;
479 let normalized_spaces = state.normalize_indentation(spaces);
480 let current = state.current_indentation();
481
482 if normalized_spaces > current {
483 state.push_indentation(normalized_spaces);
484 Ok((input, ()))
485 } else {
486 Err(nom::Err::Error(nom::error::Error::new(
487 input,
488 nom::error::ErrorKind::Verify,
489 )))
490 }
491}
492
493fn check_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
494 let (input, spaces) = count_indentation(input)?;
495 let normalized_spaces = state.normalize_indentation(spaces);
496
497 if state.check_indentation(normalized_spaces) {
498 Ok((input, ()))
499 } else {
500 Err(nom::Err::Error(nom::error::Error::new(
501 input,
502 nom::error::ErrorKind::Verify,
503 )))
504 }
505}
506
507fn element<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
508 let (input, link) = any_link(input, state)?;
509
510 if let Ok((input, _)) = push_indentation(input, state) {
511 let (input, children) = links(input, state)?;
512 Ok((input, link.with_children(children)))
513 } else {
514 Ok((input, link))
515 }
516}
517
518fn first_line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
519 let (input, spaces) = count_indentation(input)?;
522 state.set_base_indentation(spaces);
523 element(input, state)
524}
525
526fn line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
527 preceded(|i| check_indentation(i, state), |i| element(i, state)).parse(skip_empty_lines(input))
529}
530
531fn links<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
532 let (input, first) = first_line(input, state)?;
533 let (input, rest) = many0(|i| line(i, state)).parse(input)?;
534
535 state.pop_indentation();
536
537 let mut result = vec![first];
538 result.extend(rest);
539 Ok((input, result))
540}
541
542pub fn parse_document(input: &str) -> IResult<&str, Vec<Link>> {
543 let state = ParserState::new();
544
545 let input = skip_empty_lines(input);
547
548 if input.trim().is_empty() {
550 return Ok(("", vec![]));
551 }
552
553 let (input, result) = links(input, &state)?;
554 let (input, _) = whitespace(input)?;
555 let (input, _) = eof(input)?;
556
557 Ok((input, result))
558}