我目前找到的最佳解决方案是使用nom_locate,其跨度定义为
use nom_locate::LocatedSpanEx;
#[derive(Clone, PartialEq, Debug)]
struct LexState {
pub accept_literal: bool,
}
type Span<'a> = LocatedSpanEx<&'a str, LexState>;
然后你可以通过修改状态
fn set_accept_literal(
value: bool,
code: IResult<Span, TokenPayload>,
) -> IResult<Span, TokenPayload> {
match code {
Ok(mut span) => {
span.0.extra.accept_literal = value;
Ok(span)
}
_ => code,
}
}
TokenPayload 是一个枚举,代表我的令牌内容。
现在你可以编写运算符解析器了:
fn mathematical_operators(code: Span) -> IResult<Span, TokenPayload> {
set_accept_literal(
true,
alt((
map(tag("*"), |_| TokenPayload::Multiply),
map(tag("/"), |_| TokenPayload::Divide),
map(tag("+"), |_| TokenPayload::Add),
map(tag("-"), |_| TokenPayload::Subtract),
map(tag("%"), |_| TokenPayload::Remainder),
))(code),
)
}
而整数解析器为:
fn parse_integer(code: Span) -> IResult<Span, TokenPayload> {
let chars = "1234567890";
// Sign ?
let (code, sign) = opt(tag("-"))(code)?;
let sign = sign.is_some();
if sign && !code.extra.accept_literal {
return Err(nom::Err::Error((code, ErrorKind::IsNot)));
}
let (code, slice) = take_while(move |c| chars.contains(c))(code)?;
match slice.fragment.parse::<i32>() {
Ok(value) => set_accept_literal(
false,
Ok((code, TokenPayload::Int32(if sign { -value } else { value }))),
),
Err(_) => Err(nom::Err::Error((code, ErrorKind::Tag))),
}
}
这可能不会赢得选美比赛,但它确实有效。剩下的部分应该是微不足道的。