nom 解析一个单独的列表
nom parsing a separate list
我正在尝试使用 nom5.0
解析以下备用字符串
"A-Za-z0-9"
或
"A-Z|a-z|0-9"
我尝试了以下但无济于事
pub enum Node {
Range(Vec<u8>),
}
fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
map(
separated_list(
alt((tag("|"), tag(""))),
tuple((take(1usize), tag("-"), take(1usize))),
),
|v: Vec<(&[u8], _, &[u8])>| {
Node::Range(
v.iter()
.map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
.flatten()
.collect(),
)
},
)(input)
}
版本 2。
fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
alt((
map(
separated_list(tag("|"), tuple((take(1usize), tag("-"), take(1usize)))),
|v: Vec<(&[u8], _, &[u8])>| {
Node::Range(
v.iter()
.map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
.flatten()
.collect(),
)
},
),
map(
many1(tuple((take(1usize), tag("-"), take(1usize)))),
|v: Vec<(&[u8], _, &[u8])>| {
Node::Range(
v.iter()
.map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
.flatten()
.collect(),
)
},
),
))(input)
}
#[test]
fn parse_compound() {
println!("{:?}", compound_range(b"A-Za-z0-9"));
println!("{:?}", compound_range(b"A-Z|a-z|0-9"));
}
我可以解析第一个或第二个,但不能同时解析。
有什么表达方式吗?
问题是 nom
总是采用它看到的第一条路径 有点 有效(例如,它不必消耗所有输入)。因此,理想情况下,您要做的是将第一个 "a-z"
(或其他)之后的路径拆分为两个可能的路径之一:您将 |
作为分隔符处理,或者不作为分隔符。
这是因为 nom
是一个解析器组合器库,不像正则表达式那样工作,它可以回溯到需要找到有效的东西为止。
无论如何,类似的东西应该可以工作:
fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
let single_range = |input| map(
separated_pair(take(1usize), tag("-"), take(1usize)),
|(l, r): (&[u8], &[u8])| (l[0], r[0])
)(input);
map(
opt(
map(
pair(
single_range,
alt((
preceded(tag("|"), separated_nonempty_list(
tag("|"),
single_range,
)),
many0(single_range)
))
),
|(first, rest)| Node::Range(
std::iter::once(first).chain(rest).flat_map(|(l, r)| l..r).collect()
)
),
),
|o| o.unwrap_or_else(|| Node::Range(Vec::new()))
)(input)
}
有没有更好的方法?大概。给定特定任务,实现您手动编写的解析器部分实际上可能是有意义的。它是这样工作的吗?大概。 (我没有测试过)
还有一点要记住:这可能会消耗太多,如果你希望在它之后有一些其他符合模式的东西。
我正在尝试使用 nom5.0
解析以下备用字符串"A-Za-z0-9"
或
"A-Z|a-z|0-9"
我尝试了以下但无济于事
pub enum Node {
Range(Vec<u8>),
}
fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
map(
separated_list(
alt((tag("|"), tag(""))),
tuple((take(1usize), tag("-"), take(1usize))),
),
|v: Vec<(&[u8], _, &[u8])>| {
Node::Range(
v.iter()
.map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
.flatten()
.collect(),
)
},
)(input)
}
版本 2。
fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
alt((
map(
separated_list(tag("|"), tuple((take(1usize), tag("-"), take(1usize)))),
|v: Vec<(&[u8], _, &[u8])>| {
Node::Range(
v.iter()
.map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
.flatten()
.collect(),
)
},
),
map(
many1(tuple((take(1usize), tag("-"), take(1usize)))),
|v: Vec<(&[u8], _, &[u8])>| {
Node::Range(
v.iter()
.map(|(s, _, e)| (s[0]..=e[0]).collect::<Vec<_>>())
.flatten()
.collect(),
)
},
),
))(input)
}
#[test]
fn parse_compound() {
println!("{:?}", compound_range(b"A-Za-z0-9"));
println!("{:?}", compound_range(b"A-Z|a-z|0-9"));
}
我可以解析第一个或第二个,但不能同时解析。 有什么表达方式吗?
问题是 nom
总是采用它看到的第一条路径 有点 有效(例如,它不必消耗所有输入)。因此,理想情况下,您要做的是将第一个 "a-z"
(或其他)之后的路径拆分为两个可能的路径之一:您将 |
作为分隔符处理,或者不作为分隔符。
这是因为 nom
是一个解析器组合器库,不像正则表达式那样工作,它可以回溯到需要找到有效的东西为止。
无论如何,类似的东西应该可以工作:
fn compound_range(input: &[u8]) -> IResult<&[u8], Node> {
let single_range = |input| map(
separated_pair(take(1usize), tag("-"), take(1usize)),
|(l, r): (&[u8], &[u8])| (l[0], r[0])
)(input);
map(
opt(
map(
pair(
single_range,
alt((
preceded(tag("|"), separated_nonempty_list(
tag("|"),
single_range,
)),
many0(single_range)
))
),
|(first, rest)| Node::Range(
std::iter::once(first).chain(rest).flat_map(|(l, r)| l..r).collect()
)
),
),
|o| o.unwrap_or_else(|| Node::Range(Vec::new()))
)(input)
}
有没有更好的方法?大概。给定特定任务,实现您手动编写的解析器部分实际上可能是有意义的。它是这样工作的吗?大概。 (我没有测试过)
还有一点要记住:这可能会消耗太多,如果你希望在它之后有一些其他符合模式的东西。