main函数是我写滴。愿大家少造轮子。
//函数表达式字符串,x,y变量值
void calculate2V(const string & funcStr, double x, double y)
{
FunctionParser fp;
//【1】pass function expression string for c style
//【2】pass variate , split by comma
int ret = fp.Parse(funcStr.c_str(), "x,y");
if(ret >= 0)
{
std::cerr << "At col " << ret << ": " << fp.ErrorMsg() << std::endl;
return ;
}
double vals[] = { x, y};
cout << funcStr << " = " << fp.Eval(vals) << endl;
}
void calculate3V(const string & funcStr, double x, double y, double z)
{
FunctionParser fp;
//【1】pass function expression string for c style
//【2】pass variate , split by comma
int ret = fp.Parse(funcStr.c_str(), "x,y,z");
if(ret >= 0)
{
std::cerr << "At col " << ret << ": " << fp.ErrorMsg() << std::endl;
return ;
}
double vals[] = { x, y, z};
cout << funcStr << " = " << fp.Eval(vals) << endl;
}
int main()
{
calculate2V("x+y-1", 4, 8);
calculate2V("x*y", 2, 3);
calculate2V("sin(x)+y", 3.1415926/2, 3);
calculate3V("x+y*z", 2, 3,5);
//calculate2V("x*y", 2, 3);
//calculate2V("x*y", 2, 3);
system("pause");
}
int Parse(const std::string& Function, const std::string& Vars,
bool useDegrees = false);
const char* ErrorMsg() const;
inline ParseErrorType GetParseErrorType() const { return parseErrorType; }
double Eval(const double* Vars);
inline int EvalError() const { return evalErrorType; }
bool AddConstant(const std::string& name, double value);
typedef double (*FunctionPtr)(const double*);
bool AddFunction(const std::string& name,
FunctionPtr, unsigned paramsAmount);
bool AddFunction(const std::string& name, FunctionParser&);
void Optimize();
FunctionParser();
~FunctionParser();
// Copy constructor and assignment operator (implemented using the
// copy-on-write technique for efficiency):
FunctionParser(const FunctionParser&);
FunctionParser& operator=(const FunctionParser&);
#ifdef FUNCTIONPARSER_SUPPORT_DEBUG_OUTPUT
// For debugging purposes only:
void PrintByteCode(std::ostream& dest) const;
#endif
//========================================================================
private:
//========================================================================
// Private data:
// ------------
ParseErrorType parseErrorType;
int evalErrorType;
struct Data
{
unsigned referenceCounter;
int varAmount;
bool useDegreeConversion;
typedef std::map<std::string, unsigned> VarMap_t;
VarMap_t Variables;
typedef std::map<std::string, double> ConstMap_t;
ConstMap_t Constants;
VarMap_t FuncPtrNames;
struct FuncPtrData
{
FunctionPtr ptr; unsigned params;
FuncPtrData(FunctionPtr p, unsigned par): ptr(p), params(par) {}
};
std::vector<FuncPtrData> FuncPtrs;
VarMap_t FuncParserNames;
std::vector<FunctionParser*> FuncParsers;
unsigned* ByteCode;
unsigned ByteCodeSize;
double* Immed;
unsigned ImmedSize;
double* Stack;
unsigned StackSize;
Data();
~Data();
Data(const Data&);
Data& operator=(const Data&); // not implemented on purpose
};
Data* data;
// Temp data needed in Compile():
unsigned StackPtr;
std::vector<unsigned>* tempByteCode;
std::vector<double>* tempImmed;
// Private methods:
// ---------------
inline void copyOnWrite();
bool checkRecursiveLinking(const FunctionParser*) const;
bool isValidName(const std::string&) const;
Data::VarMap_t::const_iterator FindVariable(const char*,
const Data::VarMap_t&) const;
Data::ConstMap_t::const_iterator FindConstant(const char*) const;
int CheckSyntax(const char*);
bool Compile(const char*);
bool IsVariable(int);
void AddCompiledByte(unsigned);
void AddImmediate(double);
void AddFunctionOpcode(unsigned);
inline void incStackPtr();
int CompileIf(const char*, int);
int CompileFunctionParams(const char*, int, unsigned);
int CompileElement(const char*, int);
int CompilePow(const char*, int);
int CompileUnaryMinus(const char*, int);
int CompileMult(const char*, int);
int CompileAddition(const char*, int);
int CompileComparison(const char*, int);
int CompileAnd(const char*, int);
int CompileOr(const char*, int);
int CompileExpression(const char*, int, bool=false);
void MakeTree(void*) const;
};
#endif
/***************************************************************************
* Copyright (C) 2005 by Warp *
* *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
//==============================
// Function parser v2.7 by Warp
//==============================
// Comment out the following line if your compiler supports the (non-standard)
// asinh, acosh and atanh functions and you want them to be supported. If
// you are not sure, just leave it (those function will then not be supported).
#define NO_ASINH
// Uncomment the following line to disable the eval() function if it could
// be too dangerous in the target application:
//#define DISABLE_EVAL
// Comment this line out if you are not going to use the optimizer and want
// a slightly smaller library. The Optimize() method can still be called,
// but it will not do anything.
// If you are unsure, just leave it. It won't slow down the other parts of
// the library.
#define SUPPORT_OPTIMIZER
//============================================================================
#include "fparser.h"
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
using namespace std;
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
namespace
{
// The functions must be in alphabetical order:
enum OPCODE
{
cAbs, cAcos,
#ifndef NO_ASINH
cAcosh,
#endif
cAsin,
#ifndef NO_ASINH
cAsinh,
#endif
cAtan,
cAtan2,
#ifndef NO_ASINH
cAtanh,
#endif
cCeil, cCos, cCosh, cCot, cCsc,
#ifndef DISABLE_EVAL
cEval,
#endif
cExp, cFloor, cIf, cInt, cLog, cLog10, cMax, cMin,
cSec, cSin, cSinh, cSqrt, cTan, cTanh,
// These do not need any ordering:
cImmed, cJump,
cNeg, cAdd, cSub, cMul, cDiv, cMod, cPow,
cEqual, cLess, cGreater, cAnd, cOr,
cDeg, cRad,
cFCall, cPCall,
#ifdef SUPPORT_OPTIMIZER
cVar, cDup, cInv,
#endif
VarBegin
};
struct FuncDefinition
{
const char* name;
unsigned nameLength;
unsigned opcode;
unsigned params;
// This is basically strcmp(), but taking 'nameLength' as string
// length (not ending '/0'):
bool operator<(const FuncDefinition& rhs) const
{
for(unsigned i = 0; i < nameLength; ++i)
{
if(i == rhs.nameLength) return false;
const char c1 = name[i], c2 = rhs.name[i];
if(c1 < c2) return true;
if(c2 < c1) return false;
}
return nameLength < rhs.nameLength;
}
};
// This list must be in alphabetical order:
const FuncDefinition Functions[]=
{
{ "abs", 3, cAbs, 1 },
{ "acos", 4, cAcos, 1 },
#ifndef NO_ASINH
{ "acosh", 5, cAcosh, 1 },
#endif
{ "asin", 4, cAsin, 1 },
#ifndef NO_ASINH
{ "asinh", 5, cAsinh, 1 },
#endif
{ "atan", 4, cAtan, 1 },
{ "atan2", 5, cAtan2, 2 },
#ifndef NO_ASINH
{ "atanh", 5, cAtanh, 1 },
#endif
{ "ceil", 4, cCeil, 1 },
{ "cos", 3, cCos, 1 },
{ "cosh", 4, cCosh, 1 },
{ "cot", 3, cCot, 1 },
{ "csc", 3, cCsc, 1 },
#ifndef DISABLE_EVAL
{ "eval", 4, cEval, 0 },
#endif
{ "exp", 3, cExp, 1 },
{ "floor", 5, cFloor, 1 },
{ "if", 2, cIf, 0 },
{ "int", 3, cInt, 1 },
{ "log", 3, cLog, 1 },
{ "log10", 5, cLog10, 1 },
{ "max", 3, cMax, 2 },
{ "min", 3, cMin, 2 },
{ "sec", 3, cSec, 1 },
{ "sin", 3, cSin, 1 },
{ "sinh", 4, cSinh, 1 },
{ "sqrt", 4, cSqrt, 1 },
{ "tan", 3, cTan, 1 },
{ "tanh", 4, cTanh, 1 }
};
const unsigned FUNC_AMOUNT = sizeof(Functions)/sizeof(Functions[0]);
// BCB4 does not implement the standard lower_bound function.
// This is used instead:
const FuncDefinition* fp_lower_bound(const FuncDefinition* first,
const FuncDefinition* last,
const FuncDefinition& value)
{
while(first < last)
{
const FuncDefinition* middle = first+(last-first)/2;
if(*middle < value) first = middle+1;
else last = middle;
}
return last;
}
// Returns a pointer to the FuncDefinition instance which 'name' is
// the same as the one given by 'F'. If no such function name exists,
// returns 0.
inline const FuncDefinition* FindFunction(const char* F)
{
FuncDefinition func = { F, 0, 0, 0 };
while(isalnum(F[func.nameLength])) ++func.nameLength;
if(func.nameLength)
{
const FuncDefinition* found =
fp_lower_bound(Functions, Functions+FUNC_AMOUNT, func);
if(found == Functions+FUNC_AMOUNT || func < *found)
return 0;
return found;
}
return 0;
}
};
//---------------------------------------------------------------------------
// Copy-on-write method
//---------------------------------------------------------------------------
inline void FunctionParser::copyOnWrite()
{
if(data->referenceCounter > 1)
{
Data* oldData = data;
data = new Data(*oldData);
--(oldData->referenceCounter);
data->referenceCounter = 1;
}
}
//---------------------------------------------------------------------------
// Constructors and destructors
//---------------------------------------------------------------------------
//===========================================================================
FunctionParser::FunctionParser():
parseErrorType(FP_NO_ERROR), evalErrorType(0),
data(new Data)
{
data->referenceCounter = 1;
}
FunctionParser::~FunctionParser()
{
if(--(data->referenceCounter) == 0)
{
delete data;
}
}
FunctionParser::FunctionParser(const FunctionParser& cpy):
parseErrorType(cpy.parseErrorType),
evalErrorType(cpy.evalErrorType),
data(cpy.data)
{
++(data->referenceCounter);
}
FunctionParser& FunctionParser::operator=(const FunctionParser& cpy)
{
if(data != cpy.data)
{
if(--(data->referenceCounter) == 0) delete data;
parseErrorType = cpy.parseErrorType;
evalErrorType = cpy.evalErrorType;
data = cpy.data;
++(data->referenceCounter);
}
return *this;
}
FunctionParser::Data::Data():
useDegreeConversion(false),
ByteCode(0), ByteCodeSize(0),
Immed(0), ImmedSize(0),
Stack(0), StackSize(0)
{}
FunctionParser::Data::~Data()
{
if(ByteCode) { delete[] ByteCode; ByteCode=0; }
if(Immed) { delete[] Immed; Immed=0; }
if(Stack) { delete[] Stack; Stack=0; }
}
// Makes a deep-copy of Data:
FunctionParser::Data::Data(const Data& cpy):
varAmount(cpy.varAmount), useDegreeConversion(cpy.useDegreeConversion),
Variables(cpy.Variables), Constants(cpy.Constants),
FuncPtrNames(cpy.FuncPtrNames), FuncPtrs(cpy.FuncPtrs),
FuncParserNames(cpy.FuncParserNames), FuncParsers(cpy.FuncParsers),
ByteCode(0), ByteCodeSize(cpy.ByteCodeSize),
Immed(0), ImmedSize(cpy.ImmedSize),
Stack(0), StackSize(cpy.StackSize)
{
if(ByteCodeSize) ByteCode = new unsigned[ByteCodeSize];
if(ImmedSize) Immed = new double[ImmedSize];
if(StackSize) Stack = new double[StackSize];
for(unsigned i=0; i<ByteCodeSize; ++i) ByteCode[i] = cpy.ByteCode[i];
for(unsigned i=0; i<ImmedSize; ++i) Immed[i] = cpy.Immed[i];
// No need to copy the stack contents because it's obsolete outside Eval()
}
//---------------------------------------------------------------------------
// Function parsing
//---------------------------------------------------------------------------
//===========================================================================
namespace
{
// Error messages returned by ErrorMsg():
const char* ParseErrorMessage[]=
{
"Syntax error", // 0
"Mismatched parenthesis", // 1
"Missing ')'", // 2
"Empty parentheses", // 3
"Syntax error: Operator expected", // 4
"Not enough memory", // 5
"An unexpected error ocurred. Please make a full bug report "
"to warp@iki.fi", // 6
"Syntax error in parameter 'Vars' given to "
"FunctionParser::Parse()", // 7
"Illegal number of parameters to function", // 8
"Syntax error: Premature end of string", // 9
"Syntax error: Expecting ( after function", // 10
""
};
// Parse variables
bool ParseVars(const string& Vars, map<string, unsigned>& dest)
{
unsigned varNumber = VarBegin;
unsigned ind1 = 0, ind2;
while(ind1 < Vars.size())
{
if(!isalpha(Vars[ind1]) && Vars[ind1]!='_') return false;
for(ind2=ind1+1; ind2<Vars.size() && Vars[ind2]!=','; ++ind2)
if(!isalnum(Vars[ind2]) && Vars[ind2]!='_') return false;
const string varName = Vars.substr(ind1, ind2-ind1);
if(dest.insert(make_pair(varName, varNumber++)).second == false)
return false;
ind1 = ind2+1;
}
return true;
}
};
bool FunctionParser::isValidName(const std::string& name) const
{
if(name.empty() || (!isalpha(name[0]) && name[0] != '_')) return false;
for(unsigned i=0; i<name.size(); ++i)
if(!isalnum(name[i]) && name[i] != '_') return false;
if(FindFunction(name.c_str())) return false;
return true;
}
// Constants:
bool FunctionParser::AddConstant(const string& name, double value)
{
if(isValidName(name))
{
const char* n = name.c_str();
if(FindVariable(n, data->FuncParserNames) !=
data->FuncParserNames.end() ||
FindVariable(n, data->FuncPtrNames) !=
data->FuncPtrNames.end())
return false;
copyOnWrite();
data->Constants[name] = value;
return true;
}
return false;
}
// Function pointers
bool FunctionParser::AddFunction(const std::string& name,
FunctionPtr func, unsigned paramsAmount)
{
if(paramsAmount == 0) return false; // Currently must be at least one
if(isValidName(name))
{
const char* n = name.c_str();
if(FindVariable(n, data->FuncParserNames) !=
data->FuncParserNames.end() ||
FindConstant(n) != data->Constants.end())
return false;
copyOnWrite();
data->FuncPtrNames[name] = data->FuncPtrs.size();
data->FuncPtrs.push_back(Data::FuncPtrData(func, paramsAmount));
return true;
}
return false;
}
bool FunctionParser::checkRecursiveLinking(const FunctionParser* fp) const
{
if(fp == this) return true;
for(unsigned i=0; i<fp->data->FuncParsers.size(); ++i)
if(checkRecursiveLinking(fp->data->FuncParsers[i])) return true;
return false;
}
bool FunctionParser::AddFunction(const std::string& name,
FunctionParser& parser)
{
if(parser.data->varAmount == 0) // Currently must be at least one
return false;
if(isValidName(name))
{
const char* n = name.c_str();
if(FindVariable(n, data->FuncPtrNames) != data->FuncPtrNames.end() ||
FindConstant(n) != data->Constants.end())
return false;
if(checkRecursiveLinking(&parser)) return false;
copyOnWrite();
data->FuncParserNames[name] = data->FuncParsers.size();
data->FuncParsers.push_back(&parser);
return true;
}
return false;
}
// Main parsing function
// ---------------------
int FunctionParser::Parse(const std::string& Function,
const std::string& Vars,
bool useDegrees)
{
copyOnWrite();
data->Variables.clear();
if(!ParseVars(Vars, data->Variables))
{
parseErrorType = INVALID_VARS;
return Function.size();
}
data->varAmount = data->Variables.size(); // this is for Eval()
const char* Func = Function.c_str();
parseErrorType = FP_NO_ERROR;
int Result = CheckSyntax(Func);
if(Result>=0) return Result;
data->useDegreeConversion = useDegrees;
if(!Compile(Func)) return Function.size();
data->Variables.clear();
parseErrorType = FP_NO_ERROR;
return -1;
}
namespace
{
// Is given char an operator?
inline bool IsOperator(int c)
{
return strchr("+-*/%^=<>&|",c)!=NULL;
}
// skip whitespace
inline void sws(const char* F, int& Ind)
{
while(F[Ind] && isspace(F[Ind])) ++Ind;
}
};
// Returns an iterator to the variable with the same name as 'F', or to
// Variables.end() if no such variable exists:
inline FunctionParser::Data::VarMap_t::const_iterator
FunctionParser::FindVariable(const char* F, const Data::VarMap_t& vars) const
{
if(vars.size())
{
unsigned ind = 0;
while(isalnum(F[ind]) || F[ind] == '_') ++ind;
if(ind)
{
string name(F, ind);
return vars.find(name);
}
}
return vars.end();
}
inline FunctionParser::Data::ConstMap_t::const_iterator
FunctionParser::FindConstant(const char* F) const
{
if(data->Constants.size())
{
unsigned ind = 0;
while(isalnum(F[ind]) || F[ind] == '_') ++ind;
if(ind)
{
string name(F, ind);
return data->Constants.find(name);
}
}
return data->Constants.end();
}
//---------------------------------------------------------------------------
// Check function string syntax
// ----------------------------
int FunctionParser::CheckSyntax(const char* Function)
{
const Data::VarMap_t& Variables = data->Variables;
const Data::ConstMap_t& Constants = data->Constants;
const Data::VarMap_t& FuncPtrNames = data->FuncPtrNames;
const Data::VarMap_t& FuncParserNames = data->FuncParserNames;
vector<int> functionParenthDepth;
int Ind=0, ParenthCnt=0, c;
char* Ptr;
while(true)
{
sws(Function, Ind);
c=Function[Ind];
// Check for valid operand (must appear)
// Check for leading -
if(c=='-') { sws(Function, ++Ind); c=Function[Ind]; }
if(c==0) { parseErrorType=PREMATURE_EOS; return Ind; }
// Check for math function
bool foundFunc = false;
const FuncDefinition* fptr = FindFunction(&Function[Ind]);
if(fptr)
{
Ind += fptr->nameLength;
foundFunc = true;
}
else
{
// Check for user-defined function
Data::VarMap_t::const_iterator fIter =
FindVariable(&Function[Ind], FuncPtrNames);
if(fIter != FuncPtrNames.end())
{
Ind += fIter->first.size();
foundFunc = true;
}
else
{
Data::VarMap_t::const_iterator pIter =
FindVariable(&Function[Ind], FuncParserNames);
if(pIter != FuncParserNames.end())
{
Ind += pIter->first.size();
foundFunc = true;
}
}
}
if(foundFunc)
{
sws(Function, Ind);
c = Function[Ind];
if(c!='(') { parseErrorType=EXPECT_PARENTH_FUNC; return Ind; }
functionParenthDepth.push_back(ParenthCnt+1);
}
// Check for opening parenthesis
if(c=='(')
{
++ParenthCnt;
sws(Function, ++Ind);
if(Function[Ind]==')') { parseErrorType=EMPTY_PARENTH; return Ind;}
continue;
}
// Check for number
if(isdigit(c) || (c=='.' && isdigit(Function[Ind+1])))
{
strtod(&Function[Ind], &Ptr);
Ind += int(Ptr-&Function[Ind]);
sws(Function, Ind);
c = Function[Ind];
}
else
{ // Check for variable
Data::VarMap_t::const_iterator vIter =
FindVariable(&Function[Ind], Variables);
if(vIter != Variables.end())
Ind += vIter->first.size();
else
{
// Check for constant
Data::ConstMap_t::const_iterator cIter =
FindConstant(&Function[Ind]);
if(cIter != Constants.end())
Ind += cIter->first.size();
else
{ parseErrorType=SYNTAX_ERROR; return Ind; }
}
sws(Function, Ind);
c = Function[Ind];
}
// Check for closing parenthesis
while(c==')')
{
if(functionParenthDepth.size() &&
functionParenthDepth.back() == ParenthCnt)
functionParenthDepth.pop_back();
if((--ParenthCnt)<0) { parseErrorType=MISM_PARENTH; return Ind; }
sws(Function, ++Ind);
c=Function[Ind];
}
// If we get here, we have a legal operand and now a legal operator or
// end of string must follow
// Check for EOS
if(c==0) break; // The only way to end the checking loop without error
// Check for operator
if(!IsOperator(c) &&
(c != ',' || functionParenthDepth.empty() ||
functionParenthDepth.back() != ParenthCnt))
{ parseErrorType=EXPECT_OPERATOR; return Ind; }
// If we get here, we have an operand and an operator; the next loop will
// check for another operand (must appear)
++Ind;
} // while
// Check that all opened parentheses are also closed
if(ParenthCnt>0) { parseErrorType=MISSING_PARENTH; return Ind; }
// The string is ok
parseErrorType=FP_NO_ERROR;
return -1;
}
// Compile function string to bytecode
// -----------------------------------
bool FunctionParser::Compile(const char* Function)
{
if(data->ByteCode) { delete[] data->ByteCode; data->ByteCode=0; }
if(data->Immed) { delete[] data->Immed; data->Immed=0; }
if(data->Stack) { delete[] data->Stack; data->Stack=0; }
vector<unsigned> byteCode; byteCode.reserve(1024);
tempByteCode = &byteCode;
vector<double> immed; immed.reserve(1024);
tempImmed = &immed;
data->StackSize = StackPtr = 0;
CompileExpression(Function, 0);
if(parseErrorType != FP_NO_ERROR) return false;
data->ByteCodeSize = byteCode.size();
data->ImmedSize = immed.size();
if(data->ByteCodeSize)
{
data->ByteCode = new unsigned[data->ByteCodeSize];
memcpy(data->ByteCode, &byteCode[0],
sizeof(unsigned)*data->ByteCodeSize);
}
if(data->ImmedSize)
{
data->Immed = new double[data->ImmedSize];
memcpy(data->Immed, &immed[0],
sizeof(double)*data->ImmedSize);
}
if(data->StackSize)
data->Stack = new double[data->StackSize];
return true;
}
inline void FunctionParser::AddCompiledByte(unsigned c)
{
tempByteCode->push_back(c);
}
inline void FunctionParser::AddImmediate(double i)
{
tempImmed->push_back(i);
}
inline void FunctionParser::AddFunctionOpcode(unsigned opcode)
{
if(data->useDegreeConversion)
switch(opcode)
{
case cCos:
case cCosh:
case cCot:
case cCsc:
case cSec:
case cSin:
case cSinh:
case cTan:
case cTanh:
AddCompiledByte(cRad);
}
AddCompiledByte(opcode);
if(data->useDegreeConversion)
switch(opcode)
{
case cAcos:
#ifndef NO_ASINH
case cAcosh:
case cAsinh:
case cAtanh:
#endif
case cAsin:
case cAtan:
case cAtan2:
AddCompiledByte(cDeg);
}
}
inline void FunctionParser::incStackPtr()
{
if(++StackPtr > data->StackSize) ++(data->StackSize);
}
// Compile if()
int FunctionParser::CompileIf(const char* F, int ind)
{
int ind2 = CompileExpression(F, ind, true); // condition
sws(F, ind2);
if(F[ind2] != ',') { parseErrorType=ILL_PARAMS_AMOUNT; return ind2; }
AddCompiledByte(cIf);
unsigned curByteCodeSize = tempByteCode->size();
AddCompiledByte(0); // Jump index; to be set later
AddCompiledByte(0); // Immed jump index; to be set later
--StackPtr;
ind2 = CompileExpression(F, ind2+1, true); // then
sws(F, ind2);
if(F[ind2] != ',') { parseErrorType=ILL_PARAMS_AMOUNT; return ind2; }
AddCompiledByte(cJump);
unsigned curByteCodeSize2 = tempByteCode->size();
unsigned curImmedSize2 = tempImmed->size();
AddCompiledByte(0); // Jump index; to be set later
AddCompiledByte(0); // Immed jump index; to be set later
--StackPtr;
ind2 = CompileExpression(F, ind2+1, true); // else
sws(F, ind2);
if(F[ind2] != ')') { parseErrorType=ILL_PARAMS_AMOUNT; return ind2; }
// Set jump indices
(*tempByteCode)[curByteCodeSize] = curByteCodeSize2+1;
(*tempByteCode)[curByteCodeSize+1] = curImmedSize2;
(*tempByteCode)[curByteCodeSize2] = tempByteCode->size()-1;
(*tempByteCode)[curByteCodeSize2+1] = tempImmed->size();
return ind2+1;
}
int FunctionParser::CompileFunctionParams(const char* F, int ind,
unsigned requiredParams)
{
unsigned curStackPtr = StackPtr;
int ind2 = CompileExpression(F, ind);
if(StackPtr != curStackPtr+requiredParams)
{ parseErrorType=ILL_PARAMS_AMOUNT; return ind; }
StackPtr -= requiredParams - 1;
sws(F, ind2);
return ind2+1; // F[ind2] is ')'
}
// Compiles element
int FunctionParser::CompileElement(const char* F, int ind)
{
sws(F, ind);
char c = F[ind];
if(c == '(')
{
ind = CompileExpression(F, ind+1);
sws(F, ind);
return ind+1; // F[ind] is ')'
}
if(isdigit(c) || c=='.' /*|| c=='-'*/) // Number
{
const char* startPtr = &F[ind];
char* endPtr;
double val = strtod(startPtr, &endPtr);
AddImmediate(val);
AddCompiledByte(cImmed);
incStackPtr();
return ind+(endPtr-startPtr);
}
if(isalpha(c) || c == '_') // Function, variable or constant
{
const FuncDefinition* func = FindFunction(F+ind);
if(func) // is function
{
int ind2 = ind + func->nameLength;
sws(F, ind2); // F[ind2] is '('
if(strcmp(func->name, "if") == 0) // "if" is a special case
{
return CompileIf(F, ind2+1);
}
#ifndef DISABLE_EVAL
unsigned requiredParams =
strcmp(func->name, "eval") == 0 ?
data->Variables.size() : func->params;
#else
unsigned requiredParams = func->params;
#endif
ind2 = CompileFunctionParams(F, ind2+1, requiredParams);
AddFunctionOpcode(func->opcode);
return ind2; // F[ind2-1] is ')'
}
Data::VarMap_t::const_iterator vIter =
FindVariable(F+ind, data->Variables);
if(vIter != data->Variables.end()) // is variable
{
AddCompiledByte(vIter->second);
incStackPtr();
return ind + vIter->first.size();
}
Data::ConstMap_t::const_iterator cIter = FindConstant(F+ind);
if(cIter != data->Constants.end()) // is constant
{
AddImmediate(cIter->second);
AddCompiledByte(cImmed);
incStackPtr();
return ind + cIter->first.size();
}
Data::VarMap_t::const_iterator fIter =
FindVariable(F+ind, data->FuncPtrNames);
if(fIter != data->FuncPtrNames.end()) // is user-defined func pointer
{
unsigned index = fIter->second;
int ind2 = ind + fIter->first.length();
sws(F, ind2); // F[ind2] is '('
ind2 = CompileFunctionParams(F, ind2+1,
data->FuncPtrs[index].params);
AddCompiledByte(cFCall);
AddCompiledByte(index);
return ind2;
}
Data::VarMap_t::const_iterator pIter =
FindVariable(F+ind, data->FuncParserNames);
if(pIter != data->FuncParserNames.end()) // is user-defined func parser
{
unsigned index = pIter->second;
int ind2 = ind + pIter->first.length();
sws(F, ind2); // F[ind2] is '('
ind2 = CompileFunctionParams
(F, ind2+1, data->FuncParsers[index]->data->varAmount);
AddCompiledByte(cPCall);
AddCompiledByte(index);
return ind2;
}
}
parseErrorType = UNEXPECTED_ERROR;
return ind;
}
// Compiles '^'
int FunctionParser::CompilePow(const char* F, int ind)
{
int ind2 = CompileElement(F, ind);
sws(F, ind2);
while(F[ind2] == '^')
{
ind2 = CompileUnaryMinus(F, ind2+1);
sws(F, ind2);
AddCompiledByte(cPow);
--StackPtr;
}
return ind2;
}
// Compiles unary '-'
int FunctionParser::CompileUnaryMinus(const char* F, int ind)
{
sws(F, ind);
if(F[ind] == '-')
{
int ind2 = ind+1;
sws(F, ind2);
ind2 = CompilePow(F, ind2);
sws(F, ind2);
// if we are negating a constant, negate the constant itself:
if(tempByteCode->back() == cImmed)
tempImmed->back() = -tempImmed->back();
// if we are negating a negation, we can remove both:
else if(tempByteCode->back() == cNeg)
tempByteCode->pop_back();
else
AddCompiledByte(cNeg);
return ind2;
}
int ind2 = CompilePow(F, ind);
sws(F, ind2);
return ind2;
}
// Compiles '*', '/' and '%'
int FunctionParser::CompileMult(const char* F, int ind)
{
int ind2 = CompileUnaryMinus(F, ind);
sws(F, ind2);
char op;
while((op = F[ind2]) == '*' || op == '/' || op == '%')
{
ind2 = CompileUnaryMinus(F, ind2+1);
sws(F, ind2);
switch(op)
{
case '*': AddCompiledByte(cMul); break;
case '/': AddCompiledByte(cDiv); break;
case '%': AddCompiledByte(cMod); break;
}
--StackPtr;
}
return ind2;
}
// Compiles '+' and '-'
int FunctionParser::CompileAddition(const char* F, int ind)
{
int ind2 = CompileMult(F, ind);
sws(F, ind2);
char op;
while((op = F[ind2]) == '+' || op == '-')
{
ind2 = CompileMult(F, ind2+1);
sws(F, ind2);
AddCompiledByte(op=='+' ? cAdd : cSub);
--StackPtr;
}
return ind2;
}
// Compiles '=', '<' and '>'
int FunctionParser::CompileComparison(const char* F, int ind)
{
int ind2 = CompileAddition(F, ind);
sws(F, ind2);
char op;
while((op = F[ind2]) == '=' || op == '<' || op == '>')
{
ind2 = CompileAddition(F, ind2+1);
sws(F, ind2);
switch(op)
{
case '=': AddCompiledByte(cEqual); break;
case '<': AddCompiledByte(cLess); break;
case '>': AddCompiledByte(cGreater); break;
}
--StackPtr;
}
return ind2;
}
// Compiles '&'
int FunctionParser::CompileAnd(const char* F, int ind)
{
int ind2 = CompileComparison(F, ind);
sws(F, ind2);
while(F[ind2] == '&')
{
ind2 = CompileComparison(F, ind2+1);
sws(F, ind2);
AddCompiledByte(cAnd);
--StackPtr;
}
return ind2;
}
// Compiles '|'
int FunctionParser::CompileOr(const char* F, int ind)
{
int ind2 = CompileAnd(F, ind);
sws(F, ind2);
while(F[ind2] == '|')
{
ind2 = CompileAnd(F, ind2+1);
sws(F, ind2);
AddCompiledByte(cOr);
--StackPtr;
}
return ind2;
}
// Compiles ','
int FunctionParser::CompileExpression(const char* F, int ind, bool stopAtComma)
{
int ind2 = CompileOr(F, ind);
sws(F, ind2);
if(stopAtComma) return ind2;
while(F[ind2] == ',')
{
ind2 = CompileOr(F, ind2+1);
sws(F, ind2);
}
return ind2;
}
// Return parse error message
// --------------------------
const char* FunctionParser::ErrorMsg() const
{
if(parseErrorType != FP_NO_ERROR) return ParseErrorMessage[parseErrorType];
return 0;
}
//---------------------------------------------------------------------------
// Function evaluation
//---------------------------------------------------------------------------
//===========================================================================
namespace
{
inline int doubleToInt(double d)
{
return d<0 ? -int((-d)+.5) : int(d+.5);
}
inline double Min(double d1, double d2)
{
return d1<d2 ? d1 : d2;
}
inline double Max(double d1, double d2)
{
return d1>d2 ? d1 : d2;
}
inline double DegreesToRadians(double degrees)
{
return degrees*(M_PI/180.0);
}
inline double RadiansToDegrees(double radians)
{
return radians*(180.0/M_PI);
}
}
double FunctionParser::Eval(const double* Vars)
{
const unsigned* const ByteCode = data->ByteCode;
const double* const Immed = data->Immed;
double* const Stack = data->Stack;
const unsigned ByteCodeSize = data->ByteCodeSize;
unsigned IP, DP=0;
int SP=-1;
for(IP=0; IP<ByteCodeSize; ++IP)
{
switch(ByteCode[IP])
{
// Functions:
case cAbs: Stack[SP] = fabs(Stack[SP]); break;
case cAcos: if(Stack[SP] < -1 || Stack[SP] > 1)
{ evalErrorType=4; return 0; }
Stack[SP] = acos(Stack[SP]); break;
#ifndef NO_ASINH
case cAcosh: Stack[SP] = acosh(Stack[SP]); break;
#endif
case cAsin: if(Stack[SP] < -1 || Stack[SP] > 1)
{ evalErrorType=4; return 0; }
Stack[SP] = asin(Stack[SP]); break;
#ifndef NO_ASINH
case cAsinh: Stack[SP] = asinh(Stack[SP]); break;
#endif
case cAtan: Stack[SP] = atan(Stack[SP]); break;
case cAtan2: Stack[SP-1] = atan2(Stack[SP-1], Stack[SP]);
--SP; break;
#ifndef NO_ASINH
case cAtanh: Stack[SP] = atanh(Stack[SP]); break;
#endif
case cCeil: Stack[SP] = ceil(Stack[SP]); break;
case cCos: Stack[SP] = cos(Stack[SP]); break;
case cCosh: Stack[SP] = cosh(Stack[SP]); break;
case cCot:
{
double t = tan(Stack[SP]);
if(t == 0) { evalErrorType=1; return 0; }
Stack[SP] = 1/t; break;
}
case cCsc:
{
double s = sin(Stack[SP]);
if(s == 0) { evalErrorType=1; return 0; }
Stack[SP] = 1/s; break;
}
#ifndef DISABLE_EVAL
case cEval:
{
data->Stack = new double[data->StackSize];
double retVal = Eval(&Stack[SP-data->varAmount+1]);
delete[] data->Stack;
data->Stack = Stack;
SP -= data->varAmount-1;
Stack[SP] = retVal;
break;
}
#endif
case cExp: Stack[SP] = exp(Stack[SP]); break;
case cFloor: Stack[SP] = floor(Stack[SP]); break;
case cIf:
{
unsigned jumpAddr = ByteCode[++IP];
unsigned immedAddr = ByteCode[++IP];
if(doubleToInt(Stack[SP]) == 0)
{
IP = jumpAddr;
DP = immedAddr;
}
--SP; break;
}
case cInt: Stack[SP] = floor(Stack[SP]+.5); break;
case cLog: if(Stack[SP] <= 0) { evalErrorType=3; return 0; }
Stack[SP] = log(Stack[SP]); break;
case cLog10: if(Stack[SP] <= 0) { evalErrorType=3; return 0; }
Stack[SP] = log10(Stack[SP]); break;
case cMax: Stack[SP-1] = Max(Stack[SP-1], Stack[SP]);
--SP; break;
case cMin: Stack[SP-1] = Min(Stack[SP-1], Stack[SP]);
--SP; break;
case cSec:
{
double c = cos(Stack[SP]);
if(c == 0) { evalErrorType=1; return 0; }
Stack[SP] = 1/c; break;
}
case cSin: Stack[SP] = sin(Stack[SP]); break;
case cSinh: Stack[SP] = sinh(Stack[SP]); break;
case cSqrt: if(Stack[SP] < 0) { evalErrorType=2; return 0; }
Stack[SP] = sqrt(Stack[SP]); break;
case cTan: Stack[SP] = tan(Stack[SP]); break;
case cTanh: Stack[SP] = tanh(Stack[SP]); break;
// Misc:
case cImmed: Stack[++SP] = Immed[DP++]; break;
case cJump: DP = ByteCode[IP+2];
IP = ByteCode[IP+1];
break;
// Operators:
case cNeg: Stack[SP] = -Stack[SP]; break;
case cAdd: Stack[SP-1] += Stack[SP]; --SP; break;
case cSub: Stack[SP-1] -= Stack[SP]; --SP; break;
case cMul: Stack[SP-1] *= Stack[SP]; --SP; break;
case cDiv: if(Stack[SP] == 0) { evalErrorType=1; return 0; }
Stack[SP-1] /= Stack[SP]; --SP; break;
case cMod: if(Stack[SP] == 0) { evalErrorType=1; return 0; }
Stack[SP-1] = fmod(Stack[SP-1], Stack[SP]);
--SP; break;
case cPow: Stack[SP-1] = pow(Stack[SP-1], Stack[SP]);
--SP; break;
case cEqual: Stack[SP-1] = (Stack[SP-1] == Stack[SP]);
--SP; break;
case cLess: Stack[SP-1] = (Stack[SP-1] < Stack[SP]);
--SP; break;
case cGreater: Stack[SP-1] = (Stack[SP-1] > Stack[SP]);
--SP; break;
case cAnd: Stack[SP-1] =
(doubleToInt(Stack[SP-1]) &&
doubleToInt(Stack[SP]));
--SP; break;
case cOr: Stack[SP-1] =
(doubleToInt(Stack[SP-1]) ||
doubleToInt(Stack[SP]));
--SP; break;
// Degrees-radians conversion:
case cDeg: Stack[SP] = RadiansToDegrees(Stack[SP]); break;
case cRad: Stack[SP] = DegreesToRadians(Stack[SP]); break;
// User-defined function calls:
case cFCall:
{
unsigned index = ByteCode[++IP];
unsigned params = data->FuncPtrs[index].params;
double retVal =
data->FuncPtrs[index].ptr(&Stack[SP-params+1]);
SP -= params-1;
Stack[SP] = retVal;
break;
}
case cPCall:
{
unsigned index = ByteCode[++IP];
unsigned params = data->FuncParsers[index]->data->varAmount;
double retVal =
data->FuncParsers[index]->Eval(&Stack[SP-params+1]);
SP -= params-1;
Stack[SP] = retVal;
break;
}
#ifdef SUPPORT_OPTIMIZER
case cVar: break; // Paranoia. These should never exist
case cDup: Stack[SP+1] = Stack[SP]; ++SP; break;
case cInv:
if(Stack[SP] == 0.0) { evalErrorType=1; return 0; }
Stack[SP] = 1.0/Stack[SP];
break;
#endif
// Variables:
default:
Stack[++SP] = Vars[ByteCode[IP]-VarBegin];
}
}
evalErrorType=0;
return Stack[SP];
}
#ifdef FUNCTIONPARSER_SUPPORT_DEBUG_OUTPUT
namespace
{
inline void printHex(std::ostream& dest, unsigned n)
{
dest.width(8); dest.fill('0'); hex(dest); //uppercase(dest);
dest << n;
}
}
void FunctionParser::PrintByteCode(std::ostream& dest) const
{
const unsigned* const ByteCode = data->ByteCode;
const double* const Immed = data->Immed;
for(unsigned IP=0, DP=0; IP<data->ByteCodeSize; ++IP)
{
printHex(dest, IP);
dest << ": ";
unsigned opcode = ByteCode[IP];
switch(opcode)
{
case cIf:
dest << "jz/t";
printHex(dest, ByteCode[IP+1]+1);
dest << endl;
IP += 2;
break;
case cJump:
dest << "jump/t";
printHex(dest, ByteCode[IP+1]+1);
dest << endl;
IP += 2;
break;
case cImmed:
dest.precision(10);
dest << "push/t" << Immed[DP++] << endl;
break;
case cFCall:
{
unsigned index = ByteCode[++IP];
Data::VarMap_t::const_iterator iter =
data->FuncPtrNames.begin();
while(iter->second != index) ++iter;
dest << "call/t" << iter->first << endl;
break;
}
case cPCall:
{
unsigned index = ByteCode[++IP];
Data::VarMap_t::const_iterator iter =
data->FuncParserNames.begin();
while(iter->second != index) ++iter;
dest << "call/t" << iter->first << endl;
break;
}
default:
if(opcode < VarBegin)
{
string n;
switch(opcode)
{
case cNeg: n = "neg"; break;
case cAdd: n = "add"; break;
case cSub: n = "sub"; break;
case cMul: n = "mul"; break;
case cDiv: n = "div"; break;
case cMod: n = "mod"; break;
case cPow: n = "pow"; break;
case cEqual: n = "eq"; break;
case cLess: n = "lt"; break;
case cGreater: n = "gt"; break;
case cAnd: n = "and"; break;
case cOr: n = "or"; break;
case cDeg: n = "deg"; break;
case cRad: n = "rad"; break;
#ifndef DISABLE_EVAL
case cEval: n = "call/t0"; break;
#endif
#ifdef SUPPORT_OPTIMIZER
case cVar: n = "(var)"; break;
case cDup: n = "dup"; break;
case cInv: n = "inv"; break;
#endif
default: n = Functions[opcode-cAbs].name;
}
dest << n << endl;
}
else
{
dest << "push/tVar" << opcode-VarBegin << endl;
}
}
}
}
#endif