| use std::str::FromStr; |
| |
| use nom::{ |
| branch::alt, |
| bytes::complete::{is_a, tag}, |
| character::complete::{char, digit1, space0}, |
| combinator::{map, map_res, opt, recognize}, |
| error::ErrorKind, |
| sequence::{delimited, pair, preceded}, |
| Err, IResult, |
| }; |
| |
| use crate::path::Expression; |
| |
| fn raw_ident(i: &str) -> IResult<&str, String> { |
| map( |
| is_a( |
| "abcdefghijklmnopqrstuvwxyz \ |
| ABCDEFGHIJKLMNOPQRSTUVWXYZ \ |
| 0123456789 \ |
| _-", |
| ), |
| ToString::to_string, |
| )(i) |
| } |
| |
| fn integer(i: &str) -> IResult<&str, isize> { |
| map_res( |
| delimited(space0, recognize(pair(opt(tag("-")), digit1)), space0), |
| FromStr::from_str, |
| )(i) |
| } |
| |
| fn ident(i: &str) -> IResult<&str, Expression> { |
| map(raw_ident, Expression::Identifier)(i) |
| } |
| |
| fn postfix<'a>(expr: Expression) -> impl FnMut(&'a str) -> IResult<&'a str, Expression> { |
| let e2 = expr.clone(); |
| let child = map(preceded(tag("."), raw_ident), move |id| { |
| Expression::Child(Box::new(expr.clone()), id) |
| }); |
| |
| let subscript = map(delimited(char('['), integer, char(']')), move |num| { |
| Expression::Subscript(Box::new(e2.clone()), num) |
| }); |
| |
| alt((child, subscript)) |
| } |
| |
| pub fn from_str(input: &str) -> Result<Expression, ErrorKind> { |
| match ident(input) { |
| Ok((mut rem, mut expr)) => { |
| while !rem.is_empty() { |
| match postfix(expr)(rem) { |
| Ok((rem_, expr_)) => { |
| rem = rem_; |
| expr = expr_; |
| } |
| |
| // Forward Incomplete and Error |
| result => { |
| return result.map(|(_, o)| o).map_err(to_error_kind); |
| } |
| } |
| } |
| |
| Ok(expr) |
| } |
| |
| // Forward Incomplete and Error |
| result => result.map(|(_, o)| o).map_err(to_error_kind), |
| } |
| } |
| |
| pub fn to_error_kind(e: Err<nom::error::Error<&str>>) -> ErrorKind { |
| match e { |
| Err::Incomplete(_) => ErrorKind::Complete, |
| Err::Failure(e) | Err::Error(e) => e.code, |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::Expression::*; |
| use super::*; |
| |
| #[test] |
| fn test_id() { |
| let parsed: Expression = from_str("abcd").unwrap(); |
| assert_eq!(parsed, Identifier("abcd".into())); |
| } |
| |
| #[test] |
| fn test_id_dash() { |
| let parsed: Expression = from_str("abcd-efgh").unwrap(); |
| assert_eq!(parsed, Identifier("abcd-efgh".into())); |
| } |
| |
| #[test] |
| fn test_child() { |
| let parsed: Expression = from_str("abcd.efgh").unwrap(); |
| let expected = Child(Box::new(Identifier("abcd".into())), "efgh".into()); |
| |
| assert_eq!(parsed, expected); |
| |
| let parsed: Expression = from_str("abcd.efgh.ijkl").unwrap(); |
| let expected = Child( |
| Box::new(Child(Box::new(Identifier("abcd".into())), "efgh".into())), |
| "ijkl".into(), |
| ); |
| |
| assert_eq!(parsed, expected); |
| } |
| |
| #[test] |
| fn test_subscript() { |
| let parsed: Expression = from_str("abcd[12]").unwrap(); |
| let expected = Subscript(Box::new(Identifier("abcd".into())), 12); |
| |
| assert_eq!(parsed, expected); |
| } |
| |
| #[test] |
| fn test_subscript_neg() { |
| let parsed: Expression = from_str("abcd[-1]").unwrap(); |
| let expected = Subscript(Box::new(Identifier("abcd".into())), -1); |
| |
| assert_eq!(parsed, expected); |
| } |
| } |