Skip to content

Commit b2ea463

Browse files
add nom-iterator in rust
1 parent 1ac3d98 commit b2ea463

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

rust/lexer/nom-iterator/Cargo.toml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "nom-iterator"
3+
version = "0.1.0"
4+
authors = ["Yu Chen <[email protected]>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
nom = "5.0"

rust/lexer/nom-iterator/src/main.rs

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
extern crate nom;
2+
3+
use nom::IResult;
4+
use nom::bytes::complete::tag;
5+
use nom::sequence::{separated_pair, terminated};
6+
use nom::character::complete::alphanumeric1;
7+
use nom::combinator::iterator;
8+
use std::iter::Iterator;
9+
use std::collections::HashMap;
10+
11+
fn main() {
12+
let mut data = "abcabcabcabc";
13+
14+
fn parser(i: &str) -> IResult<&str, &str> {
15+
tag("abc")(i)
16+
}
17+
18+
// `from_fn` (available from Rust 1.34) can create an iterator
19+
// from a closure
20+
let it = std::iter::from_fn(move|| {
21+
match parser(data) {
22+
// when successful, a nom parser returns a tuple of
23+
// the remaining input and the output value.
24+
// So we replace the captured input data with the
25+
// remaining input, to be parsed on the next call
26+
Ok((i, o)) => {
27+
data = i;
28+
Some(o)
29+
},
30+
_ => None
31+
}
32+
});
33+
34+
for value in it {
35+
println!("parser returned: {}", value);
36+
}
37+
38+
println!("\n********************\n");
39+
40+
let data = "abcabcabcabc";
41+
42+
// if `from_fn` is not available, it is possible to fold
43+
// over an iterator of functions
44+
let res = std::iter::repeat(parser).take(3).try_fold((data, Vec::new()), |(data, mut acc), parser| {
45+
parser(data).map(|(i, o)| {
46+
acc.push(o);
47+
(i, acc)
48+
})
49+
});
50+
51+
// will print "parser iterator returned: Ok(("abc", ["abc", "abc", "abc"]))"
52+
println!("\nparser iterator returned: {:?}", res);
53+
54+
println!("\n********************\n");
55+
56+
let data = "key1:value1,key2:value2,key3:value3,;";
57+
58+
// `nom::combinator::iterator` will return an iterator
59+
// producing the parsed values. Compared to the previous
60+
// solutions:
61+
// - we can work with a normal iterator like `from_fn`
62+
// - we can get the remaining input afterwards, like with the `try_fold` trick
63+
let mut nom_it = iterator(data, terminated(separated_pair(alphanumeric1, tag(":"), alphanumeric1), tag(",")));
64+
65+
let res = nom_it.map(|(k, v)| (k.to_uppercase(), v)).collect::<HashMap<_, _>>();
66+
67+
let parser_result: IResult<_, _> = nom_it.finish();
68+
let (remaining_input, ()) = parser_result.unwrap();
69+
70+
// will print "iterator returned {"key1": "value1", "key3": "value3", "key2": "value2"}, remaining input is ';'"
71+
println!("iterator returned {:?}, remaining input is '{}'", res, remaining_input);
72+
}
73+

0 commit comments

Comments
 (0)