ISibboI / evalexpr

A powerful expression evaluation crate 🦀.
MIT License
316 stars 51 forks source link

Support for units on nummerial values #122

Open asafigan opened 1 year ago

asafigan commented 1 year ago

I would like to add units like px, rem, em, and %. This should be an opt in feature some how. Most likely a separate function/api. I would like to be able to only allow a set of units I define and error on any unknown units. This would also be useful for people who would like to define units like km, ms, etc.

I believe the output should be an expression so it can handle mixing units. For example, (4px * 8) + 50% should be 32px + 50%. There doesn't seem to be a type to express this type of output. Node seems close but it's fields are private.

Also unit conversions would be nice but that could be a separate issue.

Edge case: 100% / 3 - 2 * 1em - 2 * 1px I believe this would be 33.333...% - 2em - 2px

ISibboI commented 1 year ago

This would be a bit more complicated. First, it would require integers and floats to be types other than i64 and f64. I tried doing this for #111 and #115, but did not have time to finish it so far. After that, one could have custom types that accept units. That is then no problem. However then one also would need to parse units. So far, there is no such thing as a postfix operator in the parser that could represent a unit, so it would be a completely new thing.

I am happy to accept pull requests that push this issue forward.

hexofyore commented 1 year ago

@ISibboI Do you have any thoughts on this issue or are working on it? How would having an expression be fed with a type of units like distance and have evaluator automatically convert all of them into base.

Like, 2km+1m = 2100m.

Or what about having result be string which multiplies units and leaves as it is for add expression.

ISibboI commented 1 year ago

I think this should be done in a type-safe way, meaning that the evalexpr should know about units somehow. But only in the case where the user actually wants to use units. The way to go would be to make int and float types generic, and then allow the user to supply their own types with their own parsing mechanisms, operations, etc.

I have tried making the int and float types generic on branch sebschmi/111_custom_int_float_types, but never finished. What I didn't like about what I did back then was that having two separate numeric types makes expressions like 5.into() impossible, because the compiler does not know if 5 should become a Value::Int or a Value::Float.

My current conclusion on that is that the Value enum should not have two separate numeric types, but instead there should be only one that itself knows if it is an integer or float. And that then can also include units, or other numeric types like unlimited integers or floats.

Design-wise, that type should be specified by the context. And it probably requires some rethinking of builtin functions, to e.g. not enforce any numeric type to implement all the trigonometric functions.