1use nom::{
2 IResult,
3 branch::alt,
4 bytes::complete::{take_while, take_while1, is_not},
5 character::complete::{char, line_ending},
6 combinator::eof,
7 multi::{many0, many1},
8 sequence::{preceded, terminated, delimited},
9 Parser,
10};
11use std::cell::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}
19
20impl Link {
21 pub fn new_point(id: String) -> Self {
22 Link {
23 id: Some(id),
24 values: vec![],
25 children: vec![],
26 }
27 }
28
29 pub fn new_value(values: Vec<Link>) -> Self {
30 Link {
31 id: None,
32 values,
33 children: vec![],
34 }
35 }
36
37 pub fn new_link(id: Option<String>, values: Vec<Link>) -> Self {
38 Link {
39 id,
40 values,
41 children: vec![],
42 }
43 }
44
45 pub fn with_children(mut self, children: Vec<Link>) -> Self {
46 self.children = children;
47 self
48 }
49}
50
51pub struct ParserState {
52 indentation_stack: RefCell<Vec<usize>>,
53}
54
55impl ParserState {
56 pub fn new() -> Self {
57 ParserState {
58 indentation_stack: RefCell::new(vec![0]),
59 }
60 }
61
62 pub fn push_indentation(&self, indent: usize) {
63 self.indentation_stack.borrow_mut().push(indent);
64 }
65
66 pub fn pop_indentation(&self) {
67 let mut stack = self.indentation_stack.borrow_mut();
68 if stack.len() > 1 {
69 stack.pop();
70 }
71 }
72
73 pub fn current_indentation(&self) -> usize {
74 *self.indentation_stack.borrow().last().unwrap_or(&0)
75 }
76
77 pub fn check_indentation(&self, indent: usize) -> bool {
78 indent == self.current_indentation()
79 }
80}
81
82fn is_whitespace_char(c: char) -> bool {
83 c == ' ' || c == '\t' || c == '\n' || c == '\r'
84}
85
86fn is_horizontal_whitespace(c: char) -> bool {
87 c == ' ' || c == '\t'
88}
89
90fn is_reference_char(c: char) -> bool {
91 !is_whitespace_char(c) && c != '(' && c != ':' && c != ')'
92}
93
94fn horizontal_whitespace(input: &str) -> IResult<&str, &str> {
95 take_while(is_horizontal_whitespace)(input)
96}
97
98fn whitespace(input: &str) -> IResult<&str, &str> {
99 take_while(is_whitespace_char)(input)
100}
101
102fn simple_reference(input: &str) -> IResult<&str, String> {
103 take_while1(is_reference_char)
104 .map(|s: &str| s.to_string())
105 .parse(input)
106}
107
108fn double_quoted_reference(input: &str) -> IResult<&str, String> {
109 delimited(
110 char('"'),
111 is_not("\""),
112 char('"')
113 )
114 .map(|s: &str| s.to_string())
115 .parse(input)
116}
117
118fn single_quoted_reference(input: &str) -> IResult<&str, String> {
119 delimited(
120 char('\''),
121 is_not("'"),
122 char('\'')
123 )
124 .map(|s: &str| s.to_string())
125 .parse(input)
126}
127
128fn reference(input: &str) -> IResult<&str, String> {
129 alt((
130 double_quoted_reference,
131 single_quoted_reference,
132 simple_reference,
133 )).parse(input)
134}
135
136fn eol(input: &str) -> IResult<&str, &str> {
137 alt((
138 preceded(horizontal_whitespace, line_ending),
139 preceded(horizontal_whitespace, eof),
140 )).parse(input)
141}
142
143fn point_link(input: &str) -> IResult<&str, Link> {
144 reference.map(Link::new_point).parse(input)
145}
146
147fn single_line_point_link(input: &str) -> IResult<&str, Link> {
148 preceded(horizontal_whitespace, point_link).parse(input)
149}
150
151fn multi_line_point_link(input: &str) -> IResult<&str, Link> {
152 delimited(
153 (char('('), whitespace),
154 point_link,
155 (whitespace, char(')'))
156 ).parse(input)
157}
158
159fn reference_or_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
160 alt((
161 |i| multi_line_any_link(i, state),
162 reference.map(Link::new_point),
163 )).parse(input)
164}
165
166fn multi_line_value_and_whitespace<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
167 terminated(
168 |i| reference_or_link(i, state),
169 whitespace
170 ).parse(input)
171}
172
173fn multi_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
174 preceded(
175 whitespace,
176 many0(|i| multi_line_value_and_whitespace(i, state))
177 ).parse(input)
178}
179
180fn single_line_value_and_whitespace<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
181 preceded(
182 horizontal_whitespace,
183 |i| reference_or_link(i, state)
184 ).parse(input)
185}
186
187fn single_line_values<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
188 many1(|i| single_line_value_and_whitespace(i, state)).parse(input)
189}
190
191fn single_line_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
192 alt((
193 (
194 horizontal_whitespace,
195 reference,
196 horizontal_whitespace,
197 char(':'),
198 |i| single_line_values(i, state)
199 ).map(|(_, id, _, _, values)| Link::new_link(Some(id), values)),
200 (
201 horizontal_whitespace,
202 char(':'),
203 |i| single_line_values(i, state)
204 ).map(|(_, _, values)| Link::new_link(None, values)),
205 )).parse(input)
206}
207
208fn multi_line_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
209 alt((
210 (
211 char('('),
212 whitespace,
213 reference,
214 whitespace,
215 char(':'),
216 |i| multi_line_values(i, state),
217 whitespace,
218 char(')')
219 ).map(|(_, _, id, _, _, values, _, _)| Link::new_link(Some(id), values)),
220 (
221 char('('),
222 whitespace,
223 char(':'),
224 |i| multi_line_values(i, state),
225 whitespace,
226 char(')')
227 ).map(|(_, _, _, values, _, _)| Link::new_link(None, values)),
228 )).parse(input)
229}
230
231fn single_line_value_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
232 (|i| single_line_values(i, state))
233 .map(Link::new_value)
234 .parse(input)
235}
236
237fn multi_line_value_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
238 (
239 char('('),
240 |i| multi_line_values(i, state),
241 whitespace,
242 char(')')
243 ).map(|(_, values, _, _)| Link::new_value(values))
244 .parse(input)
245}
246
247fn multi_line_any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
248 alt((
249 multi_line_point_link,
250 |i| multi_line_value_link(i, state),
251 |i| multi_line_link(i, state),
252 )).parse(input)
253}
254
255fn single_line_any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
256 alt((
257 terminated(|i| single_line_link(i, state), eol),
258 terminated(single_line_point_link, eol),
259 terminated(|i| single_line_value_link(i, state), eol),
260 )).parse(input)
261}
262
263fn any_link<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
264 alt((
265 terminated(|i| multi_line_any_link(i, state), eol),
266 |i| single_line_any_link(i, state),
267 )).parse(input)
268}
269
270fn count_indentation(input: &str) -> IResult<&str, usize> {
271 take_while(|c| c == ' ')
272 .map(|s: &str| s.len())
273 .parse(input)
274}
275
276fn push_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
277 let (input, spaces) = count_indentation(input)?;
278 let current = state.current_indentation();
279
280 if spaces > current {
281 state.push_indentation(spaces);
282 Ok((input, ()))
283 } else {
284 Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Verify)))
285 }
286}
287
288fn check_indentation<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, ()> {
289 let (input, spaces) = count_indentation(input)?;
290
291 if state.check_indentation(spaces) {
292 Ok((input, ()))
293 } else {
294 Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Verify)))
295 }
296}
297
298fn element<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
299 let (input, link) = any_link(input, state)?;
300
301 if let Ok((input, _)) = push_indentation(input, state) {
302 let (input, children) = links(input, state)?;
303 Ok((input, link.with_children(children)))
304 } else {
305 Ok((input, link))
306 }
307}
308
309fn first_line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
310 element(input, state)
311}
312
313fn line<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Link> {
314 preceded(
315 |i| check_indentation(i, state),
316 |i| element(i, state)
317 ).parse(input)
318}
319
320fn links<'a>(input: &'a str, state: &ParserState) -> IResult<&'a str, Vec<Link>> {
321 let (input, first) = first_line(input, state)?;
322 let (input, rest) = many0(|i| line(i, state)).parse(input)?;
323
324 state.pop_indentation();
325
326 let mut result = vec![first];
327 result.extend(rest);
328 Ok((input, result))
329}
330
331pub fn parse_document(input: &str) -> IResult<&str, Vec<Link>> {
332 let state = ParserState::new();
333
334 let input = input.trim_start_matches(|c: char| c == '\n' || c == '\r');
336
337 if input.trim().is_empty() {
339 return Ok(("", vec![]));
340 }
341
342 let (input, result) = links(input, &state)?;
343 let (input, _) = whitespace(input)?;
344 let (input, _) = eof(input)?;
345
346 Ok((input, result))
347}
348