Parsing data in Rust
Дисклеймер
...и слово было RegExp
Разбор и Проверка Email по RFC 5322
Существующие проблемы при использовании RegExp
Дикий невнятный синтаксис
Обработка ошибок...
отсутствует
RegExp это дорогое удовольствие
Комбинаторные Парсеры
В дело вступает Nom
Что уже написано на Nom?
Напишем свой первый простой парсер
Задача
Написать простой парсер абстрактной css функции вида:
func(10px,10deg)
Первая комбинация: Углы
use nom::{digit, IResult, Err, Needed};
std::str;
#[derive(Debug, Clone, PartialEq)]
pub struct AngleRepr<'a, 'b> {
pub value: &'a str,
pub angle: &'b str,
}
named!(angle_type<&[u8], &str>, alt!(
tag!("rad") => { |_| "radians" } |
tag!("deg") => { |_| "degrees" }
));
named!(pub angle(&[u8]) -> UnitRepr, do_parse!(
value: digit >>
angle: angle_type >>
(UnitRepr::Angle(AngleRepr {
value: str::from_utf8(value).unwrap(),
angle
}))
));
Также по аналогии...
#[derive(Debug, Clone, PartialEq)]
pub struct LengthRepr<'a, 'b> {
pub value: &'a str,
pub unit: &'b str,
}
named!(length_type<&[u8], &str>, alt!(
tag!("%") => { |_| "percent" } |
tag!("px") => { |_| "point" }
));
named!(pub length(&[u8]) -> UnitRepr, do_parse!(
value: digit >>
unit: length_type >>
(UnitRepr::Length(LengthRepr {
value: str::from_utf8(value).unwrap(),
unit
}))
));
Унифицируем Unit как Enum
#[derive(Debug, Clone, PartialEq)]
pub enum UnitRepr<'a, 'b> {
Length(LengthRepr<'a, 'b>),
Angle(AngleRepr<'a, 'b>),
}
named!(pub unit(&[u8]) -> UnitRepr, alt!(length | angle));
Объявляем основной комбинатор
use nom::alpha;
use std::str;
#[derive(Debug, Clone, PartialEq)]
pub struct TransformFunction<'a, 'b, 'c> {
pub args: Vec<UnitRepr<'a, 'b>>,
pub name: &'c str,
}
named!(fn_name(&[u8]) -> &[u8], ws!(alpha));
named!(args(&[u8]) -> Vec<UnitRepr>,
delimited!(
char!('('),
separated_list!(char!(','), unit),
char!(')')
)
);
named!(pub transform_parse(&[u8]) -> TransformFunction, do_parse!(
name: fn_name >>
args: args >>
(TransformFunction {
name: str::from_utf8(name).unwrap(),
args,
})
));
Тестируем
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn transform_function_parse() {
use properties::parse::{LengthRepr, AngleRepr};
let my_str = "func(10px,10deg)";
let parsed = transform_parse(my_str.as_bytes())
.expect("Can't parse transform").1;
let expected = TransformFunction {
args: vec![
UnitRepr::Length(LengthRepr {
value: "10",
unit: "point",
}),
UnitRepr::Angle(AngleRepr {
value: "10",
angle: "degrees",
}),
],
name: "func",
};
assert_eq!(parsed, expected);
}
}
И Запускаем!
Оно работает!
если нет то тоже сойдет
The End
Вопросы?