use pom::parser::*;
#[derive(Clone, Debug, PartialEq)]
struct Container {
containers: Vec<Container>,
contents: Vec<String>,
}
enum TmpContainerOrContent {
Container(Container),
Content(String),
}
fn whitespace<'a>() -> Parser<'a, u8, ()> {
one_of(b" \t\r\n").repeat(0..).discard()
}
fn linebreak<'a>() -> Parser<'a, u8, ()> {
sym(b'\r').opt() * sym(b'\n').discard()
}
fn indented<'a>() -> Parser<'a, u8, Vec<u8>> {
sym(b'\t') * none_of(b"\n\r").repeat(1..) - linebreak()
}
fn empty<'a>() -> Parser<'a, u8, ()> {
one_of(b" \t").repeat(0..).discard() - linebreak()
}
fn content<'a>() -> Parser<'a, u8, String> {
none_of(b" \t\r\n").repeat(1..).convert(String::from_utf8) - linebreak()
}
fn subcontainer<'a>() -> Parser<'a, u8, (Vec<Container>, Vec<String>)> {
(call(container).map(|ctr| TmpContainerOrContent::Container(ctr))
| content().map(|ctn| TmpContainerOrContent::Content(ctn)))
.repeat(1..)
.map(|tmp| {
tmp.into_iter().fold((vec![], vec![]), |acc, x| match x {
TmpContainerOrContent::Container(ct) => (
acc.0.into_iter().chain(vec![ct].into_iter()).collect(),
acc.1,
),
TmpContainerOrContent::Content(cn) => (
acc.0,
acc.1.into_iter().chain(vec![cn].into_iter()).collect(),
),
})
})
}
fn container<'a>() -> Parser<'a, u8, Container> {
seq(b"Container\n")
* (indented() | empty().map(|()| vec![]))
.repeat(1..)
.map(|lines| {
lines
.into_iter()
.filter(|line| line.len() > 0)
.fold(vec![], |accum, line| {
accum
.into_iter()
.chain(line.into_iter().chain(vec![b'\n'].into_iter()))
.collect()
})
})
.map(|deden| subcontainer().parse(&deden).expect("subcont"))
.map(|(containers, contents)| Container {
containers,
contents,
})
}
fn mylang<'a>() -> Parser<'a, u8, Vec<Container>> {
whitespace() * list(call(container), whitespace())
}
fn main() -> Result<(), ()> {
let input = br#"
Container
Container
a
b
c
1
2
3
Container
q
Container
foo
bar
Container
baz
quux
"#;
assert_eq!(
mylang().parse(input),
Ok(vec![
Container {
containers: vec![
Container {
containers: vec![],
contents: vec!["a".into(), "b".into(), "c".into(),]
},
Container {
containers: vec![],
contents: vec!["q".into(),]
}
],
contents: vec!["1".into(), "2".into(), "3".into(),]
},
Container {
containers: vec![Container {
contents: vec!["baz".into(), "quux".into(),],
containers: vec![],
},],
contents: vec!["foo".into(), "bar".into(),]
},
])
);
Ok(())
}