update search/parser.rs

This commit is contained in:
llama 2025-06-19 23:12:55 +08:00
parent 27182c32e6
commit ef0686b679
No known key found for this signature in database
GPG key ID: 0B7543854B9413C3

View file

@ -19,6 +19,7 @@ use nom::error::ErrorKind as NomErrorKind;
use nom::multi::many0; use nom::multi::many0;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::separated_pair; use nom::sequence::separated_pair;
use nom::Parser;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
@ -202,18 +203,19 @@ fn group_inner(input: &str) -> IResult<Vec<Node>> {
} }
fn whitespace0(s: &str) -> IResult<Vec<char>> { fn whitespace0(s: &str) -> IResult<Vec<char>> {
many0(one_of(" \u{3000}"))(s) many0(one_of(" \u{3000}")).parse(s)
} }
/// Optional leading space, then a (negated) group or text /// Optional leading space, then a (negated) group or text
fn node(s: &str) -> IResult<Node> { fn node(s: &str) -> IResult<Node> {
preceded(whitespace0, alt((negated_node, group, text)))(s) preceded(whitespace0, alt((negated_node, group, text))).parse(s)
} }
fn negated_node(s: &str) -> IResult<Node> { fn negated_node(s: &str) -> IResult<Node> {
map(preceded(char('-'), alt((group, text))), |node| { map(preceded(char('-'), alt((group, text))), |node| {
Node::Not(Box::new(node)) Node::Not(Box::new(node))
})(s) })
.parse(s)
} }
/// One or more nodes surrounded by brackets, eg (one OR two) /// One or more nodes surrounded by brackets, eg (one OR two)
@ -233,7 +235,7 @@ fn group(s: &str) -> IResult<Node> {
/// Either quoted or unquoted text /// Either quoted or unquoted text
fn text(s: &str) -> IResult<Node> { fn text(s: &str) -> IResult<Node> {
alt((quoted_term, partially_quoted_term, unquoted_term))(s) alt((quoted_term, partially_quoted_term, unquoted_term)).parse(s)
} }
/// Quoted text, including the outer double quotes. /// Quoted text, including the outer double quotes.
@ -248,7 +250,8 @@ fn partially_quoted_term(s: &str) -> IResult<Node> {
escaped(is_not("\"(): \u{3000}\\"), '\\', none_of(" \u{3000}")), escaped(is_not("\"(): \u{3000}\\"), '\\', none_of(" \u{3000}")),
char(':'), char(':'),
quoted_term_str, quoted_term_str,
)(s)?; )
.parse(s)?;
Ok(( Ok((
remaining, remaining,
Node::Search(search_node_for_text_with_argument(key, val)?), Node::Search(search_node_for_text_with_argument(key, val)?),
@ -296,7 +299,7 @@ fn unquoted_term(s: &str) -> IResult<Node> {
fn quoted_term_str(s: &str) -> IResult<&str> { fn quoted_term_str(s: &str) -> IResult<&str> {
let (opened, _) = char('"')(s)?; let (opened, _) = char('"')(s)?;
if let Ok((tail, inner)) = if let Ok((tail, inner)) =
escaped::<_, ParseError, _, _, _, _>(is_not(r#""\"#), '\\', anychar)(opened) escaped::<_, ParseError, _, _>(is_not(r#""\"#), '\\', anychar).parse(opened)
{ {
if let Ok((remaining, _)) = char::<_, ParseError>('"')(tail) { if let Ok((remaining, _)) = char::<_, ParseError>('"')(tail) {
Ok((remaining, inner)) Ok((remaining, inner))
@ -321,7 +324,8 @@ fn search_node_for_text(s: &str) -> ParseResult<SearchNode> {
// leading : is only possible error for well-formed input // leading : is only possible error for well-formed input
let (tail, head) = verify(escaped(is_not(r":\"), '\\', anychar), |t: &str| { let (tail, head) = verify(escaped(is_not(r":\"), '\\', anychar), |t: &str| {
!t.is_empty() !t.is_empty()
})(s) })
.parse(s)
.map_err(|_: nom::Err<ParseError>| parse_failure(s, FailKind::MissingKey))?; .map_err(|_: nom::Err<ParseError>| parse_failure(s, FailKind::MissingKey))?;
if tail.is_empty() { if tail.is_empty() {
Ok(SearchNode::UnqualifiedText(unescape(head)?)) Ok(SearchNode::UnqualifiedText(unescape(head)?))
@ -407,7 +411,7 @@ fn parse_resched(s: &str) -> ParseResult<SearchNode> {
/// eg prop:ivl>3, prop:ease!=2.5 /// eg prop:ivl>3, prop:ease!=2.5
fn parse_prop(prop_clause: &str) -> ParseResult<SearchNode> { fn parse_prop(prop_clause: &str) -> ParseResult<SearchNode> {
let (tail, prop) = alt::<_, _, ParseError, _>(( let (tail, prop) = alt((
tag("ivl"), tag("ivl"),
tag("due"), tag("due"),
tag("reps"), tag("reps"),
@ -421,8 +425,9 @@ fn parse_prop(prop_clause: &str) -> ParseResult<SearchNode> {
tag("r"), tag("r"),
recognize(preceded(tag("cdn:"), alphanumeric1)), recognize(preceded(tag("cdn:"), alphanumeric1)),
recognize(preceded(tag("cds:"), alphanumeric1)), recognize(preceded(tag("cds:"), alphanumeric1)),
))(prop_clause) ))
.map_err(|_| { .parse(prop_clause)
.map_err(|_: nom::Err<ParseError>| {
parse_failure( parse_failure(
prop_clause, prop_clause,
FailKind::InvalidPropProperty { FailKind::InvalidPropProperty {
@ -431,15 +436,16 @@ fn parse_prop(prop_clause: &str) -> ParseResult<SearchNode> {
) )
})?; })?;
let (num, operator) = alt::<_, _, ParseError, _>(( let (num, operator) = alt((
tag("<="), tag("<="),
tag(">="), tag(">="),
tag("!="), tag("!="),
tag("="), tag("="),
tag("<"), tag("<"),
tag(">"), tag(">"),
))(tail) ))
.map_err(|_| { .parse(tail)
.map_err(|_: nom::Err<ParseError>| {
parse_failure( parse_failure(
prop_clause, prop_clause,
FailKind::InvalidPropOperator { FailKind::InvalidPropOperator {