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

Add support for expressions in syscfg values #470

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

andrzej-kaczmarek
Copy link
Contributor

@andrzej-kaczmarek andrzej-kaczmarek commented Mar 23, 2022

This adds support for evaluating syscfg values as expressions. This is done for all values by default so some existing configurations may need to be changed.

Following tokens are allowed in expressions:

  • literals (integers and strings)
  • identifiers (references to other syscfg values)
  • parentheses
  • binary operators (arithmetic, relational and boolean)
  • unary operator (boolean negation)
  • built-in function calls

Most operators support only integer values. Strings are supported by == and != only.

Conversions between integer and boolean are done implicitly:

  • true -> 1
  • false -> 0
  • ==0 -> false
  • !=0 -> true

Arguments to all built-in function shall be integer, unless explicitly mentioned otherwise.

Available built-in functions are:

  • min(a,b) - returns lesser of a and b
  • max(a,b) - returns greater of a and b
  • in_range(v,a,b) - returns if v is inside [a,b] range
  • clamp(v,a,b) - clamps v to be inside [a,b] range
  • ite(v,a,b) - if-then-else, returns a if v, otherwise returns b (note: a and b can be of any type)
  • in_set(v,...) - returns if v is one of remaining arguments (note: all argument can be of any type)
  • raw(v) - returns raw value, i.e. value that can be any arbitrary string but will not be double-quoted in syscfg.h (equivalent of default behavior for "old" evaluator)

This adds support for evaluating syscfg values as expressions. To make
it compatible with existing code, syscfg value is only evaluated as
expression if its type is explicitly set to "expr", i.e.:

syscfg.defs:
  FOO:
    description: ...
    type: expr
    value: 1 + 2 + 3

Following tokens are allowed in expressions:
- literals (integers and strings)
- identifiers (references to other syscfg values)
- parentheses
- binary operators (arthmetic, relational and boolean)
- unary operator (boolean negation)
- built-in function calls

Most of operators support only integer values. Strings are supported by
"==" and "!=" only.

Available built-in functions are:
- min(a,b) - returns lesser of "a" and "b"
- max(a,b) - returns greater of "a" and "b"
- in_range(v,a,b) - returns if "v" is inside [a,b] range
- clamp(v,a,b) - clamps "v" to be inside [a,b] range
- ite(v,a,b) - if-then-else, returns "a" if "v", otherwise returns "b"
- in_set(v,...) - returns if "v" is one of remaining arguments

Note: all arguments to built-in functions shall be integer only, except
for "a" and "b" in ite() and all arguments in in_set().
@kasjer
Copy link
Contributor

kasjer commented Mar 24, 2022

This adds support for evaluating syscfg values as expressions. To make it compatible with existing code, syscfg value is only evaluated as expression if its type is explicitly set to expr, i.e.:

syscfg.defs:
  FOO:
    description: ...
    type: expr
    value: 1 + 2 + 3

Is limiting expression evaluation to definition that explicitly allow this necessary?
It really limits its potential in my opinion.
I guess it is possible to have expressions in syscfg.vals and not only syscfg.defs, and limitation seems to be to harsh.

I would rather think of syntax similar to what is used in make/cmake where identifiers that should be evaluated are marked as $VAL or ${VAL}.
Or at least allow expression to be placed in anywhere with syntax like $(expression to be evaluated by newt) instead of forcing definition to have type:expr.

Except for defunc settings since those are not used anyway.
Some settings may reference values with MYNEWT_VAL_ prefix - this was
done to actually evaluate them as expression in preprocessor.
We can fix those and emit warning instead of failing.
If syscfg value is a choice, it's evaluated as an identifier so we need
to add special handling in such case - if identifier is evaluated
directly "from" entry, we should first check if value is a valid choice.
This adds function "raw" that expects a single string argument and will
return that argument contents as raw value, i.e. syscfg value will be
exactly that string without quotes.
This allows to differentiate empty strings (i.e. #define XXX "") and
empty values (i.e. #undef XXX).
@andrzej-kaczmarek
Copy link
Contributor Author

andrzej-kaczmarek commented Mar 24, 2022

@kasjer I changed as discussed offline, i.e. all values are now always evaluated and it's possible to explicitly state that value should not be evaluated by using raw() function. and that means effectively it's still evaluated but returns raw string so it should return meaningful errors if referenced in another syscfg. this obviously breaks backwards compatibility but it's far more flexible and thus useful and seems like necessary changes to existing configuration should be pretty straightforward to do (examples referenced in other PRs)

this moves evaluator to separate package which interfaces with syscfg
via interface. this removes direct dependency on syscfg package.
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

Successfully merging this pull request may close these issues.

None yet

2 participants