-
Notifications
You must be signed in to change notification settings - Fork 159
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
support nothrow expect #242
base: develop
Are you sure you want to change the base?
Conversation
What's the reason for using -O2 instead of -O3? Can you provide a benchmark test program that we can try that shows these numbers? How about other compilers, e.g. g++? |
Also, if you are going to just fail the parse anyway, what's the advantage of using a > b instead of just a >> b ? |
|
The example above is a simple case that will fail
|
The advantage of expectation over sequence is when we have many alternatives. For example in a simple parser below,
if the first We also found that in our practice, most of the sequence operators are actually expectations. Why are there so many failures in the code? We use spirit x3 to implement erase_all/replace_all and it performs better than the boost regex equivalents. |
Looks good to me. I'll need some time to try and study the example code. |
OK, the speed issue is really disconcerting. On one hand this fix is a step in the right direction. On the other hand, previous code using the throw can't take advantage of the "fix". For example, I rely on the exceptions for error handling, by catching the expectation errors in the grammar via error handlers. Is there a way to also change the X3 error handling scheme to not require any code changes with this fix? |
@djowel Sorry for the late reply. 1. throwing
2. inject a
3. inject a
I will put option 2 (inject a Functionality-wise option 1 and option 3 are the same and one of them should be the default option given to the users. Both of them have full error handling capability. I will assume error handling based on full The code change for option 2 and 3 in error handling (guard and parse_rhs_main) and parser structs (operators, directives) are mostly the same except for some overloaded functions for option 3. To sum up, option 2 is something nice to have. whether or not using option 3 instead of option 1 depends on what do we normally do in the error handling code and how many users need to review their user defined parser structs. |
Option 3 sounds good. I'm not sure I understand "users have to be careful when writing user defined parser structs"... could you elaborate a bit with an example, like the optional operator? |
For example we have a user defined ascii_no_case parser struct (in this case a user defined directive). In it's parse function it calls subject's parse function and simply return its result. It's safe for nothrow expect because the parse function will return false when expectation failure happens. But optional operator is different (take it as an example). The parse(_subject) function tries to match subject's parse function but will always return true even if the subject's parse function fails. Then we have to be careful and change the return statement to So if we choose option 3 as the default option, the users who write their own parser structs (like ascii_no_case or more complex ones like optional operator) should carefully review these structs.
|
OK, I like option 3. This one needs documentation though. Are you willing to go all the way and include documentation updates? Many thanks! |
Sure. I will change the code and do some profiling first. |
1. x3::expect directive and operator have the same semantics as before. 2. When expect failure happens, the whole parser will not try any alternative paths just like before. It will simply fail without throwing any exceptions. 3. x3::guard and ID::on_error can still get the expectation_failure struct when expect failure happens. The error handling scheme is also not changed. 'rethrow' means return false and keep expect failure information visible to the parent parser. 'fail' means return false but remove the expect failure information. 4. Allow user to use expect operator more aggressively without any performance loss brought by throwing exceptions. 5. If x3::expect is used and fails in skipper parser, the main parser should not fail (The skipper parser is a totally different parser with a different context. We should use it only to skip). 6. Add unit test cases to test all existing parsers. 7. Users who care more about the processing speed and always ignore the detailed expectation_failure information can accelerate your parser by injecting a 'false' into your parser/skipper using x3::with<x3::expectation_failure_tag>(false)[parser/skipper].
42df1ec
to
dc85ff2
Compare
x3::expect doesn't throw expectation_failure anymore
|
I might be missing something here. This is a major change and can definitely affect current users' code. I think the code should therefore be optional, using a preprocessor macro, with the default having the original exception behavior. We should not force this change on the users. With this option on, some parts of the API, such as 'rethrow', do not make sense any more, and perhaps should have a different behavior. We need to be very careful. I think we also need to discuss this over at the Spirit list. |
Throwing expectation_failure is so much slower (around 1000 times with clang -O2 on my mac) than the equivalent parser with sequence operator. It will increase the parsing performance if we are giving the option to use expect operator without throwing exceptions yet still keep its semantics. It has the same speed in a simple straightforward parser with the equivalent parser using sequence operator. It becomes faster when the parser has alternatives.
x3::parse(input.begin(), input.end(), x3::with<x3::expectation_failure_tag>(false)[...]);