nom::sequence::preceded Matches an object from the first parser and discards it, then gets an object from the second parser. 上面是rust nom官网对于preceded的解释

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 中的两个解析器分别是:

  1. 第一个解析器opt(alt((char('+'), char('-')))),用于匹配一个可选的符号 +-opt 表示这个符号是可选的,alt 表示匹配 +- 中的一个。
  2. 第二个解析器digit1,用于匹配至少一个数字。

这里,preceded 会先尝试匹配一个符号(+-),然后继续匹配至少一个数字。第一个解析器的结果(符号)会被丢弃,最终返回第二个解析器(数字)解析的结果。

如果需要修改函数使其同时返回符号信息(是否为负数),可以将符号部分的匹配改为通过其他方式返回,例如 altopt 组合时,可以让符号作为返回值之一。

修改代码的正确方式:

为了正确地获取符号信息,并且返回 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 是用于匹配前一个部分并丢弃结果的。如果要同时获取符号信息,可以在解析符号时单独处理,并将符号作为返回值的一部分。
  • 使用 altpreceded 更灵活地处理符号,并同时获取符号信息和数值。

posted on 2024-12-27 16:44  及途又八  阅读(28)  评论(0)    收藏  举报

导航