Created
September 8, 2021 18:10
-
-
Save MarinPostma/7b60feba2ccb3a090b43e4ccc336ef43 to your computer and use it in GitHub Desktop.
nom meili parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use nom::{ | |
| IResult, | |
| character::complete::{char, multispace0}, | |
| combinator::map, | |
| branch::alt, | |
| sequence::{delimited, tuple, preceded}, | |
| multi::many0, | |
| bytes::complete::{take_while1, tag}, | |
| }; | |
| fn is_key_component(c: char) -> bool { | |
| c.is_alphanumeric() || ['_', '-', '.'].contains(&c) | |
| } | |
| fn parse_key(input: &str) -> IResult<&str, &str> { | |
| let key = |input| take_while1(is_key_component)(input); | |
| alt((key, delimited(char('"'), key, char('"'))))(input) | |
| } | |
| #[derive(Debug)] | |
| enum Expression<'a> { | |
| And(Box<Expression<'a>>, Box<Expression<'a>>), | |
| Or(Box<Expression<'a>>, Box<Expression<'a>>), | |
| Condition(Condition<'a>), | |
| } | |
| #[derive(Debug)] | |
| enum Condition<'a> { | |
| Greater { | |
| key: &'a str, | |
| value: &'a str, | |
| }, | |
| GreaterEq { | |
| key: &'a str, | |
| value: &'a str, | |
| }, | |
| Lower { | |
| key: &'a str, | |
| value: &'a str, | |
| }, | |
| LowerEq { | |
| key: &'a str, | |
| value: &'a str, | |
| }, | |
| Neq { | |
| key: &'a str, | |
| value: &'a str, | |
| }, | |
| Eq { | |
| key: &'a str, | |
| value: &'a str, | |
| }, | |
| Range { | |
| key: &'a str, | |
| from: &'a str, | |
| to: &'a str, | |
| } | |
| } | |
| fn parse_condition(input: &str) -> IResult<&str, Condition> { | |
| let space_surrounded = |val| delimited(multispace0, val, multispace0); | |
| let parse_simple_condition = |input| { | |
| let operator = alt((tag(">"), tag(">="), tag("="), tag("<"), tag("!="), tag("<="))); | |
| let (input, (key, op, value)) = tuple((space_surrounded(parse_key), operator, space_surrounded(parse_key)))(input)?; | |
| let res = match op { | |
| ">" => Condition::Greater { key, value }, | |
| "<" => Condition::Lower { key, value }, | |
| "<=" => Condition::LowerEq { key, value }, | |
| ">=" => Condition::GreaterEq { key, value }, | |
| "=" => Condition::Eq { key, value }, | |
| "!=" => Condition::Neq { key, value }, | |
| _ => unreachable!(), | |
| }; | |
| Ok((input, res)) | |
| }; | |
| let parse_range_condition = |input| { | |
| let (input, (key, from, _, to)) = tuple((space_surrounded(parse_key), space_surrounded(parse_key), tag("TO"), space_surrounded(parse_key)))(input)?; | |
| Ok((input, Condition::Range { key, from, to })) | |
| }; | |
| let (input, condition) = alt((parse_simple_condition, parse_range_condition))(input)?; | |
| Ok((input, condition)) | |
| } | |
| fn parse_or(input: &str) -> IResult<&str, Expression> { | |
| let (input, lhs) = parse_and(input)?; | |
| let (input, ors) = many0(preceded(tag("OR"), parse_and))(input)?; | |
| let expr = ors.into_iter().fold(lhs, |acc, branch| Expression::Or(Box::new(acc), Box::new(branch))); | |
| Ok((input, expr)) | |
| } | |
| fn parse_and(input: &str) -> IResult<&str, Expression> { | |
| let (input, lhs) = map(parse_condition, Expression::Condition)(input)?; | |
| let (input, ors) = many0(preceded(tag("AND"), parse_and))(input)?; | |
| let expr = ors.into_iter().fold(lhs, |acc, branch| Expression::And(Box::new(acc), Box::new(branch))); | |
| Ok((input, expr)) | |
| } | |
| fn parse_expression(input: &str) -> IResult<&str, Expression> { | |
| parse_or(input) | |
| } | |
| fn main() { | |
| let (_input, parsed) = parse_expression("\"hello-mama\" 10 TO blabla OR hello = 19").unwrap(); | |
| println!("parsed: {:?}", parsed); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment