{"action":"created","issue":{"url":"https://api.github.com/repos/Geal/nom/issues/160","repository_url":"https://api.github.com/repos/Geal/nom","labels_url":"https://api.github.com/repos/Geal/nom/issues/160/labels{/name}","comments_url":"https://api.github.com/repos/Geal/nom/issues/160/comments","events_url":"https://api.github.com/repos/Geal/nom/issues/160/events","html_url":"https://github.com/Geal/nom/issues/160","id":124987445,"number":160,"title":"Errors are often suppressed","user":{"login":"asajeffrey","id":403333,"avatar_url":"https://avatars.githubusercontent.com/u/403333?v=3","gravatar_id":"","url":"https://api.github.com/users/asajeffrey","html_url":"https://github.com/asajeffrey","followers_url":"https://api.github.com/users/asajeffrey/followers","following_url":"https://api.github.com/users/asajeffrey/following{/other_user}","gists_url":"https://api.github.com/users/asajeffrey/gists{/gist_id}","starred_url":"https://api.github.com/users/asajeffrey/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/asajeffrey/subscriptions","organizations_url":"https://api.github.com/users/asajeffrey/orgs","repos_url":"https://api.github.com/users/asajeffrey/repos","events_url":"https://api.github.com/users/asajeffrey/events{/privacy}","received_events_url":"https://api.github.com/users/asajeffrey/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":4,"created_at":"2016-01-05T15:21:19Z","updated_at":"2016-02-23T21:11:36Z","closed_at":null,"body":"If I create a parser which uses any of the choice combinators (`alt!`, `opt!`, `many0!`, etc.) then errors are suppressed from the child parser. For example:\r\n```\r\nnamed!(pub foo< bool >,\r\n    chain!(\r\n        tag!(\"a\") ~\r\n        error!(nom::ErrorKind::Custom(42),tag!(\"b\")) ,\r\n        || { true }\r\n    )\r\n);\r\n\r\nnamed!(pub foos< Vec<bool> >,\r\n    delimited!(\r\n        tag!(\"(\"),\r\n        many0!(foo),\r\n        tag!(\")\")\r\n    )\r\n);\r\n```\r\nIf you feed this the input \"(ac)\" then the error 42 from foo is suppressed, and instead you get the error when tag!(\")\") fails to match \"ac)\". Here are tests I'd expect to pass, but which don't:\r\n```\r\n#[test]\r\nfn test_ok() {\r\n    match foos(b\"(abab)\") {\r\n        nom::IResult::Done(_,result) => assert_eq!(result,vec![true,true]),\r\n        res => panic!(\"Oops {:?}.\",res)\r\n    }\r\n}\r\n\r\n#[test]\r\nfn test_err() {\r\n    match foos(b\"(ac)\") {\r\n        nom::IResult::Error(nom::Err::Position(kind,_)) => assert_eq!(kind,nom::ErrorKind::Custom(42)),\r\n        res => panic!(\"Oops, {:?}\",res)\r\n    }\r\n}\r\n```\r\nThis is a problem with backtracking: there's no way to stop backtracking outside of the current named! function. The program behaves properly if we inline foo into foos, but this doesn't scale too well."},"comment":{"url":"https://api.github.com/repos/Geal/nom/issues/comments/187907541","html_url":"https://github.com/Geal/nom/issues/160#issuecomment-187907541","issue_url":"https://api.github.com/repos/Geal/nom/issues/160","id":187907541,"user":{"login":"Geal","id":119296,"avatar_url":"https://avatars.githubusercontent.com/u/119296?v=3","gravatar_id":"","url":"https://api.github.com/users/Geal","html_url":"https://github.com/Geal","followers_url":"https://api.github.com/users/Geal/followers","following_url":"https://api.github.com/users/Geal/following{/other_user}","gists_url":"https://api.github.com/users/Geal/gists{/gist_id}","starred_url":"https://api.github.com/users/Geal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Geal/subscriptions","organizations_url":"https://api.github.com/users/Geal/orgs","repos_url":"https://api.github.com/users/Geal/repos","events_url":"https://api.github.com/users/Geal/events{/privacy}","received_events_url":"https://api.github.com/users/Geal/received_events","type":"User","site_admin":false},"created_at":"2016-02-23T21:11:36Z","updated_at":"2016-02-23T21:11:36Z","body":"Hey, I have given some thought to your problem, and found a way. It is a bit hackish, and I know you got your own library to worry about now, but you might find it interesting.\r\n\r\n[Take a look at the gist](https://gist.github.com/Geal/6f3c9a7e81fdb8623628)\r\n\r\nThe basic idea is to change the result type of parsers from `nom::IResult<I,O,E>` to `std::result::Result<nom::IResult<I,O,E>, nom::Err<I,E>>`. If the parser returns `Ok(something)` continue as usual, backtrack normally if there's an error. But if it is `std::result::Result::Err(nom::Err(something))` then stop backtracking right there.\r\n\r\nIt is doable by replacing `named!` with `n!` which will replace the result types and wrap everything with `Ok`. That's a large part of the code because `named!` has so many syntaxes :/\r\n\r\nThen you have the `cut!` macro, which works like nom's `error!` combinator, but will wrap with `Ok` for `Incomplete` or `Done`, and wrap with `Err` and cut if there's an error.\r\n\r\nAt last, you have `c!`, used to wrap any sub function call. This will check the result, and cut backtracing if there's an `Err`. You can see it used at line 106.\r\n\r\nSo, as I said, it is a bit hackish, but the ugly macros can be stored away from the eyes, it does not change the code much, and the overhead of `Result` will not be too big.\r\n\r\nWhat do you think?"}}