Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

choice! returns an error when one of its parsers would be successful #321

Open
Ploppz opened this issue Aug 6, 2021 · 2 comments
Open

Comments

@Ploppz
Copy link

Ploppz commented Aug 6, 2021

Reproducible test case:

#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)]
pub enum ClusterType {
    Default,
    Dedicated,
}

#[derive(Debug, Eq, PartialEq)]
pub struct Cluster {
    pub pool_type: ClusterType,
    pub pool_project: Option<String>,
    pub n: usize,
}
    #[test]
    fn abc() {
        use combine::{
            choice, many, many1, none_of,
            parser::char::{digit, letter},
            struct_parser, value, Parser,
        };

        let integer = many1(digit()).map(|string: String| string.parse::<usize>().unwrap());
        let tag = |tag: &'static str| combine::tokens(|l, r| l == r, tag, tag.chars());
        let mut x_parser = struct_parser! {
            Cluster {
                _: tag("M "),
                _: many::<Vec<_>,_,_>(letter()),
                _: tag(" - "),
                n: integer.clone(),
                pool_type: value(ClusterType::Default),
                pool_project: value(None)
            }
        };
        let mut y_parser = struct_parser! {
            Cluster {
                _: tag("M "),
                _: many::<Vec<_>,_,_>(letter()),
                _: tag(" (project \""),
                pool_project: many(none_of("\"".chars())).map(Some),
                _: tag("\") - "),
                n: integer,
                pool_type: value(ClusterType::Dedicated)
            }
        };

        let x = "M Dev - 5";
        let y = "M Dev (project \"myproject\") - 4";
        println!("{:?}", x_parser.parse(x));
        println!("{:?}", y_parser.parse(x));
        println!("{:?}", x_parser.parse(y));
        println!("{:?}", y_parser.parse(y));


        let mut choice = choice!(x_parser, y_parser);

        println!("{:?}", choice.parse(x));
        println!("{:?}", choice.parse(y));
    }

Prints:

Ok((Cluster { pool_type: Default, pool_project: None, n: 5 }, ""))
Err(UnexpectedParse)
Err(UnexpectedParse)
Ok((Cluster { pool_type: Dedicated, pool_project: Some("myproject"), n: 4 }, ""))
Ok((Cluster { pool_type: Default, pool_project: None, n: 5 }, ""))
Err(UnexpectedParse)

I expected the last one to also be Ok because of how the y_parser can successfully parse y

@Ploppz
Copy link
Author

Ploppz commented Aug 6, 2021

Solved by @musikid: it needs to be choice!(attempt(x_parser), y_parser). I was looking at this documentation https://docs.rs/combine/4.6.0/combine/macro.choice.html which does not include any attempt - maybe a good idea to mention it there?
Also why does that example work, seeing as in the first assert, the first parser will fail, but it still works without attempt()?

@Marwes
Copy link
Owner

Marwes commented Aug 10, 2021

That example works because it fails when looking at the first character, so it never consumes any input. It is thanks to this choice and similar parsers can produce error messages which hint to each alternative that was attempted. attempt makes a parser always work as if it consumed no input before it failed but it does this at the cost of making the error messages worse.

attempt ought to be linked in all parsers that tries alternatives, I actually thought it did already, maybe just in some of them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants