New expression evaluator for Pothos GUI Tue, Mar 31 2015 AM
Status update! I just merged some useful new capabilities into Pothos GUI. Its been a while coming, but the GUI has a new, more capable expression evaluator and support for declaring graph-wide global variables.
Graph panel for global variables
There is now a properties panel for the graph itself. In this panel, users can create and modify global variables. The global variables can be used inside parameter entry fields for blocks. Blocks will be re-evaluated when a global variable they depend on has changed. Read more on the Tutorial page.
The old expression evaluator
Previously, the expression evaluator supported primitive types, and reached out to the system's compiler for basic expressions. So, if you wanted to use 1 + 2 as a parameter, the expression evaluator generated code with your expression, compiled it into a library, and loaded a symbol out of that library. This method is terribly slow and the compiler error messages for malformed expressions were ugly. And needing a compiler installed isn't great for the eventual user's of the windows installers. Anyway, this was always a stop-gap measure to avoid creating a more complicated expression parser like...
muparserx expression parser library
muparserx. is a truly excellent quality mathematical expression parser library. I just found out about it a week or two ago while searching for expression parser libraries.
- Its a C++ library,
- its easy to embed into a project,
- it supports variable declaration,
- it supports complex arithmetic,
- and transcendental functions,
- and array-based arithmetic.
- ...exactly what I was looking for.
A few caveats
Or almost exactly what I was looking for. The muparserx evaluator was somewhat lacking when it came to containers. Basically it supports numeric arrays and nothing more. Not arrays of arrays, not arrays of mixed data types, and for that matter, not dictionaries.
However, the parser itself is capable of dealing with arbitrary containers. A container can be set to a variable or constant on the parser object, and then used in an expression. So, I basically pre-process the expressions to extract the lists and dictionaries, and spoon-feed the containers and expressions to the parser in a way that it can handle. Not ideal, but it gets the job done and makes maximal use out of muparserx.
Also, the parser lacks a 64-bit integer type. Any expressions that don't fit into a 32-bit integer become a floating point type. Unfortunately, this will result in precision loss in some situations.
Transform block for signals/slots
As a side effect of this new evaluator work, I was able to put together a block that intercepts a signal, and uses the evaluator to transform the value. Using this transformation block, users can transform signals and slots with arbitrary expressions. In this image, the numeric entry slider effecticly produces log-scaling.