1. General info
Library written in C++ for parsing mathematical expressions and creating expression maps that can be used as an arbitrary functions in runtime. The syntax of the expression is in C and the parsing is done by transforming the given infix expression to Reverse Polish (RP) notation. The RP is then further parsed to yield a map of arguments and operators that can be reused. The RP map can be used as an arbitrary function by setting the user defined variables in the expression and calling for the calculation of the result.
2. Class architecture and working
The architecture of the class is build around the single export class MathParser. This class is virtual class that returns a pointer to the created object inside the library space. This way we keep all the allocated memory inside the library heap and the functionality is provided by a reference to the created object.
The main objective of this library is to have the following functionality:
- Load an expression as a string:
MathParser *mp = MathParser::makeMathParser(); mp->setMath("2 * sin( 2*pi*f*t + phi )");
- Use the generated object as a function with arguments in runtime:
mp->setVariableDouble("phi", 0.785398); double result = mp->calculateExpression();
We parse the expression and resolve the elements to different types of entities. Then we combine the entities in a RP notation. We run through the RP notation and generate intermediary steps of the calculation as separate arguments (generated variables).
For example, if we parse the infix expression :
a*pow(5*b, pi/(2 + 1)^4)
The following RP notation is generated:
a 5 b * pi 2 1 + 4 ^ / pow *
Which in the code is written as:
#AA #AB #AC * pi #AD #AE + #AF ^ / pow *
After expanding the RP expression to intermediate steps we get the following expression:
#AA #AB #AC * pi #AD #AE + #AF ^ / pow * #AA #AG pi #AH #AF ^ / pow * #AA #AG pi #AI / pow * #AA #AG #AJ pow * #AA #AK * #AL
|user variable||user constant||generated variable|
|#AA = a|
|#AB = 5|
|#AC = b|
|#AD = 2|
|#AE = 1|
|#AF = 4|
|#AG = 5*b|
|#AH = 2+1|
|#AI = (2+1)^4|
|#AJ = pi/(2+1)^4|
|#AK = pow(5*b, pi/(2+1)^4)|
|#AL = a*pow(5*b, pi/(2+1)^4)|
The expanded expression is not meant to be calculated, instead calculations are performed only on the generated variables. In order to get fast calling we store the generated variables in separate map. Upon request for the result we calculate generated variables successively from #AG to #AL. In the end #AL is the result of our calculation. By setting the user variables prior to calling the result this train of calculations gives the result of the function operating on those user variables.
We recognize three types of entities:
Argument - anything that can have a value. This can be a predefined constant in the system, a number from the expression (user constant), a variable or a generated variable (a temporal variable generated as a stack for calculation steps).
Operator - functions defined in the system at compile time. Names of operators are reserved words. These are functions defined for example in the math.h library like sin, pow or static functions that are defined in the MathParser.
Generator - combination of operators and arguments used in mid-steps of calculating the result. Names of generator coincide with some names of arguments from the argument map, this connects the argument and generator. Arguments connected to generators are generated variables.