📄 fprase.cpp
字号:
//==============================
// 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 "fparse.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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -