Как использовать 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
})
)
);