Skip to content

Instantly share code, notes, and snippets.

@MarinPostma
Created September 8, 2021 18:10
Show Gist options
  • Select an option

  • Save MarinPostma/7b60feba2ccb3a090b43e4ccc336ef43 to your computer and use it in GitHub Desktop.

Select an option

Save MarinPostma/7b60feba2ccb3a090b43e4ccc336ef43 to your computer and use it in GitHub Desktop.
nom meili parser
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