Rust - Как разобрать буквенные символы UTF-8 в nom? - PullRequest
1 голос
/ 11 января 2020

Я пытаюсь разобрать последовательности символов алфавитных символов, включая немецкие умлауты (ä ö ü) и другие алфавитные символы из кодировки UTF-8. Это парсер, который я попробовал первым:

named!(
    parse(&'a str) -> Self,
    map!(
        alpha1,
        |s| Self { chars: s.into() }
    )
);

Но он работает только для алфавитных символов ASCII (a-zA-Z). Я попытался выполнить синтаксический анализ char по char:

named!(
    parse(&str) -> Self,
    map!(
        take_while1!(nom::AsChar::is_alpha),
        |s| Self { chars: s.into() }
    )
);

Но это даже не проанализирует "привет", но приведет к ошибке Incomplete(Size(1)):

Как Вы анализируете алфавитные символы UTF-8 в nom ? Фрагмент из моего кода:

extern crate nom;

#[derive(PartialEq, Debug, Eq, Clone, Hash, Ord, PartialOrd)]
pub struct Word {
    chars: String,
}

impl From<&str> for Word {
    fn from(s: &str) -> Self {
        Self {
            chars: s.into(),
        }
    }
}

use nom::*;
impl Word {
    named!(
        parse(&str) -> Self,
        map!(
            take_while1!(nom::AsChar::is_alpha),
            |s| Self { chars: s.into() }
        )
    );
}


#[test]
fn parse_word() {
    let words = vec![
        "hello",
        "Hi",
        "aha",
        "Mathematik",
        "mathematical",
        "erfüllen"
    ];
    for word in words {
        assert_eq!(Word::parse(word).unwrap().1, Word::from(word));
    }
}

Когда я запускаю этот тест,

cargo test parse_word

я получаю:

thread panicked at 'called `Result::unwrap()` on an `Err` value: Incomplete(Size(1))', ...

Я знаю, что char s уже UTF-8 закодирован в Rust (слава богу, всемогущий), но кажется, что библиотека nom не ведет себя так, как я ожидал. Я использую nom 5.1.0

Ответы [ 2 ]

2 голосов
/ 11 января 2020

Первое число 5 использует функцию для разбора, я советую использовать эту форму, потому что сообщение об ошибке намного лучше, а код намного чище. строка и более:

impl Word {
    fn parse(input: &str) -> IResult<&str, Self> {
        Ok((
            &input[input.len()..],
            Self {
                chars: input.to_string(),
            },
        ))
    }
}

Но я думаю, ваша цель - разобрать слово, поэтому вот пример того, что вы можете сделать:

#[derive(PartialEq, Debug, Eq, Clone, Hash, Ord, PartialOrd)]
pub struct Word {
    chars: String,
}

impl From<&str> for Word {
    fn from(s: &str) -> Self {
        Self { chars: s.into() }
    }
}

use nom::{character::complete::*, combinator::*, multi::*, sequence::*, IResult};

impl Word {
    fn parse(input: &str) -> IResult<&str, Self> {
        let (input, word) =
            delimited(space0, recognize(many1_count(none_of(" \t"))), space0)(input)?;
        Ok((
            input,
            Self {
                chars: word.to_string(),
            },
        ))
    }
}

#[test]
fn parse_word() {
    let words = vec![
        "hello",
        " Hi",
        "aha ",
        " Mathematik ",
        "  mathematical",
        "erfüllen ",
    ];
    for word in words {
        assert_eq!(Word::parse(word).unwrap().1, Word::from(word.trim()));
    }
}

Вы также можете создать пользовательскую функцию, которая использует is_alphabetic() вместо none_of(" \t"), но для этого требуется сделать пользовательскую ошибку для nom, и в настоящее время, на мой взгляд, это очень раздражает.

0 голосов
/ 19 марта 2020

В этом Github Issue сотрудник-участник быстро поднял библиотеку (nom-unicode), чтобы справиться с этим:

use nom_unicode::complete::{alphanumeric1};

impl Word {
    named!(
        parse(&'a str) -> Self,
        map!(
            alphanumeric1,
            |w| Self::new(w)
        )
    );
}
...