Created
September 8, 2021 18:51
-
-
Save MarinPostma/78454ea2bc8a18b5707ab81248a8f0a7 to your computer and use it in GitHub Desktop.
meili nom parser
This file contains 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, branch::alt, bytes::complete::{take_while1, tag}, character::complete::{char, multispace0}, combinator::map, error::ParseError, multi::many0, sequence::{delimited, tuple, preceded}}; | |
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>>), | |
Not(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 ws<'a, F: 'a, O, E: ParseError<&'a str>>(inner: F) -> impl FnMut(&'a str) -> IResult<&'a str, O, E> | |
where | |
F: Fn(&'a str) -> IResult<&'a str, O, E>, | |
{ | |
delimited( | |
multispace0, | |
inner, | |
multispace0 | |
) | |
} | |
fn parse_condition(input: &str) -> IResult<&str, Condition> { | |
let parse_simple_condition = |input| { | |
let operator = alt((tag(">"), tag(">="), tag("="), tag("<"), tag("!="), tag("<="))); | |
let (input, (key, op, value)) = tuple((ws(parse_key), operator, ws(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((ws(parse_key), ws(parse_key), tag("TO"), ws(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) = parse_not(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_not(input: &str) -> IResult<&str, Expression> { | |
alt(( | |
map(preceded(ws(char('!')), parse_condition_expression), |e| Expression::Not(Box::new(e))), | |
parse_condition_expression, | |
))(input) | |
} | |
fn parse_condition_expression(input: &str) -> IResult<&str, Expression> { | |
alt(( | |
delimited(ws(char('(')), parse_expression, ws(char(')'))), | |
map(parse_condition, Expression::Condition) | |
))(input) | |
} | |
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 AND lala = 12").unwrap(); | |
println!("parsed: {:?}", parsed); | |
println!("input: {}", input); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment