Parenthesis and implicit AND support
This commit is contained in:
parent
83b5431994
commit
9a14114e50
1 changed files with 98 additions and 10 deletions
|
@ -64,12 +64,12 @@ pub enum Expr {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_parser() -> impl Parser<char, Expr, Error = Simple<char>> {
|
pub fn make_parser() -> impl Parser<char, Expr, Error = Simple<char>> {
|
||||||
let combined = recursive(|expr| {
|
recursive(|expr| {
|
||||||
let quoted_str = just('"')
|
let quoted_str = just('"')
|
||||||
.ignore_then(none_of('"').repeated().collect::<String>())
|
.ignore_then(none_of('"').repeated().collect::<String>())
|
||||||
.then_ignore(just('"'));
|
.then_ignore(just('"'));
|
||||||
|
|
||||||
let raw_str = filter(|c: &char| !c.is_whitespace() && *c != '"')
|
let raw_str = filter(|c: &char| !c.is_whitespace() && *c != '"' && *c != '(' && *c != ')')
|
||||||
.repeated()
|
.repeated()
|
||||||
.at_least(1)
|
.at_least(1)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
@ -130,7 +130,7 @@ pub fn make_parser() -> impl Parser<char, Expr, Error = Simple<char>> {
|
||||||
let fuzzy = literal.map(Expr::Fuzzy);
|
let fuzzy = literal.map(Expr::Fuzzy);
|
||||||
|
|
||||||
let filter = text_cmp.or(number_cmp).or(fuzzy);
|
let filter = text_cmp.or(number_cmp).or(fuzzy);
|
||||||
let atom = filter;
|
let atom = choice((filter, expr.delimited_by(just('('), just(')'))));
|
||||||
|
|
||||||
let bool_op = choice((just("&&").to(BoolOp::And), just("||").to(BoolOp::Or))).padded();
|
let bool_op = choice((just("&&").to(BoolOp::And), just("||").to(BoolOp::Or))).padded();
|
||||||
|
|
||||||
|
@ -139,14 +139,14 @@ pub fn make_parser() -> impl Parser<char, Expr, Error = Simple<char>> {
|
||||||
.then(bool_op.then(atom).repeated())
|
.then(bool_op.then(atom).repeated())
|
||||||
.foldl(|a, (b, c)| Expr::Combined(Box::new(a), b, Box::new(c)));
|
.foldl(|a, (b, c)| Expr::Combined(Box::new(a), b, Box::new(c)));
|
||||||
|
|
||||||
combined
|
let implicit_and = combined
|
||||||
});
|
.clone()
|
||||||
|
.then(whitespace().ignore_then(combined).repeated())
|
||||||
|
.foldl(|a: Expr, b: Expr| Expr::Combined(Box::new(a), BoolOp::And, Box::new(b)));
|
||||||
|
|
||||||
combined
|
implicit_and
|
||||||
.clone()
|
})
|
||||||
.then(whitespace().ignore_then(combined).repeated())
|
.then_ignore(end())
|
||||||
.foldl(|a: Expr, b: Expr| Expr::Combined(Box::new(a), BoolOp::And, Box::new(b)))
|
|
||||||
.then_ignore(end())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -162,6 +162,36 @@ fn can_parse_fuzzy_query() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_repeat_fuzzy_queries() {
|
||||||
|
let parser = make_parser();
|
||||||
|
assert_eq!(
|
||||||
|
parser.parse(r#"rhapsody "of victory""#).unwrap(),
|
||||||
|
Expr::Combined(
|
||||||
|
Box::new(Expr::Fuzzy(Literal::Text("rhapsody".to_owned()))),
|
||||||
|
BoolOp::And,
|
||||||
|
Box::new(Expr::Fuzzy(Literal::Text("of victory".to_owned()))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_mix_fuzzy_and_structured() {
|
||||||
|
let parser = make_parser();
|
||||||
|
assert_eq!(
|
||||||
|
parser.parse(r#"rhapsody album % dragonflame"#).unwrap(),
|
||||||
|
Expr::Combined(
|
||||||
|
Box::new(Expr::Fuzzy(Literal::Text("rhapsody".to_owned()))),
|
||||||
|
BoolOp::And,
|
||||||
|
Box::new(Expr::TextCmp(
|
||||||
|
TextField::Album,
|
||||||
|
TextOp::Like,
|
||||||
|
"dragonflame".to_owned()
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_parse_text_fields() {
|
fn can_parse_text_fields() {
|
||||||
let parser = make_parser();
|
let parser = make_parser();
|
||||||
|
@ -371,3 +401,61 @@ fn boolean_operators_share_precedence() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_use_parenthesis_for_precedence() {
|
||||||
|
let parser = make_parser();
|
||||||
|
assert_eq!(
|
||||||
|
parser
|
||||||
|
.parse(r#"album % lands || (album % tales && title % sword)"#)
|
||||||
|
.unwrap(),
|
||||||
|
Expr::Combined(
|
||||||
|
Box::new(Expr::TextCmp(
|
||||||
|
TextField::Album,
|
||||||
|
TextOp::Like,
|
||||||
|
"lands".to_owned()
|
||||||
|
)),
|
||||||
|
BoolOp::Or,
|
||||||
|
Box::new(Expr::Combined(
|
||||||
|
Box::new(Expr::TextCmp(
|
||||||
|
TextField::Album,
|
||||||
|
TextOp::Like,
|
||||||
|
"tales".to_owned()
|
||||||
|
)),
|
||||||
|
BoolOp::And,
|
||||||
|
Box::new(Expr::TextCmp(
|
||||||
|
TextField::Title,
|
||||||
|
TextOp::Like,
|
||||||
|
"sword".to_owned()
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parser
|
||||||
|
.parse(r#"(album % lands || album % tales) && title % "sword""#)
|
||||||
|
.unwrap(),
|
||||||
|
Expr::Combined(
|
||||||
|
Box::new(Expr::Combined(
|
||||||
|
Box::new(Expr::TextCmp(
|
||||||
|
TextField::Album,
|
||||||
|
TextOp::Like,
|
||||||
|
"lands".to_owned()
|
||||||
|
)),
|
||||||
|
BoolOp::Or,
|
||||||
|
Box::new(Expr::TextCmp(
|
||||||
|
TextField::Album,
|
||||||
|
TextOp::Like,
|
||||||
|
"tales".to_owned()
|
||||||
|
))
|
||||||
|
)),
|
||||||
|
BoolOp::And,
|
||||||
|
Box::new(Expr::TextCmp(
|
||||||
|
TextField::Title,
|
||||||
|
TextOp::Like,
|
||||||
|
"sword".to_owned()
|
||||||
|
))
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue