📄 fparser.txt
字号:
Function parser for C++ v2.7 by Warp.
=====================================
Optimization code contributed by Bisqwit (http://iki.fi/bisqwit/)
The usage license of this library is located at the end of this text file.
What's new in v2.7
------------------
- Changed precedence rules for unary minus and the power operator (^) to
make it closer in functionality to the power "operator" in mathematics
(ie. superscript):
* Consecutive power operators at the same precedence level are
evaluated from right to left. That is, for example "2^3^4" is
now evaluated as if it had been written as "2^(3^4)" (and not
as "(2^3)^4" like in previous versions).
* The unary minus in the base of the power has now a lower precedence
than the power operator. That is, "-2^3" will be evaluated as if
written as "-(2^3)", ie. the result is "-8" (and not "8" like in
previous versions). The unary minus in the exponent is still
evaluated first because of the right-left precedence change above
(that is, "-2^-3" is evaluated as "-(2^(-3))").
- Fixed a bug in the copy-on-write engine.
=============================================================================
- Preface
=============================================================================
Often people need to ask some mathematical expression from the user and
then evaluate values for that expression. The simplest example is a program
which draws the graphic of a user-defined function on screen.
This library adds C-style function string parsing to the program. This
means that you can evaluate the string "sqrt(1-x^2+y^2)" with given values
of 'x' and 'y'.
The library is intended to be very fast. It byte-compiles the function
string at parse time and interpretes this byte-code at evaluation time.
The evaluation is straightforward and no recursions are done (uses stack
arithmetic).
Empirical tests show that it indeed is very fast (specially compared to
libraries which evaluate functions by just interpreting the raw function
string).
The library is made in ISO C++ and requires a standard-conforming C++
compiler.
=============================================================================
- Usage
=============================================================================
To use the FunctionParser class, you have to include "fparser.hh". When
compiling, you have to compile fparser.cc and link it to the main program.
You can also make a library from the fparser.cc (see the help on your
compiler to see how this is done).
* Conditional compiling:
---------------------
There is a set of precompiler options at the beginning of fparser.cc
which can be used for setting certain features on or off. These lines
can be commented or uncommented depending on the desired behaviour:
NO_ASINH : (Default on)
By default the library does not support the asinh(), acosh()
and atanh() functions because they are not part of the ISO C++
standard. If your compiler supports them and you want the
parser to support them as well, comment this line.
DISABLE_EVAL : (Default off)
The eval() function can be dangerous because it can cause an
infinite recursion in the parser when not used properly (which
causes the function stack created by the compiler to overflow).
If this possibility should be prevented then the eval() function
can be disabled completely by uncommenting this line.
SUPPORT_OPTIMIZER : (Default on)
If you are not going to use the Optimize() method, you can comment
this line out to speed-up the compilation of fparser.cc a bit, as
well as making the binary a bit smaller. (Optimize() can still be
called, but it will not do anything.)
* Copying and assignment:
----------------------
The class implements a safe copy constructor and assignment operator.
It uses the copy-on-write technique for efficiency. This means that
when copying or assigning a FunctionParser instance, the internal data
(which in some cases can be quite lengthy) is not immediately copied
but only when the contents of the copy (or the original) are changed.
This means that copying/assigning is a very fast operation, and if
the copies are never modified then actual data copying never happens
either.
The Eval() and EvalError() methods of the copy can be called without
the internal data being copied.
Calling Parse(), Optimize() or the user-defined constant/function adding
methods will cause a deep-copy.
(C++ basics: The copy constructor is called when a new FunctionParser
instance is initialized with another, ie. like:
FunctionParser fp2 = fp1; // or: FunctionParser fp2(fp1);
or when a function takes a FunctionParser instance as parameter, eg:
void foo(FunctionParser p) // takes an instance of FunctionParser
{ ... }
The assignment operator is called when a FunctionParser instance is
assigned to another, like "fp2 = fp1;".)
* Short descriptions of FunctionParser methods:
--------------------------------------------
int Parse(const std::string& Function, const std::string& Vars,
bool useDegrees = false);
Parses the given function and compiles it to internal format.
Return value is -1 if successful, else the index value to the location
of the error.
const char* ErrorMsg(void) const;
Returns an error message corresponding to the error in Parse(), or 0 if
no such error occurred.
ParseErrorType GetParseErrorType() const;
Returns the type of parsing error which occurred. Possible return types
are described in the long description.
double Eval(const double* Vars);
Evaluates the function given to Parse().
int EvalError(void) const;
Returns 0 if no error happened in the previous call to Eval(), else an
error code >0.
void Optimize();
Tries to optimize the bytecode for faster evaluation.
bool AddConstant(const std::string& name, double value);
Add a constant to the parser. Returns false if the name of the constant
is invalid, else true.
bool AddFunction(const std::string& name,
double (*functionPtr)(const double*),
unsigned paramsAmount);
Add a user-defined function to the parser (as a function pointer).
Returns false if the name of the function is invalid, else true.
bool AddFunction(const std::string& name, FunctionParser&);
Add a user-defined function to the parser (as a FunctionParser instance).
Returns false if the name of the function is invalid, else true.
* Long descriptions of FunctionParser methods:
-------------------------------------------
---------------------------------------------------------------------------
int Parse(const std::string& Function, const std::string& Vars,
bool useDegrees = false);
---------------------------------------------------------------------------
Parses the given function (and compiles it to internal format).
Destroys previous function. Following calls to Eval() will evaluate
the given function.
The strings given as parameters are not needed anymore after parsing.
Parameters:
Function : String containing the function to parse.
Vars : String containing the variable names, separated by commas.
Eg. "x,y", "VarX,VarY,VarZ,n" or "x1,x2,x3,x4,__VAR__".
useDegrees: (Optional.) Whether to use degrees or radians in
trigonometric functions. (Default: radians)
Variables can have any size and they are case sensitive (ie. "var",
"VAR" and "Var" are *different* variable names). Letters, digits and
underscores can be used in variable names, but the name of a variable
can't begin with a digit. Each variable name can appear only once in
the string. Function names are not legal variable names.
Using longer variable names causes no overhead whatsoever to the Eval()
method, so it's completely safe to use variable names of any size.
The third, optional parameter specifies whether angles should be
interpreted as radians or degrees in trigonometrical functions.
If not specified, the default value is radians.
Return values:
-On success the function returns -1.
-On error the function returns an index to where the error was found
(0 is the first character, 1 the second, etc). If the error was not
a parsing error returns an index to the end of the string + 1.
Example: parser.Parse("3*x+y", "x,y");
---------------------------------------------------------------------------
const char* ErrorMsg(void) const;
---------------------------------------------------------------------------
Returns a pointer to an error message string corresponding to the error
caused by Parse() (you can use this to print the proper error message to
the user). If no such error has occurred, returns 0.
---------------------------------------------------------------------------
ParseErrorType GetParseErrorType() const;
---------------------------------------------------------------------------
Returns the type of parse error which occurred.
This method can be used to get the error type if ErrorMsg() is not
enough for printing the error message. In other words, this can be
used for printing customized error messages (eg. in another language).
If the default error messages suffice, then this method doesn't need
to be called.
FunctionParser::ParseErrorType is an enumerated type inside the class
(ie. its values are accessed like "FunctionParser::SYNTAX_ERROR").
The possible values for FunctionParser::ParseErrorType are listed below,
along with their equivalent error message returned by the ErrorMsg()
method:
FP_NO_ERROR : If no error occurred in the previous call to Parse().
SYNTAX_ERROR : "Syntax error"
MISM_PARENTH : "Mismatched parenthesis"
MISSING_PARENTH : "Missing ')'"
EMPTY_PARENTH : "Empty parentheses"
EXPECT_OPERATOR : "Syntax error: Operator expected"
OUT_OF_MEMORY : "Not enough memory"
UNEXPECTED_ERROR : "An unexpected error ocurred. Please make a full bug "
"report to warp@iki.fi"
INVALID_VARS : "Syntax error in parameter 'Vars' given to "
"FunctionParser::Parse()"
ILL_PARAMS_AMOUNT : "Illegal number of parameters to function"
PREMATURE_EOS : "Syntax error: Premature end of string"
EXPECT_PARENTH_FUNC: "Syntax error: Expecting ( after function"
---------------------------------------------------------------------------
double Eval(const double* Vars);
---------------------------------------------------------------------------
Evaluates the function given to Parse().
The array given as parameter must contain the same amount of values as
the amount of variables given to Parse(). Each value corresponds to each
variable, in the same order.
Return values:
-On success returns the evaluated value of the function given to
Parse().
-On error (such as division by 0) the return value is unspecified,
probably 0.
Example:
double Vars[] = {1, -2.5};
double result = parser.Eval(Vars);
---------------------------------------------------------------------------
int EvalError(void) const;
---------------------------------------------------------------------------
Used to test if the call to Eval() succeeded.
Return values:
If there was no error in the previous call to Eval(), returns 0,
else returns a positive value as follows:
1: division by zero
2: sqrt error (sqrt of a negative value)
3: log error (logarithm of a negative value)
4: trigonometric error (asin or acos of illegal value)
---------------------------------------------------------------------------
void Optimize();
---------------------------------------------------------------------------
This method can be called after calling the Parse() method. It will try
to simplify the internal bytecode so that it will evaluate faster (it
tries to reduce the amount of opcodes in the bytecode).
For example, the bytecode for the function "5+x*y-25*4/8" will be
reduced to a bytecode equivalent to the function "x*y-7.5" (the original
11 opcodes will be reduced to 5). Besides calculating constant expressions
(like in the example), it also performs other types of simplifications
with variable and function expressions.
This method is quite slow and the decision of whether to use it or
not should depend on the type of application. If a function is parsed
once and evaluated millions of times, then calling Optimize() may speed-up
noticeably. However, if there are tons of functions to parse and each one
is evaluated once or just a few times, then calling Optimize() will only
slow down the program.
Also, if the original function is expected to be optimal, then calling
Optimize() would be useless.
Note: Currently this method does not make any checks (like Eval() does)
and thus things like "1/0" will cause undefined behaviour. (On the other
hand, if such expression is given to the parser, Eval() will always give
an error code, no matter what the parameters.) If caching this type of
errors is important, a work-around is to call Eval() once before calling
Optimize() and checking EvalError().
If the destination application is not going to use this method,
the compiler constant SUPPORT_OPTIMIZER can be undefined at the beginning
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -