nom::sequence::preceded
Matches an object from the first parser and discards it, then gets an object from the second parser.
上面是rust nom官网对于preceded的解释
UUUUUUUUU
你完全正确,nom::sequence::preceded
的定义确实是:
Matches an object from the first parser and discards it, then gets an object from the second parser.
这意味着 preceded
会先运行第一个解析器来匹配输入,成功后丢弃第一个解析器的结果,然后继续执行第二个解析器并返回第二个解析器的结果。
在你提供的代码中,使用 preceded
时:
let parse_number = preceded(
opt(alt((char('+'), char('-')))), // 可选的 + 或 - 符号
digit1, // 至少一个数字
);
preceded
中的两个解析器分别是:
- 第一个解析器:
opt(alt((char('+'), char('-'))))
,用于匹配一个可选的符号+
或-
。opt
表示这个符号是可选的,alt
表示匹配+
或-
中的一个。 - 第二个解析器:
digit1
,用于匹配至少一个数字。
这里,preceded
会先尝试匹配一个符号(+
或 -
),然后继续匹配至少一个数字。第一个解析器的结果(符号)会被丢弃,最终返回第二个解析器(数字)解析的结果。
如果需要修改函数使其同时返回符号信息(是否为负数),可以将符号部分的匹配改为通过其他方式返回,例如 alt
和 opt
组合时,可以让符号作为返回值之一。
修改代码的正确方式:
为了正确地获取符号信息,并且返回 bool
类型的变量,我们需要对符号部分进行修改,而不是完全依赖 preceded
。我会改用一个更加合适的解析方式。
解决方案:
use nom::{
character::complete::{digit1, char},
combinator::{map, opt},
sequence::preceded,
IResult,
};
// Token 枚举
#[derive(Debug, PartialEq)]
pub enum Token {
Integer64(i64),
// 其他Token类型
}
// 解析带符号或不带符号的整数
fn integer64(input: &str) -> IResult<&str, (Token, bool)> {
// 判断第一个字符是否为 '-',并设置 bool 类型变量
let is_negative = input.chars().next() == Some('-');
// 允许 + 或 - 符号,后接至少一个数字
let parse_number = alt((
map(preceded(char('-'), digit1), |s: &str| Token::Integer64(s.parse().unwrap())),
map(preceded(char('+'), digit1), |s: &str| Token::Integer64(s.parse().unwrap())),
map(digit1, |s: &str| Token::Integer64(s.parse().unwrap())),
));
let result = parse_number(input);
result.map(|(next_input, token)| {
(next_input, (token, is_negative))
})
}
fn main() {
let input = "-123";
let result = integer64(input);
match result {
Ok((next_input, (token, is_negative))) => {
println!("Parsed token: {:?}, Is Negative: {}", token, is_negative);
}
Err(_) => {
println!("Parsing failed");
}
}
}
解析逻辑解释:
- 我们修改了符号部分的解析,使其直接返回符号信息和数值部分。
- 使用
alt
来处理:- 如果符号是
"-"
,则通过preceded(char('-'), digit1)
解析。 - 如果符号是
"+"
,则通过preceded(char('+'), digit1)
解析。 - 如果没有符号,则直接通过
digit1
解析。
- 如果符号是
- 解析后,直接返回数值和符号的标记元组
(Token::Integer64(i64), bool)
,表示数字和是否为负数。
测试:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_integer64() {
let inputs = vec![
("123", (Token::Integer64(123), false)),
("-123", (Token::Integer64(-123), true)),
("+123", (Token::Integer64(123), false)),
("0", (Token::Integer64(0), false)),
];
for (input, expected) in inputs {
let result = integer64(input);
assert!(result.is_ok());
let (_, (token, is_negative)) = result.unwrap();
assert_eq!(token, expected.0);
assert_eq!(is_negative, expected.1);
}
}
#[test]
fn test_invalid_integer64() {
let inputs = vec![
"123abc", // 非法的数字
"-abc", // 非法的数字
"+", // 没有数字
];
for input in inputs {
let result = integer64(input);
assert!(result.is_err());
}
}
}
总结:
preceded
是用于匹配前一个部分并丢弃结果的。如果要同时获取符号信息,可以在解析符号时单独处理,并将符号作为返回值的一部分。- 使用
alt
和preceded
更灵活地处理符号,并同时获取符号信息和数值。