1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use std::collections::HashMap;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_till1;
use nom::character::complete::alpha1;
use nom::character::complete::alphanumeric1;
use nom::character::complete::char;
use nom::character::complete::line_ending;
use nom::character::complete::multispace1;
use nom::character::complete::not_line_ending;
use nom::character::complete::space0;
use nom::character::complete::space1;
use nom::combinator::all_consuming;
use nom::combinator::map;
use nom::combinator::not;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::multi::many0;
use nom::sequence::delimited;
use nom::sequence::pair;
use nom::sequence::preceded;
use nom::sequence::terminated;
use nom::sequence::tuple;
use nom::IResult;
pub fn comment(input: &str) -> IResult<&str, &str> {
preceded(char('#'), not_line_ending)(input)
}
pub fn blank_lines(input: &str) -> IResult<&str, &str> {
recognize(many0(alt((multispace1, comment))))(input)
}
fn identifier(input: &str) -> IResult<&str, &str> {
recognize(pair(
alt((alpha1, tag("_"))),
many0(alt((alphanumeric1, tag("_")))),
))(input)
}
pub fn parse_file(input: &str) -> IResult<&str, std::collections::HashMap<String, Vec<Vec<&str>>>> {
map(all_consuming(many0(section)), |sections: Vec<_>| {
let mut m = HashMap::new();
for (name, lines) in sections {
m.insert(name.to_string(), lines);
}
m
})(input)
}
fn section(input: &str) -> IResult<&str, (&str, Vec<Vec<&str>>)> {
terminated(
tuple((section_name, many0(section_line))),
terminated(section_end, blank_lines),
)(input)
}
fn section_name(input: &str) -> IResult<&str, &str> {
delimited(blank_lines, identifier, terminated(space0, line_ending))(input)
}
fn section_end(input: &str) -> IResult<&str, &str> {
tag("end")(input)
}
fn section_line(input: &str) -> IResult<&str, Vec<&str>> {
delimited(
blank_lines,
preceded(not(peek(section_end)), columns),
blank_lines,
)(input)
}
fn columns(input: &str) -> IResult<&str, Vec<&str>> {
many0(delimited(column_space, column, column_space))(input)
}
fn column(input: &str) -> IResult<&str, &str> {
take_till1(|c: char| !c.is_ascii_graphic() || c == '#' || c == ',')(input)
}
pub fn column_space(input: &str) -> IResult<&str, &str> {
recognize(many0(alt((space1, comment, tag(",")))))(input)
}
pub fn parse(input: &str) -> Option<std::collections::HashMap<String, Vec<Vec<&str>>>> {
match parse_file(input) {
Ok((_, v)) => Some(v),
Err(_) => None,
}
}