Repository navigation
tinyexpr improvements - #13061
tinyexpr improvements#13061Nahor wants to merge 13 commits into
Conversation
The alias was using a lowerCamelCase while conventionally Rust types use UpperCamelCase. Some syntax highlighters rely on the convention for styling, leading to the incorrect style being used. The chosen alias was also not ideal since the error is technically not limited to the `wcstodXXX` functions, even if that's the only way to currently get it in `tinyexpr`. The qualified name is barely longer than the alias, so go with that rather than a better alias.
The reason `get_token` returns an `Option` is for whitespaces and eof. - Move the whitespaces handling in `next_token` since they do not generate tokens. This is also marginally more efficient. - Move the eof handling in `get_token`. This also makes `get_token` the sole token generator outside of the special `Token::Error`
The token is not just for infix operator but also as a sign for subsequent number or function
This makes the code more readable and intuitive by removing uncommon `if let <constant> = ...` and verbose `matches!(...)` (and the easily misread `if !matches!()`)
`get_token()` should only be called if there are no errors, and nothing sets `self.current` earlier in the function.
The old API is taken from C and is not idiomatic Rust.
Instead of putting the caret at the end of the input, which is unhelpful, put it on the opening parenthesis
A part of the change is how "too many args" error is reported because it is more logical and allow for significant code simplification. The error carret is now on the first extra comma rather than the following value, since that's where a closing parenthesis is expected (or an end-of-string). And do not report a length since the parser could stop at some arbitrary location when there are more errors afterwards.
Rename `State::current` to `State::token`. The old name doesn't tell you "what" the variable is about (the state? the position? some settings? ...). Moreover, "current" is uselessly specific when there isn't some "previous" or "next" variable to go along with it.
This improves error reporting by allowing to put the caret at the start of the current token instead of the end, or avoids hardcoding the token width when we already put the caret at the correct location.
| } | ||
| } | ||
|
|
||
| impl PartialEq for Function { |
There was a problem hiding this comment.
I did a relatively quick scan over all commits; everything I saw and the test behavior changes look good to me.
I didn't review super closely but as long as there are no unintended behavior changes this is fine.
LGTM. One optional comment.
| (Self::Constant(l0), Self::Constant(r0)) => l0 == r0, | ||
| (Self::Fn1(l0), Self::Fn1(r0)) => std::ptr::fn_addr_eq(*l0, *r0), | ||
| (Self::Fn2(l0), Self::Fn2(r0)) => std::ptr::fn_addr_eq(*l0, *r0), | ||
| (Self::FnN(l0), Self::FnN(r0)) => std::ptr::fn_addr_eq(*l0, *r0), |
There was a problem hiding this comment.
I agree that "if let" that doesn't bind anything is weird;
I'm okay with either but if we stick to "matches!(...)", we wouldn't need to add this PartialEq impl.
If "!matches!()" is too subtle, we could maybe write "does_not_match!()"?
That's something we could use elsewhere too. Sounds a bit weird though.
There was a problem hiding this comment.
I thought of using matches! everywhere instead of PartialEq, but it is not as nice as ==, it's harder to read/write.
I'm not keen on does_not_match! since it's weird as you say. I'd rather keep !matches!.
An alternative is to manually implement PartialEq for Token and only compare the discriminant (i.e. the enum itself, not the values in each enum entry) since we only use == for entries with not values and still use matches! for the other (mostly Token::Error).
At some point I had that, to avoid the PartialEq on Function because I don't like the use of fn_addr_eq. But that make PartialEq on Token a bit odd/non-standard, so I reverted that change.
A variant of the alternative is to use our function instead of the standard PartialEq::eq(), for instance a is(<token>) to avoid the subversion of PartialEq but it's not as discoverable.
Or a family if is_<token>() (similar to is_some()/is_ok()) but that a lot of functions barely used if at all.
Best option: the is operator, but we'll have to wait a while for that 🥲
It started with a fix for the error caret location but evolved in a major update to make the code more idiomatic, and hopefully more readable and easier to reason with.