Как использовать nom для разбора строки со знаком в i32?

Используя ящик я пытаюсь написать парсер, который может распознать подписанный i32 номер из String т.е. может преобразовать строку -42 в i32 представление.

До сих пор я придумал следующее, но мне не удалось разобрать отрицательные числа:

use nom::types::CompleteStr;
use std::str::FromStr;

named!(
    i32_parser<CompleteStr, i32>,
    map_res!(nom::digit, |CompleteStr(s)| i32::from_str(s))
);

#[test]
fn parse_i32_positive() {
    assert_eq!(
        i32_parser(CompleteStr::from("42")),
        Ok((CompleteStr::from(""), 42))
    );
}

#[test]
fn parse_i32_negative() {
    assert_eq!(
        i32_parser(CompleteStr::from("-42")),
        Ok((CompleteStr::from(""), -42))
    );
}

Я также попробовал следующее, но с загадочной ошибкой компиляции:

named!(
     i32_parser<CompleteStr, i32>,
     map_res!(alt!(char!('-') | nom::digit), |CompleteStr(s)| i32::from_str(s))
 );
^ expected char, found struct `nom::types::CompleteStr`

Любое предложение о том, как это исправить? Или более простой способ добиться этого с помощью nom?

Я явно хочу реализовать это с помощью nom, потому что я пытаюсь разобрать более сложную структуру. i32::from_str(s) работает для простых строк, но это не то, что я ищу.

3 ответа

Решение

recognize! Макрос может помочь вам. Он возвращает проанализированную входную строку вместо вывода парсера, который затем может быть преобразован как обычно. Например:

named!(i32_parser<&str, i32>,
    map_res!(
        recognize!(tuple!(opt!(char!('-')), digit)),
        FromStr::from_str)
);

Обновлено это для современного имени, не являющегося макрокомандой:

      fn parse_isize(input: &str) -> IResult<&str, isize> {
    let (i, number) = map_res(recognize(preceded(opt(tag("-")), digit1)), |s| {
        isize::from_str(s)
    })(input)?;

    Ok((i, number))
}

alt!(char!('-') | nom::digit) "возвращает" символ, поэтому ваша лямбда должна принять символ в качестве аргумента. И это возможно '-'так зовет i32::from_str на нем не получится во время выполнения.

Вместо того, чтобы обрабатывать как знак, так и цифры за один шаг, вы должны разбить вашу проблему на две части, например. с помощью do_parse,

named!(
    i32_parser<CompleteStr, i32>,
    do_parse!(
        minus: opt!(char!('-')) >>
        digits: many1!(digit) >>
        ({
            let sign = if minus.is_some() { -1 } else { 1 };
            let mut number = 0;
            for digit in digits {
                number = number*10 + i32::from_str(digit.0).unwrap();
            }
            sign * number
        })
    )
);
Другие вопросы по тегам