Разбор целого числа с помощью 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);
    }
}
Другие вопросы по тегам