Разбор пользовательского идентификатора с помощью nom - PullRequest
1 голос
/ 19 апреля 2020

Мне интересно использовать nom комбинаторы синтаксического анализа для распознавания идентификаторов такого типа:

"a"
"a1"
"a_b"
"aA"
"aB_3_1"

Первым символом идентификатора должен быть алфавит c в нижнем регистре может последовать любая комбинация буквенного символа c и подчеркивания (таким образом, [a-zA-Z0-9_]*) с ограничением на то, что двойное (или более) подчеркивание не должно встречаться и подчеркивание не должно заканчиваться идентификатором, отклоняя эти случаи:

"Aa"
"aB_"
"a__a"
"_a"

Пока что я пришел с этим решением, но не уверен в правильности моего подхода:

pub fn identifier(s: &str) -> IResult<&str, &str> {
    let (i, _) = verify(anychar, |c: &char| c.is_lowercase())(s)?;
    let (j, _) = alphanumeric0(i)?;
    let (k, _) = recognize(opt(many1(preceded(underscore, alphanumeric1))))(j)?;
    Ok((k,s))
}

Также мне нужно обернуть вокруг recognize этот identifier парсер при его использовании, например это:

pub fn identifier2(s: &str) -> IResult<&str, &str> {
    (recognize(identifier))(s)
}

1 Ответ

1 голос
/ 20 апреля 2020

Вот вариант, который я придумал. Это в основном так же, как у вас; Я внес следующие изменения:

  • Самое главное, я добавил all_consuming, что гарантирует совпадение всего ввода. Ошибка в предложенной вами реализации заключается в том, что «aBa_» будет успешно соответствовать идентификатору «aBa» и оставит завершающий «_» непарсированным (возвращая его на входной стороне).
  • Переписано исключительно с точки зрения комбинаторов синтаксического анализа, вместо использования операторов ?.
  • Сделал сопоставление подчеркивания необязательным. Nom синтаксические анализаторы в целом жадные, поэтому это не приведет к снижению производительности.
  • Упрощено только до 2 предложений вместо 3. Парсер, по сути, запускает «соответствует любому символу нижнего регистра, за которым следует 0 или более прогонов необязательного _, за которым следуют еще 1 алфавитно-цифровая цифра ".
  • Изменено many1 на many0_count просто потому, что последний не выделяет вектор.
  • Сделал функцию generi c над типом ошибки, позволяя пользователям функции использовать любой тип ошибки, который они имеют sh.
pub fn identifier<'a, E: ParseError<&'a str>>(s: &'a str) -> IResult<&'a str, &'a str, E> {
    recognize(all_consuming(pair(
        verify(anychar, |&c| c.is_lowercase()),
        many0_count(preceded(opt(char('_')), alphanumeric1)),
    )))(s)
}

Эта функция в письменном виде проходит все предоставленные вами тесты. Если вы специально не хотите all_consuming, возможно, потому что он используется как часть большего набора анализаторов, вам придется вручную проверить, что распознанный идентификатор не заканчивается в _ символ.

...