Разбор целого числа с помощью nom
Я ожидал, что это будет довольно легко, но оказывается, что это не так. Практически все, что я пробую, дает мне "Incomplete(Size(1))". Моя лучшая догадка сейчас:
named!(my_u64(&str) -> u64,
map_res!(recognize!(nom::digit), u64::from_str)
);
Тестовое задание:
#[cfg(test)]
mod test {
#[test]
fn my_u64() {
assert_eq!(Ok(("", 0)), super::my_u64("0"));
}
}
Иногда в моих вариациях (например, добавление завершено!) Я смог получить его для анализа, если я добавлю символ в конец.
Я хотел бы получить рабочий анализ для этого (в конечном счете, я надеюсь, что это позволит мне создать синтаксический анализатор для типа оболочки u64), но в целом я хотел бы получить представление о том, как правильно построить синтаксический анализатор самостоятельно.,
Спасибо...
1 ответ
Nom 4 сделал обработку частичных данных намного более строгой, чем в предыдущих версиях, чтобы лучше поддерживать потоковые парсеры и пользовательские типы ввода.
Фактически, если синтаксический анализатор исчерпал ввод, и он не может сказать, что у него исчерпан ввод, он всегда вернет Err::Incomplete
, Это может также содержать информацию о том, сколько именно входных данных ожидал парсер (в вашем случае, по крайней мере, еще 1 байт).
Он определяет, есть ли потенциальные дополнительные данные, используя AtEof
черта характера. Это всегда возвращается false
за &str
а также &[u8]
, поскольку они не предоставляют никакой информации о том, завершены они или нет!
Хитрость заключается в том, чтобы изменить тип ввода ваших парсеров, чтобы было ясно, что ввод всегда будет завершен - Nom предоставляет CompleteStr
а также CompleteByteSlice
обертки для этой цели, или вы можете реализовать свой собственный тип ввода.
Таким образом, чтобы ваш парсер работал как положено, он должен выглядеть примерно так:
named!(my_u64(CompleteStr) -> u64,
map_res!(recognize!(nom::digit), u64::from_str)
);
И ваш тест будет выглядеть примерно так:
#[cfg(test)]
mod test {
#[test]
fn my_u64() {
assert_eq!(Ok((CompleteStr(""), 0)), super::my_u64(CompleteStr("0")));
}
}
Смотрите сообщение для Nom 4 для более подробной информации.
По состоянию на nom 5.1.1
подход к объединению парсеров изменился с макросов на функциональные, что более подробно обсуждается в блоге автора nom.
Вместе с этим изменением последовало другое - потоковые и полные парсеры теперь находятся в разных модулях, и вам нужно явно выбрать, какой тип синтаксического анализа вам нужен. Чаще всего существует четкое различие с именем модуля.
Старые макросы сохраняются, но работают строго в потоковом режиме. Типы вродеCompleteStr
или CompleteByteSlice
ушли.
Чтобы написать код, который вы просили по -новому, вы могли бы сделать это, например, вот так (обратите внимание на явноеcharacter::complete
в импорте)
Поскольку мне потребовалось время, чтобы понять это - парсеры, например map_res
вернуть impl Fn(I) -> IResult<I, O2, E>
вот почему есть дополнительная пара круглых скобок - чтобы назвать это закрытие.
use std::str;
use nom::{
IResult,
character::complete::{
digit1
},
combinator::{
recognize,
map_res
}
};
fn my_u64(input : &str) -> IResult<&str, u64> {
map_res(recognize(digit1), str::parse)(input)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_my_u64() {
let input = "42";
let num = my_u64(input);
assert_eq!(Ok(("", 42u64)), num);
}
}