diff --git a/Cargo.lock b/Cargo.lock index 65483734670..01e7642c629 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,7 +138,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -149,7 +149,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -319,12 +319,6 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" -[[package]] -name = "bytecount" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" - [[package]] name = "byteorder" version = "1.5.0" @@ -482,7 +476,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -671,7 +665,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -979,7 +973,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1559,7 +1553,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1641,9 +1635,6 @@ dependencies = [ "miette", "nextest-metadata", "nextest-workspace-hack", - "nom", - "nom-tracable", - "nom_locate", "proptest", "recursion", "regex", @@ -1652,6 +1643,7 @@ dependencies = [ "test-strategy", "thiserror", "twox-hash", + "winnow", ] [[package]] @@ -1781,7 +1773,7 @@ dependencies = [ "serde", "serde_json", "similar", - "syn 2.0.48", + "syn", "tokio", "twox-hash", "uuid", @@ -1811,38 +1803,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nom-tracable" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a39d3ec4e5bc9816ca540bd6b1e4885c0275536eb3293d317d984bb17f9a294" -dependencies = [ - "nom", - "nom-tracable-macros", - "nom_locate", -] - -[[package]] -name = "nom-tracable-macros" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9c68f5316254dae193b3ce083f6caf19ae1a58471e6585e89f0796b9e5bdf4a" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "nom_locate" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" -dependencies = [ - "bytecount", - "memchr", - "nom", -] - [[package]] name = "num-traits" version = "0.2.17" @@ -1907,7 +1867,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1995,7 +1955,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2103,7 +2063,7 @@ dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2521,7 +2481,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2747,7 +2707,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.48", + "syn", ] [[package]] @@ -2758,7 +2718,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2795,17 +2755,6 @@ dependencies = [ "is-terminal", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.48" @@ -2927,7 +2876,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2938,7 +2887,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", "test-case-core", ] @@ -2951,7 +2900,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta", - "syn 2.0.48", + "syn", ] [[package]] @@ -2982,7 +2931,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -3047,7 +2996,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -3217,7 +3166,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -3434,7 +3383,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-shared", ] @@ -3468,7 +3417,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3751,9 +3700,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.28" +version = "0.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" dependencies = [ "memchr", ] @@ -3817,7 +3766,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] diff --git a/nextest-filtering/Cargo.toml b/nextest-filtering/Cargo.toml index 43a8b7454b3..9fc6294e184 100644 --- a/nextest-filtering/Cargo.toml +++ b/nextest-filtering/Cargo.toml @@ -29,9 +29,6 @@ internal-testing = ["dep:proptest", "dep:test-strategy", "dep:twox-hash"] globset.workspace = true guppy = "0.17.4" miette = "5.10.0" -nom = "7.1.3" -nom-tracable = "0.9.1" -nom_locate = "4.2.0" recursion = "0.5.2" regex = "1.10.2" regex-syntax = "0.8.2" @@ -41,6 +38,7 @@ proptest = { version = "1.4.0", optional = true } test-strategy = { version = "0.3.1", optional = true } twox-hash = { version = "1.6.3", optional = true } nextest-workspace-hack.workspace = true +winnow = "0.5.34" [dev-dependencies] clap = { version = "4.4.18", features = ["derive"] } diff --git a/nextest-filtering/src/errors.rs b/nextest-filtering/src/errors.rs index 5dcf0ceb655..d02e33a5c41 100644 --- a/nextest-filtering/src/errors.rs +++ b/nextest-filtering/src/errors.rs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use miette::{Diagnostic, SourceSpan}; -use nom_tracable::TracableInfo; -use std::cell::RefCell; use thiserror::Error; /// A set of errors that occurred while parsing a filter expression. @@ -151,37 +149,19 @@ pub enum GlobConstructError { RegexError(String), } -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct State<'a> { // A `RefCell` is required here because the state must implement `Clone` to work with nom. - errors: &'a RefCell>, - tracable_info: TracableInfo, + errors: &'a mut Vec, } impl<'a> State<'a> { - pub fn new(errors: &'a RefCell>) -> Self { - let tracable_info = nom_tracable::TracableInfo::new() - .forward(true) - .backward(true); - Self { - errors, - tracable_info, - } - } - - pub fn report_error(&self, error: ParseSingleError) { - self.errors.borrow_mut().push(error); - } -} - -impl<'a> nom_tracable::HasTracableInfo for State<'a> { - fn get_tracable_info(&self) -> TracableInfo { - self.tracable_info.get_tracable_info() + pub fn new(errors: &'a mut Vec) -> Self { + Self { errors } } - fn set_tracable_info(mut self, info: TracableInfo) -> Self { - self.tracable_info = self.tracable_info.set_tracable_info(info); - self + pub fn report_error(&mut self, error: ParseSingleError) { + self.errors.push(error); } } diff --git a/nextest-filtering/src/expression.rs b/nextest-filtering/src/expression.rs index b70959c29b7..16bf930a760 100644 --- a/nextest-filtering/src/expression.rs +++ b/nextest-filtering/src/expression.rs @@ -2,10 +2,10 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ - errors::{FilterExpressionParseErrors, ParseSingleError, State}, + errors::{FilterExpressionParseErrors, ParseSingleError}, parsing::{ - parse, DisplayParsedRegex, DisplayParsedString, ExprResult, GenericGlob, ParsedExpr, - SetDef, Span, + new_span, parse, DisplayParsedRegex, DisplayParsedString, ExprResult, GenericGlob, + ParsedExpr, SetDef, }, }; use guppy::{ @@ -15,7 +15,7 @@ use guppy::{ use miette::SourceSpan; use nextest_metadata::{RustBinaryId, RustTestBinaryKind}; use recursion::{Collapsible, CollapsibleExt, MappableFrame, PartiallyApplied}; -use std::{cell::RefCell, collections::HashSet, fmt}; +use std::{collections::HashSet, fmt}; /// Matcher for name /// @@ -227,11 +227,9 @@ impl FilteringSet { impl FilteringExpr { /// Parse a filtering expression pub fn parse(input: String, graph: &PackageGraph) -> Result { - let errors = RefCell::new(Vec::new()); - match parse(Span::new_extra(&input, State::new(&errors))) { + let mut errors = Vec::new(); + match parse(new_span(&input, &mut errors)) { Ok(parsed_expr) => { - let errors = errors.into_inner(); - if !errors.is_empty() { return Err(FilterExpressionParseErrors::new(input.clone(), errors)); } diff --git a/nextest-filtering/src/parsing.rs b/nextest-filtering/src/parsing.rs index f6613f7f22d..852fa2949c0 100644 --- a/nextest-filtering/src/parsing.rs +++ b/nextest-filtering/src/parsing.rs @@ -11,20 +11,19 @@ //! - on error: //! - consume as much input as it makes sense so that we can try to resume parsing //! - return an error/none variant of the expected result type -//! - push an error in the parsing state (in span.extra) +//! - push an error in the parsing state (in span.state) use guppy::graph::cargo::BuildPlatform; use miette::SourceSpan; -use nom::{ - branch::alt, - bytes::complete::{is_not, tag, take_till}, - character::complete::{char, line_ending}, - combinator::{eof, map, peek, recognize, value, verify}, - multi::{fold_many0, many0}, - sequence::{delimited, pair, preceded, terminated}, +use std::fmt; +use winnow::{ + ascii::line_ending, + combinator::{alt, delimited, eof, fold_repeat, peek, preceded, repeat, terminated}, + stream::{Location, SliceLen, Stream}, + token::{tag, take_till}, + trace::trace, + Parser, }; -use nom_tracable::tracable_parser; -use std::{cell::RefCell, fmt}; mod glob; mod unicode_string; @@ -32,12 +31,20 @@ use crate::{errors::*, NameMatcher}; pub(crate) use glob::GenericGlob; pub(crate) use unicode_string::DisplayParsedString; -pub(crate) type Span<'a> = nom_locate::LocatedSpan<&'a str, State<'a>>; -type IResult<'a, T> = nom::IResult, T>; +pub(crate) type Span<'a> = winnow::Stateful, State<'a>>; +type Error = (); +type PResult = winnow::PResult; impl<'a> ToSourceSpan for Span<'a> { fn to_span(&self) -> SourceSpan { - (self.location_offset(), self.fragment().len()).into() + (self.location(), self.slice_len()).into() + } +} + +pub(crate) fn new_span<'a>(input: &'a str, errors: &'a mut Vec) -> Span<'a> { + Span { + input: winnow::Located::new(input), + state: State::new(errors), } } @@ -106,10 +113,11 @@ pub enum ParsedExpr { impl ParsedExpr { pub fn parse(input: &str) -> Result> { - let errors = RefCell::new(Vec::new()); - match parse(Span::new_extra(input, State::new(&errors))).unwrap() { + let mut errors = Vec::new(); + let span = new_span(input, &mut errors); + match parse(span).unwrap() { ExprResult::Valid(expr) => Ok(expr), - ExprResult::Error => Err(errors.into_inner()), + ExprResult::Error => Err(errors), } } @@ -223,16 +231,15 @@ fn expect_inner<'a, F, T>( mut parser: F, make_err: fn(SourceSpan) -> ParseSingleError, limit: SpanLength, -) -> impl FnMut(Span<'a>) -> IResult<'a, Option> +) -> impl Parser, Option, Error> where - F: FnMut(Span<'a>) -> IResult<'_, T>, + F: Parser, T, Error>, { - move |input| match parser(input) { - Ok((remaining, out)) => Ok((remaining, Some(out))), - Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => { - let nom::error::Error { input, .. } = err; - let fragment_start = input.location_offset(); - let fragment_length = input.fragment().len(); + move |input: &mut _| match parser.parse_next(input) { + Ok(out) => Ok(Some(out)), + Err(winnow::error::ErrMode::Backtrack(_)) | Err(winnow::error::ErrMode::Cut(_)) => { + let fragment_start = input.location(); + let fragment_length = input.slice_len(); let span = match limit { SpanLength::Unknown => (fragment_start, fragment_length).into(), SpanLength::Exact(x) => (fragment_start, x.min(fragment_length)).into(), @@ -248,8 +255,8 @@ where } }; let err = make_err(span); - input.extra.report_error(err); - Ok((input, None)) + input.state.report_error(err); + Ok(None) } Err(err) => Err(err), } @@ -258,9 +265,9 @@ where fn expect<'a, F, T>( parser: F, make_err: fn(SourceSpan) -> ParseSingleError, -) -> impl FnMut(Span<'a>) -> IResult<'a, Option> +) -> impl Parser, Option, Error> where - F: FnMut(Span<'a>) -> IResult<'_, T>, + F: Parser, T, Error>, { expect_inner(parser, make_err, SpanLength::Unknown) } @@ -269,9 +276,9 @@ fn expect_n<'a, F, T>( parser: F, make_err: fn(SourceSpan) -> ParseSingleError, limit: SpanLength, -) -> impl FnMut(Span<'a>) -> IResult<'a, Option> +) -> impl Parser, Option, Error> where - F: FnMut(Span<'a>) -> IResult<'_, T>, + F: Parser, T, Error>, { expect_inner(parser, make_err, limit) } @@ -279,44 +286,44 @@ where fn expect_char<'a>( c: char, make_err: fn(SourceSpan) -> ParseSingleError, -) -> impl FnMut(Span<'a>) -> IResult<'a, Option> { - expect_inner(ws(char(c)), make_err, SpanLength::Exact(0)) +) -> impl Parser, Option, Error> { + expect_inner(ws(c), make_err, SpanLength::Exact(0)) } -fn silent_expect<'a, F, T>(mut parser: F) -> impl FnMut(Span<'a>) -> IResult<'_, Option> +fn silent_expect<'a, F, T>(mut parser: F) -> impl Parser, Option, Error> where - F: FnMut(Span<'a>) -> IResult<'_, T>, + F: Parser, T, Error>, { - move |input| match parser(input) { - Ok((remaining, out)) => Ok((remaining, Some(out))), - Err(nom::Err::Error(err)) | Err(nom::Err::Failure(err)) => { - let nom::error::Error { input, .. } = err; - Ok((input, None)) - } + move |input: &mut _| match parser.parse_next(input) { + Ok(out) => Ok(Some(out)), + Err(winnow::error::ErrMode::Backtrack(_)) | Err(winnow::error::ErrMode::Cut(_)) => Ok(None), Err(err) => Err(err), } } -fn ws<'a, T, P: FnMut(Span<'a>) -> IResult<'a, T>>( - mut inner: P, -) -> impl FnMut(Span<'a>) -> IResult<'a, T> { - move |input| { - let (i, _) = many0(alt(( - // Match individual space characters. - value((), char(' ')), - // Match CRLF and LF line endings. This allows filters to be specified as multiline TOML - // strings. - value((), line_ending), - )))(input.clone())?; - match inner(i) { +fn ws<'a, T, P: Parser, T, Error>>(mut inner: P) -> impl Parser, T, Error> { + move |input: &mut Span<'a>| { + let start = input.checkpoint(); + repeat( + 0.., + alt(( + // Match individual space characters. + ' '.void(), + // Match CRLF and LF line endings. This allows filters to be specified as multiline TOML + // strings. + line_ending.void(), + )), + ) + .parse_next(input)?; + match inner.parse_next(input) { Ok(res) => Ok(res), - Err(nom::Err::Error(err)) => { - let nom::error::Error { code, .. } = err; - Err(nom::Err::Error(nom::error::Error { input, code })) + Err(winnow::error::ErrMode::Backtrack(err)) => { + input.reset(start); + Err(winnow::error::ErrMode::Backtrack(err)) } - Err(nom::Err::Failure(err)) => { - let nom::error::Error { code, .. } = err; - Err(nom::Err::Failure(nom::error::Error { input, code })) + Err(winnow::error::ErrMode::Cut(err)) => { + input.reset(start); + Err(winnow::error::ErrMode::Cut(err)) } Err(err) => Err(err), } @@ -324,77 +331,85 @@ fn ws<'a, T, P: FnMut(Span<'a>) -> IResult<'a, T>>( } // This parse will never fail -#[tracable_parser] -fn parse_matcher_text(input: Span<'_>) -> IResult<'_, Option> { - let (i, res) = match expect( - unicode_string::parse_string, - ParseSingleError::InvalidString, - )(input.clone()) - { - Ok((i, res)) => (i, res.flatten()), - Err(_) => unreachable!(), - }; +fn parse_matcher_text<'i>(input: &mut Span<'i>) -> PResult> { + trace("parse_matcher_text", |input: &mut Span<'i>| { + let res = match expect( + unicode_string::parse_string, + ParseSingleError::InvalidString, + ) + .parse_next(input) + { + Ok(res) => res.flatten(), + Err(_) => unreachable!(), + }; - if res.as_ref().map(|s| s.is_empty()).unwrap_or(false) { - let start = i.location_offset(); - i.extra - .report_error(ParseSingleError::InvalidString((start..0).into())); - } + if res.as_ref().map(|s| s.is_empty()).unwrap_or(false) { + let start = input.location(); + input + .state + .report_error(ParseSingleError::InvalidString((start..0).into())); + } - Ok((i, res)) + Ok(res) + }) + .parse_next(input) } -#[tracable_parser] -fn parse_contains_matcher(input: Span<'_>) -> IResult<'_, Option> { - map( - preceded(char('~'), parse_matcher_text), - |res: Option| { +fn parse_contains_matcher(input: &mut Span<'_>) -> PResult> { + trace( + "parse_contains_matcher", + preceded('~', parse_matcher_text).map(|res: Option| { res.map(|value| NameMatcher::Contains { value, implicit: false, }) - }, - )(input) + }), + ) + .parse_next(input) } -#[tracable_parser] -fn parse_equal_matcher(input: Span<'_>) -> IResult<'_, Option> { - ws(map( - preceded(char('='), parse_matcher_text), - |res: Option| { - res.map(|value| NameMatcher::Equal { - value, - implicit: false, - }) - }, - ))(input) +fn parse_equal_matcher(input: &mut Span<'_>) -> PResult> { + trace( + "parse_equal_matcher", + ws( + preceded('=', parse_matcher_text).map(|res: Option| { + res.map(|value| NameMatcher::Equal { + value, + implicit: false, + }) + }), + ), + ) + .parse_next(input) } -#[tracable_parser] -fn parse_regex_inner(input: Span<'_>) -> IResult<'_, String> { - enum Frag<'a> { - Literal(&'a str), - Escape(char), - } +fn parse_regex_inner(input: &mut Span<'_>) -> PResult { + trace("parse_regex_inner", |input: &mut _| { + enum Frag<'a> { + Literal(&'a str), + Escape(char), + } - let parse_escape = map(alt((map(tag(r"\/"), |_| '/'), char('\\'))), Frag::Escape); - let parse_literal = map( - verify(is_not("\\/"), |s: &Span<'_>| !s.fragment().is_empty()), - |s: Span<'_>| Frag::Literal(s.fragment()), - ); - let parse_frag = alt((parse_escape, parse_literal)); + let parse_escape = alt((r"\/".value('/'), '\\')).map(Frag::Escape); + let parse_literal = take_till(1.., ('\\', '/')) + .verify(|s: &str| !s.is_empty()) + .map(|s: &str| Frag::Literal(s)); + let parse_frag = alt((parse_escape, parse_literal)); - let (i, res) = fold_many0(parse_frag, String::new, |mut string, frag| { - match frag { - Frag::Escape(c) => string.push(c), - Frag::Literal(s) => string.push_str(s), - } - string - })(input)?; + let res = fold_repeat(0.., parse_frag, String::new, |mut string, frag| { + match frag { + Frag::Escape(c) => string.push(c), + Frag::Literal(s) => string.push_str(s), + } + string + }) + .parse_next(input)?; - let (i, _) = peek(char('/'))(i)?; + let _ = peek('/').parse_next(input)?; - Ok((i, res)) + Ok(res) + }) + .parse_next(input) } // This should match parse_regex_inner above. @@ -422,98 +437,116 @@ impl fmt::Display for DisplayParsedRegex<'_> { } } -#[tracable_parser] -fn parse_regex(input: Span<'_>) -> IResult<'_, Option> { - let (i, res) = match parse_regex_inner(input.clone()) { - Ok((i, res)) => (i, res), - Err(_) => match take_till::<_, _, nom::error::Error>>(|c| c == ')')(input.clone()) - { - Ok((i, _)) => { - let start = i.location_offset(); - let err = ParseSingleError::ExpectedCloseRegex((start, 0).into()); - i.extra.report_error(err); - return Ok((i, None)); +fn parse_regex<'i>(input: &mut Span<'i>) -> PResult> { + trace("parse_regex", |input: &mut Span<'i>| { + let start = input.checkpoint(); + let res = match parse_regex_inner.parse_next(input) { + Ok(res) => res, + Err(_) => { + input.reset(start); + match take_till::<_, _, Error>(0.., ')').parse_next(input) { + Ok(_) => { + let start = input.location(); + let err = ParseSingleError::ExpectedCloseRegex((start, 0).into()); + input.state.report_error(err); + return Ok(None); + } + Err(_) => unreachable!(), + } + } + }; + match regex::Regex::new(&res).map(NameMatcher::Regex) { + Ok(res) => Ok(Some(res)), + Err(_) => { + let end = input.checkpoint(); + + input.reset(start); + let start = input.location(); + + input.reset(end); + let end = input.location(); + + let err = ParseSingleError::invalid_regex(&res, start, end); + input.state.report_error(err); + Ok(None) } - Err(_) => unreachable!(), - }, - }; - match regex::Regex::new(&res).map(NameMatcher::Regex) { - Ok(res) => Ok((i, Some(res))), - Err(_) => { - let start = input.location_offset(); - let end = i.location_offset(); - let err = ParseSingleError::invalid_regex(&res, start, end); - i.extra.report_error(err); - Ok((i, None)) } - } + }) + .parse_next(input) } -#[tracable_parser] -fn parse_regex_matcher(input: Span<'_>) -> IResult<'_, Option> { - ws(delimited( - char('/'), - parse_regex, - silent_expect(ws(char('/'))), - ))(input) +fn parse_regex_matcher(input: &mut Span<'_>) -> PResult> { + trace( + "parse_regex_matcher", + ws(delimited('/', parse_regex, silent_expect(ws('/')))), + ) + .parse_next(input) } -#[tracable_parser] -fn parse_glob_matcher(input: Span<'_>) -> IResult<'_, Option> { - ws(preceded(char('#'), |input| glob::parse_glob(input, false)))(input) +fn parse_glob_matcher(input: &mut Span<'_>) -> PResult> { + trace( + "parse_glob_matcher", + ws(preceded('#', glob::parse_glob(false))), + ) + .parse_next(input) } // This parse will never fail (because default_matcher won't) -fn set_matcher( +fn set_matcher<'a>( default_matcher: DefaultMatcher, -) -> impl FnMut(Span<'_>) -> IResult<'_, Option> { - move |input: Span<'_>| { - ws(alt(( - parse_regex_matcher, - parse_glob_matcher, - parse_equal_matcher, - parse_contains_matcher, - default_matcher.into_parser(), - )))(input) - } +) -> impl Parser, Option, Error> { + ws(alt(( + parse_regex_matcher, + parse_glob_matcher, + parse_equal_matcher, + parse_contains_matcher, + default_matcher.into_parser(), + ))) } -#[tracable_parser] -fn recover_unexpected_comma(input: Span<'_>) -> IResult<'_, ()> { - match peek(ws(char(',')))(input.clone()) { - Ok((i, _)) => { - let pos = i.location_offset(); - i.extra - .report_error(ParseSingleError::UnexpectedComma((pos..0).into())); - match take_till::<_, _, nom::error::Error>>(|c| c == ')')(i) { - Ok((i, _)) => Ok((i, ())), - Err(_) => unreachable!(), +fn recover_unexpected_comma<'i>(input: &mut Span<'i>) -> PResult<()> { + trace("recover_unexpected_comma", |input: &mut Span<'i>| { + let start = input.checkpoint(); + match peek(ws(',')).parse_next(input) { + Ok(_) => { + let pos = input.location(); + input + .state + .report_error(ParseSingleError::UnexpectedComma((pos..0).into())); + match take_till::<_, _, Error>(0.., ')').parse_next(input) { + Ok(_) => Ok(()), + Err(_) => unreachable!(), + } + } + Err(_) => { + input.reset(start); + Ok(()) } } - Err(_) => Ok((input, ())), - } + }) + .parse_next(input) } -fn nullary_set_def( +fn nullary_set_def<'a>( name: &'static str, make_set: fn() -> SetDef, -) -> impl FnMut(Span<'_>) -> IResult<'_, Option> { - move |i| { - let (i, _) = tag(name)(i)?; - let (i, _) = expect_char('(', ParseSingleError::ExpectedOpenParenthesis)(i)?; - let i = match recognize::<_, _, nom::error::Error>, _>(take_till(|c| c == ')'))(i) - { - Ok((i, res)) => { - if !res.fragment().trim().is_empty() { - let err = ParseSingleError::UnexpectedArgument(res.to_span()); - i.extra.report_error(err); +) -> impl Parser, Option, Error> { + move |i: &mut _| { + let _ = tag(name).parse_next(i)?; + let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?; + let err_loc = i.location(); + match take_till::<_, _, Error>(0.., ')').parse_next(i) { + Ok(res) => { + if !res.trim().is_empty() { + let span = (err_loc, res.len()).into(); + let err = ParseSingleError::UnexpectedArgument(span); + i.state.report_error(err); } - i } Err(_) => unreachable!(), - }; - let (i, _) = expect_char(')', ParseSingleError::ExpectedCloseParenthesis)(i)?; - Ok((i, Some(make_set()))) + } + let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?; + Ok(Some(make_set())) } } @@ -526,55 +559,52 @@ enum DefaultMatcher { } impl DefaultMatcher { - fn into_parser(self) -> impl FnMut(Span<'_>) -> IResult<'_, Option> { - move |input| match self { - Self::Equal => map(parse_matcher_text, |res: Option| { - res.map(NameMatcher::implicit_equal) - })(input), - Self::Contains => map(parse_matcher_text, |res: Option| { - res.map(NameMatcher::implicit_contains) - })(input), - Self::Glob => glob::parse_glob(input, true), + fn into_parser<'a>(self) -> impl Parser, Option, Error> { + move |input: &mut _| match self { + Self::Equal => parse_matcher_text + .map(|res: Option| res.map(NameMatcher::implicit_equal)) + .parse_next(input), + Self::Contains => parse_matcher_text + .map(|res: Option| res.map(NameMatcher::implicit_contains)) + .parse_next(input), + Self::Glob => glob::parse_glob(true).parse_next(input), } } } -fn unary_set_def( +fn unary_set_def<'a>( name: &'static str, default_matcher: DefaultMatcher, make_set: fn(NameMatcher, SourceSpan) -> SetDef, -) -> impl FnMut(Span<'_>) -> IResult<'_, Option> { - move |i| { - let (i, _) = tag(name)(i)?; - let (i, _) = expect_char('(', ParseSingleError::ExpectedOpenParenthesis)(i)?; - let start = i.location_offset(); - let (i, res) = set_matcher(default_matcher)(i)?; - let end = i.location_offset(); - let (i, _) = recover_unexpected_comma(i)?; - let (i, _) = expect_char(')', ParseSingleError::ExpectedCloseParenthesis)(i)?; - Ok(( - i, - res.map(|matcher| make_set(matcher, (start, end - start).into())), - )) +) -> impl Parser, Option, Error> { + move |i: &mut _| { + let _ = tag(name).parse_next(i)?; + let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?; + let start = i.location(); + let res = set_matcher(default_matcher).parse_next(i)?; + let end = i.location(); + recover_unexpected_comma.parse_next(i)?; + let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?; + Ok(res.map(|matcher| make_set(matcher, (start, end - start).into()))) } } -fn platform_def(i: Span<'_>) -> IResult<'_, Option> { - let (i, _) = tag("platform")(i)?; - let (i, _) = expect_char('(', ParseSingleError::ExpectedOpenParenthesis)(i)?; - let start = i.location_offset(); +fn platform_def(i: &mut Span<'_>) -> PResult> { + let _ = "platform".parse_next(i)?; + let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?; + let start = i.location(); // Try parsing the argument as a string for better error messages. - let (i, res) = ws(parse_matcher_text)(i)?; - let end = i.location_offset(); - let (i, _) = recover_unexpected_comma(i)?; - let (i, _) = expect_char(')', ParseSingleError::ExpectedCloseParenthesis)(i)?; + let res = ws(parse_matcher_text).parse_next(i)?; + let end = i.location(); + recover_unexpected_comma.parse_next(i)?; + let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?; // The returned string will include leading and trailing whitespace. let platform = match res.as_deref().map(|res| res.trim()) { Some("host") => Some(BuildPlatform::Host), Some("target") => Some(BuildPlatform::Target), Some(_) => { - i.extra + i.state .report_error(ParseSingleError::InvalidPlatformArgument( (start, end - start).into(), )); @@ -585,59 +615,61 @@ fn platform_def(i: Span<'_>) -> IResult<'_, Option> { None } }; - Ok(( - i, - platform.map(|platform| SetDef::Platform(platform, (start, end - start).into())), - )) + Ok(platform.map(|platform| SetDef::Platform(platform, (start, end - start).into()))) } -#[tracable_parser] -fn parse_set_def(input: Span<'_>) -> IResult<'_, Option> { - ws(alt(( - unary_set_def("package", DefaultMatcher::Glob, SetDef::Package), - unary_set_def("deps", DefaultMatcher::Glob, SetDef::Deps), - unary_set_def("rdeps", DefaultMatcher::Glob, SetDef::Rdeps), - unary_set_def("kind", DefaultMatcher::Equal, SetDef::Kind), - // binary_id must go above binary, otherwise we'll parse the opening predicate wrong. - unary_set_def("binary_id", DefaultMatcher::Glob, SetDef::BinaryId), - unary_set_def("binary", DefaultMatcher::Glob, SetDef::Binary), - unary_set_def("test", DefaultMatcher::Contains, SetDef::Test), - platform_def, - nullary_set_def("all", || SetDef::All), - nullary_set_def("none", || SetDef::None), - )))(input) +fn parse_set_def(input: &mut Span<'_>) -> PResult> { + trace( + "parse_set_def", + ws(alt(( + unary_set_def("package", DefaultMatcher::Glob, SetDef::Package), + unary_set_def("deps", DefaultMatcher::Glob, SetDef::Deps), + unary_set_def("rdeps", DefaultMatcher::Glob, SetDef::Rdeps), + unary_set_def("kind", DefaultMatcher::Equal, SetDef::Kind), + // binary_id must go above binary, otherwise we'll parse the opening predicate wrong. + unary_set_def("binary_id", DefaultMatcher::Glob, SetDef::BinaryId), + unary_set_def("binary", DefaultMatcher::Glob, SetDef::Binary), + unary_set_def("test", DefaultMatcher::Contains, SetDef::Test), + platform_def, + nullary_set_def("all", || SetDef::All), + nullary_set_def("none", || SetDef::None), + ))), + ) + .parse_next(input) } -fn expect_expr<'a, P: FnMut(Span<'a>) -> IResult<'a, ExprResult>>( +fn expect_expr<'a, P: Parser, ExprResult, Error>>( inner: P, -) -> impl FnMut(Span<'a>) -> IResult<'a, ExprResult> { - map(expect(inner, ParseSingleError::ExpectedExpr), |res| { - res.unwrap_or(ExprResult::Error) - }) +) -> impl Parser, ExprResult, Error> { + expect(inner, ParseSingleError::ExpectedExpr).map(|res| res.unwrap_or(ExprResult::Error)) } -#[tracable_parser] -fn parse_parentheses_expr(input: Span<'_>) -> IResult<'_, ExprResult> { - map( +fn parse_parentheses_expr(input: &mut Span<'_>) -> PResult { + trace( + "parse_parentheses_expr", delimited( - char('('), + '(', expect_expr(parse_expr), expect_char(')', ParseSingleError::ExpectedCloseParenthesis), - ), - |expr| expr.parens(), - )(input) + ) + .map(|expr| expr.parens()), + ) + .parse_next(input) } -#[tracable_parser] -fn parse_basic_expr(input: Span<'_>) -> IResult<'_, ExprResult> { - ws(alt(( - map(parse_set_def, |set| { - set.map(|set| ExprResult::Valid(ParsedExpr::Set(set))) - .unwrap_or(ExprResult::Error) - }), - parse_expr_not, - parse_parentheses_expr, - )))(input) +fn parse_basic_expr(input: &mut Span<'_>) -> PResult { + trace( + "parse_basic_expr", + ws(alt(( + parse_set_def.map(|set| { + set.map(|set| ExprResult::Valid(ParsedExpr::Set(set))) + .unwrap_or(ExprResult::Error) + }), + parse_expr_not, + parse_parentheses_expr, + ))), + ) + .parse_next(input) } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -659,18 +691,19 @@ impl fmt::Display for NotOperator { } } -#[tracable_parser] -fn parse_expr_not(input: Span<'_>) -> IResult<'_, ExprResult> { - map( - pair( +fn parse_expr_not(input: &mut Span<'_>) -> PResult { + trace( + "parse_expr_not", + ( alt(( - value(NotOperator::LiteralNot, tag("not ")), - value(NotOperator::Exclamation, tag("!")), + "not ".value(NotOperator::LiteralNot), + '!'.value(NotOperator::Exclamation), )), expect_expr(ws(parse_basic_expr)), - ), - |(op, expr)| expr.negate(op), - )(input) + ) + .map(|(op, expr)| expr.negate(op)), + ) + .parse_next(input) } // --- @@ -696,51 +729,59 @@ impl fmt::Display for OrOperator { } } -#[tracable_parser] -fn parse_expr(input: Span<'_>) -> IResult<'_, ExprResult> { - // "or" binds less tightly than "and", so parse and within or. - let (input, expr) = expect_expr(parse_and_or_difference_expr)(input)?; - - let (input, ops) = fold_many0( - pair(parse_or_operator, expect_expr(parse_and_or_difference_expr)), - Vec::new, - |mut ops, (op, expr)| { - ops.push((op, expr)); - ops - }, - )(input)?; - - let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| { - if let Some(op) = op { - expr_1.combine( - |expr_1, expr_2| ParsedExpr::union(op, expr_1, expr_2), - expr_2, - ) - } else { - ExprResult::Error - } - }); +fn parse_expr(input: &mut Span<'_>) -> PResult { + trace("parse_expr", |input: &mut _| { + // "or" binds less tightly than "and", so parse and within or. + let expr = expect_expr(parse_and_or_difference_expr).parse_next(input)?; + + let ops = fold_repeat( + 0.., + (parse_or_operator, expect_expr(parse_and_or_difference_expr)), + Vec::new, + |mut ops, (op, expr)| { + ops.push((op, expr)); + ops + }, + ) + .parse_next(input)?; + + let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| { + if let Some(op) = op { + expr_1.combine( + |expr_1, expr_2| ParsedExpr::union(op, expr_1, expr_2), + expr_2, + ) + } else { + ExprResult::Error + } + }); - Ok((input, expr)) + Ok(expr) + }) + .parse_next(input) } -#[tracable_parser] -fn parse_or_operator(input: Span<'_>) -> IResult<'_, Option> { - ws(alt(( - // This is not a valid OR operator in this position, but catch it to provide a better - // experience. - map(alt((tag("||"), tag("OR "))), |op: Span<'_>| { - // || is not supported in filter expressions: suggest using | instead. - let start = op.location_offset(); - let length = op.fragment().len(); - let err = ParseSingleError::InvalidOrOperator((start, length).into()); - op.extra.report_error(err); - None - }), - value(Some(OrOperator::LiteralOr), tag("or ")), - value(Some(OrOperator::Pipe), tag("|")), - value(Some(OrOperator::Plus), tag("+")), - )))(input) +fn parse_or_operator<'i>(input: &mut Span<'i>) -> PResult> { + trace( + "parse_or_operator", + ws(alt(( + |input: &mut Span<'i>| { + let start = input.location(); + // This is not a valid OR operator in this position, but catch it to provide a better + // experience. + let op = alt(("||", "OR ")).parse_next(input)?; + // || is not supported in filter expressions: suggest using | instead. + let length = op.len(); + let err = ParseSingleError::InvalidOrOperator((start, length).into()); + input.state.report_error(err); + Ok(None) + }, + "or ".value(Some(OrOperator::LiteralOr)), + '|'.value(Some(OrOperator::Pipe)), + '+'.value(Some(OrOperator::Plus)), + ))), + ) + .parse_next(input) } // --- @@ -787,89 +828,86 @@ enum AndOrDifferenceOperator { Difference(DifferenceOperator), } -#[tracable_parser] -fn parse_and_or_difference_expr(input: Span<'_>) -> IResult<'_, ExprResult> { - let (input, expr) = expect_expr(parse_basic_expr)(input)?; +fn parse_and_or_difference_expr(input: &mut Span<'_>) -> PResult { + trace("parse_and_or_difference_expr", |input: &mut _| { + let expr = expect_expr(parse_basic_expr).parse_next(input)?; - let (input, ops) = fold_many0( - pair( - parse_and_or_difference_operator, - expect_expr(parse_basic_expr), - ), - Vec::new, - |mut ops, (op, expr)| { - ops.push((op, expr)); - ops - }, - )(input)?; - - let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| match op { - Some(AndOrDifferenceOperator::And(op)) => expr_1.combine( - |expr_1, expr_2| ParsedExpr::intersection(op, expr_1, expr_2), - expr_2, - ), - Some(AndOrDifferenceOperator::Difference(op)) => expr_1.combine( - |expr_1, expr_2| ParsedExpr::difference(op, expr_1, expr_2), - expr_2, - ), - None => ExprResult::Error, - }); + let ops = fold_repeat( + 0.., + ( + parse_and_or_difference_operator, + expect_expr(parse_basic_expr), + ), + Vec::new, + |mut ops, (op, expr)| { + ops.push((op, expr)); + ops + }, + ) + .parse_next(input)?; - Ok((input, expr)) + let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| match op { + Some(AndOrDifferenceOperator::And(op)) => expr_1.combine( + |expr_1, expr_2| ParsedExpr::intersection(op, expr_1, expr_2), + expr_2, + ), + Some(AndOrDifferenceOperator::Difference(op)) => expr_1.combine( + |expr_1, expr_2| ParsedExpr::difference(op, expr_1, expr_2), + expr_2, + ), + None => ExprResult::Error, + }); + + Ok(expr) + }) + .parse_next(input) } -#[tracable_parser] -fn parse_and_or_difference_operator( - input: Span<'_>, -) -> IResult<'_, Option> { - ws(alt(( - map(alt((tag("&&"), tag("AND "))), |op: Span<'_>| { - // && is not supported in filter expressions: suggest using & instead. - let start = op.location_offset(); - let length = op.fragment().len(); - let err = ParseSingleError::InvalidAndOperator((start, length).into()); - op.extra.report_error(err); - None - }), - value( - Some(AndOrDifferenceOperator::And(AndOperator::LiteralAnd)), - tag("and "), - ), - value( - Some(AndOrDifferenceOperator::And(AndOperator::Ampersand)), - char('&'), - ), - value( - Some(AndOrDifferenceOperator::Difference( +fn parse_and_or_difference_operator<'i>( + input: &mut Span<'i>, +) -> PResult> { + trace( + "parse_and_or_difference_operator", + ws(alt(( + |input: &mut Span<'i>| { + let start = input.location(); + let op = alt(("&&", "AND ")).parse_next(input)?; + // && is not supported in filter expressions: suggest using & instead. + let length = op.len(); + let err = ParseSingleError::InvalidAndOperator((start, length).into()); + input.state.report_error(err); + Ok(None) + }, + "and ".value(Some(AndOrDifferenceOperator::And(AndOperator::LiteralAnd))), + '&'.value(Some(AndOrDifferenceOperator::And(AndOperator::Ampersand))), + '-'.value(Some(AndOrDifferenceOperator::Difference( DifferenceOperator::Minus, - )), - char('-'), - ), - )))(input) + ))), + ))), + ) + .parse_next(input) } // --- -pub(crate) fn parse(input: Span<'_>) -> Result>>> { +pub(crate) fn parse(input: Span<'_>) -> Result> { let (_, expr) = terminated( parse_expr, expect(ws(eof), ParseSingleError::ExpectedEndOfExpression), - )(input)?; + ) + .parse_peek(input)?; Ok(expr) } #[cfg(test)] mod tests { use super::*; - use std::cell::RefCell; #[track_caller] fn parse_regex(input: &str) -> NameMatcher { - let errors = RefCell::new(Vec::new()); - parse_regex_matcher(Span::new_extra(input, State::new(&errors))) - .unwrap() - .1 - .unwrap() + let mut errors = Vec::new(); + let span = new_span(input, &mut errors); + parse_regex_matcher.parse_peek(span).unwrap().1.unwrap() } #[test] @@ -902,8 +940,10 @@ mod tests { #[track_caller] fn parse_glob(input: &str) -> NameMatcher { - let errors = RefCell::new(Vec::new()); - let matcher = parse_glob_matcher(Span::new_extra(input, State::new(&errors))) + let mut errors = Vec::new(); + let span = new_span(input, &mut errors); + let matcher = parse_glob_matcher + .parse_peek(span) .unwrap_or_else(|error| { panic!("for input {input}, parse_glob_matcher returned an error: {error}") }) @@ -914,7 +954,7 @@ mod tests { (reported errors: {errors:?})" ) }); - if errors.borrow().len() > 0 { + if !errors.is_empty() { panic!("for input {input}, parse_glob_matcher reported errors: {errors:?}"); } @@ -953,11 +993,9 @@ mod tests { #[track_caller] fn parse_set(input: &str) -> SetDef { - let errors = RefCell::new(Vec::new()); - parse_set_def(Span::new_extra(input, State::new(&errors))) - .unwrap() - .1 - .unwrap() + let mut errors = Vec::new(); + let span = new_span(input, &mut errors); + parse_set_def.parse_peek(span).unwrap().1.unwrap() } macro_rules! assert_set_def { @@ -1373,28 +1411,30 @@ mod tests { // string parsing is compatible with possible future syntax fn parse_future_syntax( - input: Span<'_>, - ) -> IResult<'_, (Option, Option)> { - let (i, _) = tag("something")(input)?; - let (i, _) = char('(')(i)?; - let (i, n1) = set_matcher(DefaultMatcher::Contains)(i)?; - let (i, _) = ws(char(','))(i)?; - let (i, n2) = set_matcher(DefaultMatcher::Contains)(i)?; - let (i, _) = char(')')(i)?; - Ok((i, (n1, n2))) + input: &mut Span<'_>, + ) -> PResult<(Option, Option)> { + let _ = "something".parse_next(input)?; + let _ = '('.parse_next(input)?; + let n1 = set_matcher(DefaultMatcher::Contains).parse_next(input)?; + let _ = ws(',').parse_next(input)?; + let n2 = set_matcher(DefaultMatcher::Contains).parse_next(input)?; + let _ = ')'.parse_next(input)?; + Ok((n1, n2)) } - let errors = RefCell::new(Vec::new()); - if parse_future_syntax(Span::new_extra("something(aa, bb)", State::new(&errors))).is_err() { + let mut errors = Vec::new(); + let mut span = new_span("something(aa, bb)", &mut errors); + if parse_future_syntax.parse_next(&mut span).is_err() { panic!("Failed to parse comma separated matchers"); } } #[track_caller] fn parse_err(input: &str) -> Vec { - let errors = RefCell::new(Vec::new()); - super::parse(Span::new_extra(input, State::new(&errors))).unwrap(); - errors.into_inner() + let mut errors = Vec::new(); + let span = new_span(input, &mut errors); + super::parse(span).unwrap(); + errors } macro_rules! assert_error { diff --git a/nextest-filtering/src/parsing/glob.rs b/nextest-filtering/src/parsing/glob.rs index e6e6b348125..6bd71b9574d 100644 --- a/nextest-filtering/src/parsing/glob.rs +++ b/nextest-filtering/src/parsing/glob.rs @@ -3,12 +3,12 @@ //! Glob matching. -use super::{parse_matcher_text, IResult, Span}; +use super::{parse_matcher_text, Error, Span}; use crate::{ errors::{GlobConstructError, ParseSingleError}, NameMatcher, }; -use nom_tracable::tracable_parser; +use winnow::{stream::Location, trace::trace, Parser}; /// A glob pattern. /// @@ -58,30 +58,31 @@ impl GenericGlob { } // This never returns Err(()) -- instead, it reports an error to the parsing state. -#[tracable_parser] -pub(super) fn parse_glob(input: Span<'_>, implicit: bool) -> IResult<'_, Option> { - let (i, res) = match parse_matcher_text(input.clone()) { - Ok((i, res)) => (i, res), - Err(_) => { - unreachable!("parse_matcher_text should never fail") - } - }; +pub(super) fn parse_glob<'i>(implicit: bool) -> impl Parser, Option, Error> { + trace("parse_glob", move |input: &mut Span<'i>| { + let start = input.location(); + let res = match parse_matcher_text.parse_next(input) { + Ok(res) => res, + Err(_) => { + unreachable!("parse_matcher_text should never fail") + } + }; - let Some(parsed_value) = res else { - return Ok((i, None)); - }; + let Some(parsed_value) = res else { + return Ok(None); + }; - match GenericGlob::new(parsed_value) { - Ok(glob) => Ok((i, Some(NameMatcher::Glob { glob, implicit }))), - Err(error) => { - let start = input.location_offset(); - let end = i.location_offset(); - let err = ParseSingleError::InvalidGlob { - span: (start, end - start).into(), - error, - }; - i.extra.report_error(err); - Ok((i, None)) + match GenericGlob::new(parsed_value) { + Ok(glob) => Ok(Some(NameMatcher::Glob { glob, implicit })), + Err(error) => { + let end = input.location(); + let err = ParseSingleError::InvalidGlob { + span: (start, end - start).into(), + error, + }; + input.state.report_error(err); + Ok(None) + } } - } + }) } diff --git a/nextest-filtering/src/parsing/unicode_string.rs b/nextest-filtering/src/parsing/unicode_string.rs index 8af833c42d1..5069c2c88bf 100644 --- a/nextest-filtering/src/parsing/unicode_string.rs +++ b/nextest-filtering/src/parsing/unicode_string.rs @@ -3,83 +3,53 @@ // Adapted from https://github.com/Geal/nom/blob/294ffb3d9e0ade2c3b7ddfff52484b6d643dcce1/examples/string.rs -use super::{expect_n, IResult, Span, SpanLength}; +use super::{expect_n, PResult, Span, SpanLength}; use crate::errors::ParseSingleError; -use nom::{ - branch::alt, - bytes::complete::{is_not, take_while_m_n}, - character::complete::char, - combinator::{map, map_opt, map_res, value, verify}, - multi::fold_many0, - sequence::{delimited, preceded}, - Slice, -}; -use nom_tracable::tracable_parser; use std::fmt; +use winnow::{ + combinator::{alt, delimited, fold_repeat, preceded}, + token::{take_till, take_while}, + trace::trace, + Parser, +}; -fn run_str_parser<'a, T, I>(mut inner: I) -> impl FnMut(Span<'a>) -> IResult<'a, T> -where - I: FnMut(&'a str) -> nom::IResult<&'a str, T>, -{ - move |input| match inner(input.fragment()) { - Ok((i, res)) => { - let eaten = input.fragment().len() - i.len(); - Ok((input.slice(eaten..), res)) - } - Err(nom::Err::Error(err)) => { - let nom::error::Error { input: i, code } = err; - let eaten = input.fragment().len() - i.len(); - let err = nom::error::Error { - input: input.slice(eaten..), - code, - }; - Err(nom::Err::Error(err)) - } - Err(nom::Err::Failure(err)) => { - let nom::error::Error { input: i, code } = err; - let eaten = input.fragment().len() - i.len(); - let err = nom::error::Error { - input: input.slice(eaten..), - code, - }; - Err(nom::Err::Failure(err)) - } - Err(nom::Err::Incomplete(err)) => Err(nom::Err::Incomplete(err)), - } -} - -#[tracable_parser] -fn parse_unicode(input: Span<'_>) -> IResult<'_, char> { - let parse_hex = take_while_m_n(1, 6, |c: char| c.is_ascii_hexdigit()); - let parse_delimited_hex = preceded(char('u'), delimited(char('{'), parse_hex, char('}'))); - let parse_u32 = map_res(parse_delimited_hex, |hex| u32::from_str_radix(hex, 16)); - run_str_parser(map_opt(parse_u32, std::char::from_u32))(input) +fn parse_unicode(input: &mut Span<'_>) -> PResult { + trace("parse_unicode", |input: &mut _| { + let parse_hex = take_while(1..=6, |c: char| c.is_ascii_hexdigit()); + let parse_delimited_hex = preceded('u', delimited('{', parse_hex, '}')); + let parse_u32 = parse_delimited_hex.try_map(|hex| u32::from_str_radix(hex, 16)); + parse_u32.verify_map(std::char::from_u32).parse_next(input) + }) + .parse_next(input) } -#[tracable_parser] -fn parse_escaped_char(input: Span<'_>) -> IResult<'_, Option> { - let valid = alt(( - parse_unicode, - value('\n', char('n')), - value('\r', char('r')), - value('\t', char('t')), - value('\u{08}', char('b')), - value('\u{0C}', char('f')), - value('\\', char('\\')), - value('/', char('/')), - value(')', char(')')), - value(',', char(',')), - )); - preceded( - char('\\'), - // If none of the valid characters are found, this will report an error. - expect_n( - valid, - ParseSingleError::InvalidEscapeCharacter, - // -1 to account for the preceding backslash. - SpanLength::Offset(-1, 2), - ), - )(input) +fn parse_escaped_char(input: &mut Span<'_>) -> PResult> { + trace("parse_escaped_char", |input: &mut _| { + let valid = alt(( + parse_unicode, + 'n'.value('\n'), + 'r'.value('\r'), + 't'.value('\t'), + 'b'.value('\u{08}'), + 'f'.value('\u{0C}'), + '\\'.value('\\'), + '/'.value('/'), + ')'.value(')'), + ','.value(','), + )); + preceded( + '\\', + // If none of the valid characters are found, this will report an error. + expect_n( + valid, + ParseSingleError::InvalidEscapeCharacter, + // -1 to account for the preceding backslash. + SpanLength::Offset(-1, 2), + ), + ) + .parse_next(input) + }) + .parse_next(input) } // This should match parse_escaped_char above. @@ -100,11 +70,15 @@ impl fmt::Display for DisplayParsedString<'_> { Ok(()) } } -#[tracable_parser] -fn parse_literal(input: Span<'_>) -> IResult<'_, Span<'_>> { - let not_quote_slash = is_not(",)\\"); - let res = verify(not_quote_slash, |s: &Span<'_>| !s.fragment().is_empty())(input.clone()); - res +fn parse_literal<'i>(input: &mut Span<'i>) -> PResult<&'i str> { + trace("parse_literal", |input: &mut _| { + let not_quote_slash = take_till(1.., (',', ')', '\\')); + + not_quote_slash + .verify(|s: &str| !s.is_empty()) + .parse_next(input) + }) + .parse_next(input) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -113,43 +87,46 @@ enum StringFragment<'a> { EscapedChar(char), } -#[tracable_parser] -fn parse_fragment(input: Span<'_>) -> IResult<'_, Option>> { - alt(( - map(parse_literal, |span| { - Some(StringFragment::Literal(span.fragment())) - }), - map(parse_escaped_char, |res| { - res.map(StringFragment::EscapedChar) - }), - ))(input) +fn parse_fragment<'i>(input: &mut Span<'i>) -> PResult>> { + trace( + "parse_fragment", + alt(( + parse_literal.map(|span| Some(StringFragment::Literal(span))), + parse_escaped_char.map(|res| res.map(StringFragment::EscapedChar)), + )), + ) + .parse_next(input) } /// Construct a string by consuming the input until the next unescaped ) or ,. /// /// Returns None if the string isn't valid. -#[tracable_parser] -pub(super) fn parse_string(input: Span<'_>) -> IResult<'_, Option> { - fold_many0( - parse_fragment, - || Some(String::new()), - |string, fragment| { - match (string, fragment) { - (Some(mut string), Some(StringFragment::Literal(s))) => { - string.push_str(s); - Some(string) - } - (Some(mut string), Some(StringFragment::EscapedChar(c))) => { - string.push(c); - Some(string) +pub(super) fn parse_string(input: &mut Span<'_>) -> PResult> { + trace( + "parse_string", + fold_repeat( + 0.., + parse_fragment, + || Some(String::new()), + |string, fragment| { + match (string, fragment) { + (Some(mut string), Some(StringFragment::Literal(s))) => { + string.push_str(s); + Some(string) + } + (Some(mut string), Some(StringFragment::EscapedChar(c))) => { + string.push(c); + Some(string) + } + (Some(_), None) => { + // We encountered a parsing error, and at this point we'll stop returning + // values. + None + } + (None, _) => None, } - (Some(_), None) => { - // We encountered a parsing error, and at this point we'll stop returning - // values. - None - } - (None, _) => None, - } - }, - )(input) + }, + ), + ) + .parse_next(input) } diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index 22564b3b4c5..f8c750af430 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -25,7 +25,7 @@ futures-sink = { version = "0.3.30", default-features = false, features = ["std" getrandom = { version = "0.2.11", default-features = false, features = ["std"] } indexmap = { version = "2.1.0", features = ["serde"] } log = { version = "0.4.20", default-features = false, features = ["std"] } -memchr = { version = "2.7.1", features = ["use_std"] } +memchr = { version = "2.7.1" } miette = { version = "5.10.0", features = ["fancy"] } num-traits = { version = "0.2.17", default-features = false, features = ["libm", "std"] } owo-colors = { version = "4.0.0", default-features = false, features = ["supports-colors"] }