/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen, Joel Yliluoma *| |* *| |* This library is distributed under the terms of the *| |* GNU Lesser General Public License version 3. *| |* (See lgpl.txt and gpl.txt for the license text.) *| \***************************************************************************/ #include "fpconfig.hh" #include "fparser.hh" #include #include #include #include #include #include #include #include "extrasrc/fptypes.hh" #include "extrasrc/fpaux.hh" using namespace FUNCTIONPARSERTYPES; #ifdef FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA #ifndef FP_USE_THREAD_SAFE_EVAL #define FP_USE_THREAD_SAFE_EVAL #endif #endif #ifdef __GNUC__ # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) #else # define likely(x) (x) # define unlikely(x) (x) #endif //========================================================================= // Opcode analysis functions //========================================================================= // These functions are used by the Parse() bytecode optimizer (mostly from // code in fp_opcode_add.inc). bool FUNCTIONPARSERTYPES::IsLogicalOpcode(unsigned op) { switch(op) { case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: return true; default: break; } return false; } bool FUNCTIONPARSERTYPES::IsComparisonOpcode(unsigned op) { switch(op) { case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: return true; default: break; } return false; } unsigned FUNCTIONPARSERTYPES::OppositeComparisonOpcode(unsigned op) { switch(op) { case cLess: return cGreater; case cGreater: return cLess; case cLessOrEq: return cGreaterOrEq; case cGreaterOrEq: return cLessOrEq; } return op; } bool FUNCTIONPARSERTYPES::IsNeverNegativeValueOpcode(unsigned op) { switch(op) { case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: case cSqrt: case cRSqrt: case cSqr: case cHypot: case cAbs: case cAcos: case cCosh: return true; default: break; } return false; } bool FUNCTIONPARSERTYPES::IsAlwaysIntegerOpcode(unsigned op) { switch(op) { case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: case cInt: case cFloor: case cCeil: case cTrunc: return true; default: break; } return false; } bool FUNCTIONPARSERTYPES::IsUnaryOpcode(unsigned op) { switch(op) { case cInv: case cNeg: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cSqr: case cRSqrt: case cDeg: case cRad: return true; } return (op < FUNC_AMOUNT && Functions[op].params == 1); } bool FUNCTIONPARSERTYPES::IsBinaryOpcode(unsigned op) { switch(op) { case cAdd: case cSub: case cRSub: case cMul: case cDiv: case cRDiv: case cMod: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: case cAnd: case cAbsAnd: case cOr: case cAbsOr: return true; } return (op < FUNC_AMOUNT && Functions[op].params == 2); } bool FUNCTIONPARSERTYPES::IsVarOpcode(unsigned op) { // See comment in declaration of FP_ParamGuardMask return int(op) >= VarBegin; } bool FUNCTIONPARSERTYPES::IsCommutativeOrParamSwappableBinaryOpcode(unsigned op) { switch(op) { case cAdd: case cMul: case cEqual: case cNEqual: case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cMin: case cMax: case cHypot: return true; case cDiv: case cSub: case cRDiv: case cRSub: return true; case cLess: case cGreater: case cLessOrEq: case cGreaterOrEq: return true; } return false; } unsigned FUNCTIONPARSERTYPES::GetParamSwappedBinaryOpcode(unsigned op) { switch(op) { case cAdd: case cMul: case cEqual: case cNEqual: case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cMin: case cMax: case cHypot: return op; case cDiv: return cRDiv; case cSub: return cRSub; case cRDiv: return cDiv; case cRSub: return cSub; case cLess: return cGreater; case cGreater: return cLess; case cLessOrEq: return cGreaterOrEq; case cGreaterOrEq: return cLessOrEq; } return op; // Error } template bool FUNCTIONPARSERTYPES::HasInvalidRangesOpcode(unsigned op) { // Returns true, if the given opcode has a range of // input values that gives an error. if(ComplexType) { // COMPLEX: switch(op) { case cAtan: // allowed range: x != +-1i case cAtanh: // allowed range: x != +-1 //case cCot: // allowed range: tan(x) != 0 //case cCsc: // allowed range: sin(x) != 0 case cLog: // allowed range: x != 0 case cLog2: // allowed range: x != 0 case cLog10: // allowed range: x != 0 #ifdef FP_SUPPORT_OPTIMIZER case cLog2by:// allowed range: x != 0 #endif //case cPow: // allowed when: x != 0 or y != 0 //case cSec: // allowed range: cos(x) != 0 //case cTan: // allowed range: cos(x) != 0 --> x != +-(pi/2) //case cTanh: // allowed range: log(x) != -1 --> x != +-(pi/2)i case cRSqrt: // allowed range: x != 0 //case cDiv: // allowed range: y != 0 //case cRDiv: // allowed range: x != 0 //case cInv: // allowed range: x != 0 return true; } } else { // REAL: switch(op) { case cAcos: // allowed range: |x| <= 1 case cAsin: // allowed range: |x| <= 1 case cAcosh: // allowed range: x >= 1 case cAtanh: // allowed range: |x| < 1 //case cCot: // allowed range: tan(x) != 0 //case cCsc: // allowed range: sin(x) != 0 case cLog: // allowed range: x > 0 case cLog2: // allowed range: x > 0 case cLog10: // allowed range: x > 0 #ifdef FP_SUPPORT_OPTIMIZER case cLog2by:// allowed range: x > 0 #endif //case cPow: // allowed when: x > 0 or (x = 0 and y != 0) or (x<0) // Technically, when (x<0 and y is not integer), // it is not allowed, but we allow it anyway // in order to make nontrivial roots work. //case cSec: // allowed range: cos(x) != 0 case cSqrt: // allowed range: x >= 0 case cRSqrt: // allowed range: x > 0 //case cTan: // allowed range: cos(x) != 0 --> x != +-(pi/2) //case cDiv: // allowed range: y != 0 //case cRDiv: // allowed range: x != 0 //case cInv: // allowed range: x != 0 return true; } } return false; } template bool FUNCTIONPARSERTYPES::HasInvalidRangesOpcode(unsigned op); template bool FUNCTIONPARSERTYPES::HasInvalidRangesOpcode(unsigned op); #if(0) // Implementation moved to fpaux.hh due to linker problems //========================================================================= // Mathematical template functions //========================================================================= /* fp_pow() is a wrapper for std::pow() * that produces an identical value for * exp(1) ^ 2.0 (0x4000000000000000) * as exp(2.0) (0x4000000000000000) * - std::pow() on x86_64 * produces 2.0 (0x3FFFFFFFFFFFFFFF) instead! * See comments below for other special traits. */ namespace { template inline ValueT fp_pow_with_exp_log(const ValueT& x, const ValueT& y) { // Exponentiation using exp(log(x)*y). // See http://en.wikipedia.org/wiki/Exponentiation#Real_powers // Requirements: x > 0. return fp_exp(fp_log(x) * y); } template inline ValueT fp_powi(ValueT x, unsigned long y) { // Fast binary exponentiation algorithm // See http://en.wikipedia.org/wiki/Exponentiation_by_squaring // Requirements: y is non-negative integer. ValueT result(1); while(y != 0) { if(y & 1) { result *= x; y -= 1; } else { x *= x; y /= 2; } } return result; } } template ValueT FUNCTIONPARSERTYPES::fp_pow(const ValueT& x, const ValueT& y) { if(x == ValueT(1)) return ValueT(1); // y is now zero or positive if(isLongInteger(y)) { // Use fast binary exponentiation algorithm if(y >= ValueT(0)) return fp_powi(x, makeLongInteger(y)); else return ValueT(1) / fp_powi(x, -makeLongInteger(y)); } if(y >= ValueT(0)) { // y is now positive. Calculate using exp(log(x)*y). if(x > ValueT(0)) return fp_pow_with_exp_log(x, y); if(x == ValueT(0)) return ValueT(0); // At this point, y > 0.0 and x is known to be < 0.0, // because positive and zero cases are already handled. if(!isInteger(y*ValueT(16))) return -fp_pow_with_exp_log(-x, y); // ^This is not technically correct, but it allows // functions such as cbrt(x^5), that is, x^(5/3), // to be evaluated when x is negative. // It is too complicated (and slow) to test whether y // is a formed from a ratio of an integer to an odd integer. // (And due to floating point inaccuracy, pointless too.) // For example, x^1.30769230769... is // actually x^(17/13), i.e. (x^17) ^ (1/13). // (-5)^(17/13) gives us now -8.204227562330453. // To see whether the result is right, we can test the given // root: (-8.204227562330453)^13 gives us the value of (-5)^17, // which proves that the expression was correct. // // The y*16 check prevents e.g. (-4)^(3/2) from being calculated, // as it would confuse functioninfo when pow() returns no error // but sqrt() does when the formula is converted into sqrt(x)*x. // // The errors in this approach are: // (-2)^sqrt(2) should produce NaN // or actually sqrt(2)I + 2^sqrt(2), // produces -(2^sqrt(2)) instead. // (Impact: Neglible) // Thus, at worst, we're changing a NaN (or complex) // result into a negative real number result. } else { // y is negative. Utilize the x^y = 1/(x^-y) identity. if(x > ValueT(0)) return fp_pow_with_exp_log(ValueT(1) / x, -y); if(x < ValueT(0)) { if(!isInteger(y*ValueT(-16))) return -fp_pow_with_exp_log(ValueT(-1) / x, -y); // ^ See comment above. } // Remaining case: 0.0 ^ negative number } // This is reached when: // x=0, and y<0 // x<0, and y*16 is either positive or negative integer // It is used for producing error values and as a safe fallback. return fp_pow_base(x, y); } #endif //========================================================================= // Elementary (atom) parsing functions //========================================================================= namespace { const unsigned FP_ParamGuardMask = 1U << (sizeof(unsigned) * 8u - 1u); // ^ This mask is used to prevent cFetch/other opcode's parameters // from being confused into opcodes or variable indices within the // bytecode optimizer. Because the way it is tested in bytecoderules.dat // for speed reasons, it must also be the sign-bit of the "int" datatype. // Perhaps an "assert(IsVarOpcode(X | FP_ParamGuardMask) == false)" // might be justified to put somewhere in the code, just in case? /* Reads an UTF8-encoded sequence which forms a valid identifier name from the given input string and returns its length. If bit 31 is set, the return value also contains the internal function opcode (defined in fptypes.hh) that matches the name. */ unsigned readIdentifierCommon(const char* input) { /* Assuming unsigned = 32 bits: 76543210 76543210 76543210 76543210 Return value if built-in function: 1PPPPPPP PPPPPPPP LLLLLLLL LLLLLLLL P = function opcode (15 bits) L = function name length (16 bits) Return value if not built-in function: 0LLLLLLL LLLLLLLL LLLLLLLL LLLLLLLL L = identifier length (31 bits) If unsigned has more than 32 bits, the other higher order bits are to be assumed zero. */ #include "extrasrc/fp_identifier_parser.inc" return 0; } template inline unsigned readIdentifier(const char* input) { const unsigned value = readIdentifierCommon(input); if( (value & 0x80000000U) != 0) // Function? { // Verify that the function actually exists for this datatype if(IsIntType::result && !Functions[(value >> 16) & 0x7FFF].okForInt()) { // If it does not exist, return it as an identifier instead return value & 0xFFFFu; } if(!IsComplexType::result && Functions[(value >> 16) & 0x7FFF].complexOnly()) { // If it does not exist, return it as an identifier instead return value & 0xFFFFu; } } return value; } // Returns true if the entire string is a valid identifier template bool containsOnlyValidIdentifierChars(const std::string& name) { if(name.empty()) return false; return readIdentifier(name.c_str()) == (unsigned) name.size(); } // ----------------------------------------------------------------------- // Wrappers for strto... functions // ----------------------------------------------------------------------- template inline Value_t fp_parseLiteral(const char* str, char** endptr) { return std::strtod(str, endptr); } #if defined(FP_USE_STRTOLD) || defined(FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS) template<> inline long double fp_parseLiteral(const char* str, char** endptr) { using namespace std; // Just in case strtold() is not inside std:: return strtold(str, endptr); } #endif #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline long fp_parseLiteral(const char* str, char** endptr) { return std::strtol(str, endptr, 10); } #endif #ifdef FP_SUPPORT_COMPLEX_NUMBERS template inline std::complex fp_parseComplexLiteral(const char* str, char** endptr) { T result = fp_parseLiteral (str,endptr); const char* end = *endptr; if( (*end == 'i' || *end == 'I') && !std::isalnum(end[1]) ) { ++*endptr; return std::complex (T(), result); } return std::complex (result, T()); } #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE template<> inline std::complex fp_parseLiteral > (const char* str, char** endptr) { return fp_parseComplexLiteral (str,endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE template<> inline std::complex fp_parseLiteral > (const char* str, char** endptr) { return fp_parseComplexLiteral (str,endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE template<> inline std::complex fp_parseLiteral > (const char* str, char** endptr) { return fp_parseComplexLiteral (str,endptr); } #endif // ----------------------------------------------------------------------- // Hexadecimal floating point literal parsing // ----------------------------------------------------------------------- inline int testXdigit(unsigned c) { if((c-'0') < 10u) return c&15; // 0..9 if(((c|0x20)-'a') < 6u) return 9+(c&15); // A..F or a..f return -1; // Not a hex digit } template inline void addXdigit(elem_t* buffer, unsigned nibble) { for(unsigned p=0; p> (elem_t)(limb_bits-4) ); buffer[p] = (buffer[p] << 4) | nibble; nibble = carry; } } template Value_t parseHexLiteral(const char* str, char** endptr) { const unsigned bits_per_char = 8; const int MantissaBits = std::numeric_limits::radix == 2 ? std::numeric_limits::digits : (((sizeof(Value_t) * bits_per_char) &~ 3) - 4); typedef unsigned long elem_t; const int ExtraMantissaBits = 4 + ((MantissaBits+3)&~3); // Store one digit more for correct rounding const unsigned limb_bits = sizeof(elem_t) * bits_per_char; const unsigned n_limbs = (ExtraMantissaBits + limb_bits-1) / limb_bits; elem_t mantissa_buffer[n_limbs] = { 0 }; int n_mantissa_bits = 0; // Track the number of bits int exponent = 0; // The exponent that will be used to multiply the mantissa // Read integer portion while(true) { int xdigit = testXdigit(*str); if(xdigit < 0) break; addXdigit (mantissa_buffer, xdigit); ++str; n_mantissa_bits += 4; if(n_mantissa_bits >= ExtraMantissaBits) { // Exhausted the precision. Parse the rest (until exponent) // normally but ignore the actual digits. for(; testXdigit(*str) >= 0; ++str) exponent += 4; // Read but ignore decimals if(*str == '.') for(++str; testXdigit(*str) >= 0; ++str) {} goto read_exponent; } } // Read decimals if(*str == '.') for(++str; ; ) { int xdigit = testXdigit(*str); if(xdigit < 0) break; addXdigit (mantissa_buffer, xdigit); ++str; exponent -= 4; n_mantissa_bits += 4; if(n_mantissa_bits >= ExtraMantissaBits) { // Exhausted the precision. Skip the rest // of the decimals, until the exponent. while(testXdigit(*str) >= 0) ++str; break; } } // Read exponent read_exponent: if(*str == 'p' || *str == 'P') { const char* str2 = str+1; long p_exponent = strtol(str2, const_cast (&str2), 10); if(str2 != str+1 && p_exponent == (long)(int)p_exponent) { exponent += (int)p_exponent; str = str2; } } if(endptr) *endptr = const_cast (str); Value_t result = std::ldexp(Value_t(mantissa_buffer[0]), exponent); for(unsigned p=1; p long parseHexLiteral(const char* str, char** endptr) { return std::strtol(str, endptr, 16); } #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE template<> std::complex parseHexLiteral >(const char* str, char** endptr) { return parseHexLiteral (str, endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE template<> std::complex parseHexLiteral >(const char* str, char** endptr) { return parseHexLiteral (str, endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE template<> std::complex parseHexLiteral >(const char* str, char** endptr) { return parseHexLiteral (str, endptr); } #endif } //========================================================================= // Utility functions //========================================================================= namespace { // ----------------------------------------------------------------------- // Add a new identifier to the specified identifier map // ----------------------------------------------------------------------- // Return value will be false if the name already existed template bool addNewNameData(NamePtrsMap& namePtrs, std::pair >& newName, bool isVar) { typename NamePtrsMap::iterator nameIter = namePtrs.lower_bound(newName.first); if(nameIter != namePtrs.end() && newName.first == nameIter->first) { // redefining a var is not allowed. if(isVar) return false; // redefining other tokens is allowed, if the type stays the same. if(nameIter->second.type != newName.second.type) return false; // update the data nameIter->second = newName.second; return true; } if(!isVar) { // Allocate a copy of the name (pointer stored in the map key) // However, for VARIABLEs, the pointer points to VariableString, // which is managed separately. Thusly, only done when !IsVar. char* namebuf = new char[newName.first.nameLength]; memcpy(namebuf, newName.first.name, newName.first.nameLength); newName.first.name = namebuf; } namePtrs.insert(nameIter, newName); return true; } } //========================================================================= // Data struct implementation //========================================================================= template FunctionParserBase::Data::Data(): mReferenceCounter(1), mDelimiterChar(0), mParseErrorType(NO_FUNCTION_PARSED_YET), mEvalErrorType(0), mUseDegreeConversion(false), mErrorLocation(0), mVariablesAmount(0), mStackSize(0) {} template FunctionParserBase::Data::Data(const Data& rhs): mReferenceCounter(0), mDelimiterChar(rhs.mDelimiterChar), mParseErrorType(rhs.mParseErrorType), mEvalErrorType(rhs.mEvalErrorType), mUseDegreeConversion(rhs.mUseDegreeConversion), mErrorLocation(rhs.mErrorLocation), mVariablesAmount(rhs.mVariablesAmount), mVariablesString(rhs.mVariablesString), mNamePtrs(), mFuncPtrs(rhs.mFuncPtrs), mFuncParsers(rhs.mFuncParsers), mByteCode(rhs.mByteCode), mImmed(rhs.mImmed), #ifndef FP_USE_THREAD_SAFE_EVAL mStack(rhs.mStackSize), #endif mStackSize(rhs.mStackSize) { for(typename NamePtrsMap::const_iterator i = rhs.mNamePtrs.begin(); i != rhs.mNamePtrs.end(); ++i) { if(i->second.type == NameData::VARIABLE) { const std::size_t variableStringOffset = i->first.name - rhs.mVariablesString.c_str(); std::pair > tmp (NamePtr(&mVariablesString[variableStringOffset], i->first.nameLength), i->second); mNamePtrs.insert(mNamePtrs.end(), tmp); } else { std::pair > tmp (NamePtr(new char[i->first.nameLength], i->first.nameLength), i->second ); memcpy(const_cast(tmp.first.name), i->first.name, tmp.first.nameLength); mNamePtrs.insert(mNamePtrs.end(), tmp); } } } template FunctionParserBase::Data::~Data() { for(typename NamePtrsMap::iterator i = mNamePtrs.begin(); i != mNamePtrs.end(); ++i) { if(i->second.type != NameData::VARIABLE) delete[] i->first.name; } } template void FunctionParserBase::incFuncWrapperRefCount (FunctionWrapper* wrapper) { ++wrapper->mReferenceCount; } template unsigned FunctionParserBase::decFuncWrapperRefCount (FunctionWrapper* wrapper) { return --wrapper->mReferenceCount; } template FunctionParserBase::Data::FuncWrapperPtrData::FuncWrapperPtrData(): mRawFuncPtr(0), mFuncWrapperPtr(0), mParams(0) {} template FunctionParserBase::Data::FuncWrapperPtrData::~FuncWrapperPtrData() { if(mFuncWrapperPtr && FunctionParserBase::decFuncWrapperRefCount(mFuncWrapperPtr) == 0) delete mFuncWrapperPtr; } template FunctionParserBase::Data::FuncWrapperPtrData::FuncWrapperPtrData (const FuncWrapperPtrData& rhs): mRawFuncPtr(rhs.mRawFuncPtr), mFuncWrapperPtr(rhs.mFuncWrapperPtr), mParams(rhs.mParams) { if(mFuncWrapperPtr) FunctionParserBase::incFuncWrapperRefCount(mFuncWrapperPtr); } template typename FunctionParserBase::Data::FuncWrapperPtrData& FunctionParserBase::Data::FuncWrapperPtrData::operator= (const FuncWrapperPtrData& rhs) { if(&rhs != this) { if(mFuncWrapperPtr && FunctionParserBase::decFuncWrapperRefCount(mFuncWrapperPtr) == 0) delete mFuncWrapperPtr; mRawFuncPtr = rhs.mRawFuncPtr; mFuncWrapperPtr = rhs.mFuncWrapperPtr; mParams = rhs.mParams; if(mFuncWrapperPtr) FunctionParserBase::incFuncWrapperRefCount(mFuncWrapperPtr); } return *this; } //========================================================================= // FunctionParser constructors, destructor and assignment //========================================================================= template FunctionParserBase::FunctionParserBase(): mData(new Data), mStackPtr(0) { } template FunctionParserBase::~FunctionParserBase() { if(--(mData->mReferenceCounter) == 0) delete mData; } template FunctionParserBase::FunctionParserBase(const FunctionParserBase& cpy): mData(cpy.mData), mStackPtr(0) { ++(mData->mReferenceCounter); } template FunctionParserBase& FunctionParserBase::operator=(const FunctionParserBase& cpy) { if(mData != cpy.mData) { if(--(mData->mReferenceCounter) == 0) delete mData; mData = cpy.mData; ++(mData->mReferenceCounter); } return *this; } template typename FunctionParserBase::Data* FunctionParserBase::getParserData() { return mData; } template void FunctionParserBase::setDelimiterChar(char c) { mData->mDelimiterChar = c; } //--------------------------------------------------------------------------- // Copy-on-write method //--------------------------------------------------------------------------- template void FunctionParserBase::CopyOnWrite() { if(mData->mReferenceCounter > 1) { Data* oldData = mData; mData = new Data(*oldData); --(oldData->mReferenceCounter); mData->mReferenceCounter = 1; } } template void FunctionParserBase::ForceDeepCopy() { CopyOnWrite(); } //========================================================================= // Epsilon //========================================================================= template Value_t FunctionParserBase::epsilon() { return Epsilon::value; } template void FunctionParserBase::setEpsilon(Value_t value) { Epsilon::value = value; } //========================================================================= // User-defined identifier addition functions //========================================================================= template bool FunctionParserBase::AddConstant(const std::string& name, Value_t value) { if(!containsOnlyValidIdentifierChars(name)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::CONSTANT, value)); return addNewNameData(mData->mNamePtrs, newName, false); } template bool FunctionParserBase::AddUnit(const std::string& name, Value_t value) { if(!containsOnlyValidIdentifierChars(name)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::UNIT, value)); return addNewNameData(mData->mNamePtrs, newName, false); } template bool FunctionParserBase::AddFunction (const std::string& name, FunctionPtr ptr, unsigned paramsAmount) { if(!containsOnlyValidIdentifierChars(name)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::FUNC_PTR, unsigned(mData->mFuncPtrs.size()))); const bool success = addNewNameData(mData->mNamePtrs, newName, false); if(success) { mData->mFuncPtrs.push_back(typename Data::FuncWrapperPtrData()); mData->mFuncPtrs.back().mRawFuncPtr = ptr; mData->mFuncPtrs.back().mParams = paramsAmount; } return success; } template bool FunctionParserBase::addFunctionWrapperPtr (const std::string& name, FunctionWrapper* wrapper, unsigned paramsAmount) { if(!AddFunction(name, FunctionPtr(0), paramsAmount)) return false; mData->mFuncPtrs.back().mFuncWrapperPtr = wrapper; return true; } template typename FunctionParserBase::FunctionWrapper* FunctionParserBase::GetFunctionWrapper(const std::string& name) { CopyOnWrite(); NamePtr namePtr(name.data(), unsigned(name.size())); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(namePtr); if(nameIter != mData->mNamePtrs.end() && nameIter->second.type == NameData::FUNC_PTR) { return mData->mFuncPtrs[nameIter->second.index].mFuncWrapperPtr; } return 0; } template bool FunctionParserBase::CheckRecursiveLinking (const FunctionParserBase* fp) const { if(fp == this) return true; for(unsigned i = 0; i < fp->mData->mFuncParsers.size(); ++i) if(CheckRecursiveLinking(fp->mData->mFuncParsers[i].mParserPtr)) return true; return false; } template bool FunctionParserBase::AddFunction(const std::string& name, FunctionParserBase& fp) { if(!containsOnlyValidIdentifierChars(name) || CheckRecursiveLinking(&fp)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::PARSER_PTR, unsigned(mData->mFuncParsers.size()))); const bool success = addNewNameData(mData->mNamePtrs, newName, false); if(success) { mData->mFuncParsers.push_back(typename Data::FuncParserPtrData()); mData->mFuncParsers.back().mParserPtr = &fp; mData->mFuncParsers.back().mParams = fp.mData->mVariablesAmount; } return success; } template bool FunctionParserBase::RemoveIdentifier(const std::string& name) { CopyOnWrite(); NamePtr namePtr(name.data(), unsigned(name.size())); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(namePtr); if(nameIter != mData->mNamePtrs.end()) { if(nameIter->second.type == NameData::VARIABLE) { // Illegal attempt to delete variables return false; } delete[] nameIter->first.name; mData->mNamePtrs.erase(nameIter); return true; } return false; } //========================================================================= // Function parsing //========================================================================= namespace { // Error messages returned by ErrorMsg(): const char* const ParseErrorMessage[]= { "Syntax error", // 0 "Mismatched parenthesis", // 1 "Missing ')'", // 2 "Empty parentheses", // 3 "Syntax error: Operator expected", // 4 "Not enough memory", // 5 "An unexpected error occurred. Please make a full bug report " "to the author", // 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 "Syntax error: Unknown identifier", // 11 "(No function has been parsed yet)", "" }; template inline typename FunctionParserBase::ParseErrorType noCommaError(char c) { return c == ')' ? FunctionParserBase::ILL_PARAMS_AMOUNT : FunctionParserBase::SYNTAX_ERROR; } template inline typename FunctionParserBase::ParseErrorType noParenthError(char c) { return c == ',' ? FunctionParserBase::ILL_PARAMS_AMOUNT : FunctionParserBase::MISSING_PARENTH; } template struct IntLiteralMask { enum { mask = // ( 1UL << ('-'-offset)) | (0x3FFUL << ('0'-offset)) }; /* 0x3FF = 10 bits worth "1" */ // Note: If you change fparser to support negative numbers parsing // (as opposed to parsing them as cNeg followed by literal), // enable the '-' line above, and change the offset value // in BeginsLiteral() to '-' instead of '.'. }; template struct LiteralMask { enum { mask = ( 1UL << ('.'-offset)) | IntLiteralMask::mask }; }; #ifdef FP_SUPPORT_LONG_INT_TYPE template struct LiteralMask: public IntLiteralMask { }; #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template struct LiteralMask: public IntLiteralMask { }; #endif template struct SimpleSpaceMask { enum { mask = (1UL << ('\r'-offset)) | (1UL << ('\n'-offset)) | (1UL << ('\v'-offset)) | (1UL << ('\t'-offset)) | (1UL << (' ' -offset)) }; }; template inline bool BeginsLiteral(unsigned byte) { enum { n = sizeof(unsigned long)>=8 ? 0 : '.' }; byte -= n; if(byte > (unsigned char)('9'-n)) return false; unsigned long shifted = 1UL << byte; const unsigned long mask = LiteralMask::mask; return (mask & shifted) != 0; } template inline void SkipSpace(CharPtr& function) { /* Space characters in unicode: U+0020 SPACE Depends on font, often adjusted (see below) U+00A0 NO-BREAK SPACE As a space, but often not adjusted U+2000 EN QUAD 1 en (= 1/2 em) U+2001 EM QUAD 1 em (nominally, the height of the font) U+2002 EN SPACE 1 en (= 1/2 em) U+2003 EM SPACE 1 em U+2004 THREE-PER-EM SPACE 1/3 em U+2005 FOUR-PER-EM SPACE 1/4 em U+2006 SIX-PER-EM SPACE 1/6 em U+2007 FIGURE SPACE Tabular width, the width of digits U+2008 PUNCTUATION SPACE The width of a period . U+2009 THIN SPACE 1/5 em (or sometimes 1/6 em) U+200A HAIR SPACE Narrower than THIN SPACE U+200B ZERO WIDTH SPACE Nominally no width, but may expand U+202F NARROW NO-BREAK SPACE Narrower than NO-BREAK SPACE (or SPACE) U+205F MEDIUM MATHEMATICAL SPACE 4/18 em U+3000 IDEOGRAPHIC SPACE The width of ideographic (CJK) characters. Also: U+000A \n U+000D \r U+0009 \t U+000B \v As UTF-8 sequences: 09 0A 0B 0D 20 C2 A0 E2 80 80-8B E2 80 AF E2 81 9F E3 80 80 */ while(true) { enum { n = sizeof(unsigned long)>=8 ? 0 : '\t' }; typedef signed char schar; unsigned byte = (unsigned char)*function; byte -= n; // ^Note: values smaller than n intentionally become // big values here due to integer wrap. The // comparison below thus excludes them, making // the effective range 0x09..0x20 (32-bit) // or 0x00..0x20 (64-bit) within the if-clause. if(byte <= (unsigned char)(' '-n)) { unsigned long shifted = 1UL << byte; const unsigned long mask = SimpleSpaceMask::mask; if(mask & shifted) { ++function; continue; } // \r, \n, \t, \v and space break; } if(likely(byte < 0xC2-n)) break; if(byte == 0xC2-n && function[1] == char(0xA0)) { function += 2; continue; } // U+00A0 if(byte == 0xE3-n && function[1] == char(0x80) && function[2] == char(0x80)) { function += 3; continue; } // U+3000 if(byte == 0xE2-n) { if(function[1] == char(0x81)) { if(function[2] != char(0x9F)) break; function += 3; // U+205F continue; } if(function[1] == char(0x80)) if(function[2] == char(0xAF) || // U+202F schar(function[2]) <= schar(0x8B) // U+2000..U+200B ) { function += 3; continue; } } break; } // while(true) } // SkipSpace(CharPtr& function) } // --------------------------------------------------------------------------- // Return parse error message // --------------------------------------------------------------------------- template const char* FunctionParserBase::ErrorMsg() const { return ParseErrorMessage[mData->mParseErrorType]; } template typename FunctionParserBase::ParseErrorType FunctionParserBase::GetParseErrorType() const { return mData->mParseErrorType; } template int FunctionParserBase::EvalError() const { return mData->mEvalErrorType; } // --------------------------------------------------------------------------- // Parse variables // --------------------------------------------------------------------------- template bool FunctionParserBase::ParseVariables (const std::string& inputVarString) { if(mData->mVariablesString == inputVarString) return true; /* Delete existing variables from mNamePtrs */ for(typename NamePtrsMap::iterator i = mData->mNamePtrs.begin(); i != mData->mNamePtrs.end(); ) { if(i->second.type == NameData::VARIABLE) { typename NamePtrsMap::iterator j (i); ++i; mData->mNamePtrs.erase(j); } else ++i; } mData->mVariablesString = inputVarString; const std::string& vars = mData->mVariablesString; const unsigned len = unsigned(vars.size()); unsigned varNumber = VarBegin; const char* beginPtr = vars.c_str(); const char* finalPtr = beginPtr + len; while(beginPtr < finalPtr) { SkipSpace(beginPtr); unsigned nameLength = readIdentifier(beginPtr); if(nameLength == 0 || (nameLength & 0x80000000U)) return false; const char* endPtr = beginPtr + nameLength; SkipSpace(endPtr); if(endPtr != finalPtr && *endPtr != ',') return false; std::pair > newName (NamePtr(beginPtr, nameLength), NameData(NameData::VARIABLE, varNumber++)); if(!addNewNameData(mData->mNamePtrs, newName, true)) { return false; } beginPtr = endPtr + 1; } mData->mVariablesAmount = varNumber - VarBegin; return true; } // --------------------------------------------------------------------------- // Parse() public interface functions // --------------------------------------------------------------------------- template int FunctionParserBase::Parse(const char* Function, const std::string& Vars, bool useDegrees) { CopyOnWrite(); if(!ParseVariables(Vars)) { mData->mParseErrorType = INVALID_VARS; return int(strlen(Function)); } return ParseFunction(Function, useDegrees); } template int FunctionParserBase::Parse(const std::string& Function, const std::string& Vars, bool useDegrees) { CopyOnWrite(); if(!ParseVariables(Vars)) { mData->mParseErrorType = INVALID_VARS; return int(Function.size()); } return ParseFunction(Function.c_str(), useDegrees); } // --------------------------------------------------------------------------- // Main parsing function // --------------------------------------------------------------------------- template int FunctionParserBase::ParseFunction(const char* function, bool useDegrees) { mData->mUseDegreeConversion = useDegrees; mData->mParseErrorType = FP_NO_ERROR; mData->mInlineVarNames.clear(); mData->mByteCode.clear(); mData->mByteCode.reserve(128); mData->mImmed.clear(); mData->mImmed.reserve(128); mData->mStackSize = mStackPtr = 0; mData->mHasByteCodeFlags = false; const char* ptr = Compile(function); mData->mInlineVarNames.clear(); if(mData->mHasByteCodeFlags) { for(unsigned i = unsigned(mData->mByteCode.size()); i-- > 0; ) mData->mByteCode[i] &= ~FP_ParamGuardMask; } if(mData->mParseErrorType != FP_NO_ERROR) return int(mData->mErrorLocation - function); assert(ptr); // Should never be null at this point. It's a bug otherwise. if(*ptr) { if(mData->mDelimiterChar == 0 || *ptr != mData->mDelimiterChar) mData->mParseErrorType = EXPECT_OPERATOR; return int(ptr - function); } #ifndef FP_USE_THREAD_SAFE_EVAL mData->mStack.resize(mData->mStackSize); #endif return -1; } //========================================================================= // Parsing and bytecode compiling functions //========================================================================= template inline const char* FunctionParserBase::SetErrorType(ParseErrorType t, const char* pos) { mData->mParseErrorType = t; mData->mErrorLocation = pos; return 0; } template inline void FunctionParserBase::incStackPtr() { if(++mStackPtr > mData->mStackSize) ++(mData->mStackSize); } namespace { const unsigned char powi_factor_table[128] = { 0,1,0,0,0,0,0,0, 0, 0,0,0,0,0,0,3,/* 0 - 15 */ 0,0,0,0,0,0,0,0, 0, 5,0,3,0,0,3,0,/* 16 - 31 */ 0,0,0,0,0,0,0,3, 0, 0,0,0,0,5,0,0,/* 32 - 47 */ 0,0,5,3,0,0,3,5, 0, 3,0,0,3,0,0,3,/* 48 - 63 */ 0,0,0,0,0,0,0,0, 0, 0,0,3,0,0,3,0,/* 64 - 79 */ 0,9,0,0,0,5,0,3, 0, 0,5,7,0,0,0,5,/* 80 - 95 */ 0,0,0,3,5,0,3,0, 0, 3,0,0,3,0,5,3,/* 96 - 111 */ 0,0,3,5,0,9,0,7, 3,11,0,3,0,5,3,0,/* 112 - 127 */ }; inline int get_powi_factor(long abs_int_exponent) { if(abs_int_exponent >= int(sizeof(powi_factor_table))) return 0; return powi_factor_table[abs_int_exponent]; } #if 0 int EstimatePowiComplexity(int abs_int_exponent) { int cost = 0; while(abs_int_exponent > 1) { int factor = get_powi_factor(abs_int_exponent); if(factor) { cost += EstimatePowiComplexity(factor); abs_int_exponent /= factor; continue; } if(!(abs_int_exponent & 1)) { abs_int_exponent /= 2; cost += 3; // sqr } else { cost += 4; // dup+mul abs_int_exponent -= 1; } } return cost; } #endif bool IsEligibleIntPowiExponent(long int_exponent) { if(int_exponent == 0) return false; long abs_int_exponent = int_exponent; #if 0 int cost = 0; if(abs_int_exponent < 0) { cost += 11; abs_int_exponent = -abs_int_exponent; } cost += EstimatePowiComplexity(abs_int_exponent); return cost < (10*3 + 4*4); #else if(abs_int_exponent < 0) abs_int_exponent = -abs_int_exponent; return (abs_int_exponent >= 1) && (abs_int_exponent <= 46 || (abs_int_exponent <= 1024 && (abs_int_exponent & (abs_int_exponent - 1)) == 0)); #endif } /* Needed by fp_opcode_add.inc if tracing is enabled */ template std::string findName(const NamePtrsMap& nameMap, unsigned index, typename NameData::DataType type) { for(typename NamePtrsMap::const_iterator iter = nameMap.begin(); iter != nameMap.end(); ++iter) { if(iter->second.type == type && iter->second.index == index) return std::string(iter->first.name, iter->first.name + iter->first.nameLength); } return "?"; } } template inline void FunctionParserBase::AddImmedOpcode(Value_t value) { mData->mImmed.push_back(value); mData->mByteCode.push_back(cImmed); } template inline void FunctionParserBase::CompilePowi(long abs_int_exponent) { int num_muls=0; while(abs_int_exponent > 1) { long factor = get_powi_factor(abs_int_exponent); if(factor) { CompilePowi(factor); abs_int_exponent /= factor; continue; } if(!(abs_int_exponent & 1)) { abs_int_exponent /= 2; mData->mByteCode.push_back(cSqr); // ^ Don't put AddFunctionOpcode here, // it would slow down a great deal. } else { mData->mByteCode.push_back(cDup); incStackPtr(); abs_int_exponent -= 1; ++num_muls; } } if(num_muls > 0) { mData->mByteCode.resize(mData->mByteCode.size()+num_muls, cMul); mStackPtr -= num_muls; } } template inline bool FunctionParserBase::TryCompilePowi(Value_t original_immed) { Value_t changed_immed = original_immed; for(int sqrt_count=0; /**/; ++sqrt_count) { long int_exponent = makeLongInteger(changed_immed); if(isLongInteger(changed_immed) && IsEligibleIntPowiExponent(int_exponent)) { long abs_int_exponent = int_exponent; if(abs_int_exponent < 0) abs_int_exponent = -abs_int_exponent; mData->mImmed.pop_back(); mData->mByteCode.pop_back(); --mStackPtr; // ^Though the above is accounted for by the procedure // that generates cPow, we need it for correct cFetch // indexes in CompilePowi(). while(sqrt_count > 0) { int opcode = cSqrt; if(sqrt_count == 1 && int_exponent < 0) { opcode = cRSqrt; int_exponent = -int_exponent; } mData->mByteCode.push_back(opcode); --sqrt_count; } if((abs_int_exponent & 1) == 0) { // This special rule fixes the optimization // shortcoming of (-x)^2 with minimal overhead. AddFunctionOpcode(cSqr); abs_int_exponent >>= 1; } CompilePowi(abs_int_exponent); if(int_exponent < 0) mData->mByteCode.push_back(cInv); ++mStackPtr; // Needed because cPow adding will assume this. return true; } if(sqrt_count >= 4) break; changed_immed += changed_immed; } // When we don't know whether x >= 0, we still know that // x^y can be safely converted into exp(y * log(x)) // when y is _not_ integer, because we know that x >= 0. // Otherwise either expression will give a NaN. if(/*!isInteger(original_immed) ||*/ IsNeverNegativeValueOpcode(mData->mByteCode[mData->mByteCode.size()-2])) { mData->mImmed.pop_back(); mData->mByteCode.pop_back(); //--mStackPtr; - accounted for by the procedure that generates cPow AddFunctionOpcode(cLog); AddImmedOpcode(original_immed); //incStackPtr(); - this and the next are redundant because... AddFunctionOpcode(cMul); //--mStackPtr; - ...because the cImmed was popped earlier. AddFunctionOpcode(cExp); return true; } return false; } //#include "fpoptimizer/opcodename.hh" // ^ needed only if FP_TRACE_BYTECODE_OPTIMIZATION() is used template inline void FunctionParserBase::AddFunctionOpcode(unsigned opcode) { #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 0 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline void FunctionParserBase::AddFunctionOpcode(unsigned opcode) { typedef long Value_t; #define FP_FLOAT_VERSION 0 #define FP_COMPLEX_VERSION 0 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> inline void FunctionParserBase::AddFunctionOpcode(unsigned opcode) { typedef GmpInt Value_t; #define FP_FLOAT_VERSION 0 #define FP_COMPLEX_VERSION 0 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE template<> inline void FunctionParserBase >::AddFunctionOpcode(unsigned opcode) { typedef std::complex Value_t; #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 1 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE template<> inline void FunctionParserBase >::AddFunctionOpcode(unsigned opcode) { typedef std::complex Value_t; #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 1 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE template<> inline void FunctionParserBase >::AddFunctionOpcode(unsigned opcode) { typedef std::complex Value_t; #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 1 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif template unsigned FunctionParserBase::ParseIdentifier(const char* function) { return readIdentifier(function); } template std::pair FunctionParserBase::ParseLiteral(const char* function) { char* endptr; #if 0 /* Profile the hex literal parser */ if(function[0]=='0' && function[1]=='x') { // Parse hexadecimal literal if fp_parseLiteral didn't already Value_t val = parseHexLiteral(function+2, &endptr); if(endptr == function+2) return std::pair (function, Value_t()); return std::pair (endptr, val); } #endif Value_t val = fp_parseLiteral(function, &endptr); if(endptr == function+1 && function[0] == '0' && function[1] == 'x') { // Parse hexadecimal literal if fp_parseLiteral didn't already val = parseHexLiteral(function+2, &endptr); if(endptr == function+2) return std::pair (function, Value_t()); } else if(endptr == function) return std::pair (function, Value_t()); return std::pair (endptr, val); } #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE template<> std::pair FunctionParserBase::ParseLiteral(const char* function) { char* endPtr; const MpfrFloat val = MpfrFloat::parseString(function, &endPtr); if(endPtr == function) return std::pair (function, MpfrFloat()); return std::pair (endPtr, val); } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> std::pair FunctionParserBase::ParseLiteral(const char* function) { char* endPtr; const GmpInt val = GmpInt::parseString(function, &endPtr); if(endPtr == function) return std::pair (function, GmpInt()); return std::pair (endPtr, val); } #endif template inline const char* FunctionParserBase::CompileLiteral(const char* function) { std::pair result = ParseLiteral(function); if(result.first == function) return SetErrorType(SYNTAX_ERROR, result.first); AddImmedOpcode(result.second); incStackPtr(); SkipSpace(result.first); return result.first; } template const char* FunctionParserBase::CompileIf(const char* function) { if(*function != '(') return SetErrorType(EXPECT_PARENTH_FUNC, function); function = CompileExpression(function+1); if(!function) return 0; if(*function != ',') return SetErrorType(noCommaError(*function), function); OPCODE opcode = cIf; if(mData->mByteCode.back() == cNotNot) mData->mByteCode.pop_back(); if(IsNeverNegativeValueOpcode(mData->mByteCode.back())) { // If we know that the condition to be tested is always // a positive value (such as when produced by "x= 0.5, // cAbsIf simply tests whether cond >= 0.5. opcode = cAbsIf; } mData->mByteCode.push_back(opcode); const unsigned curByteCodeSize = unsigned(mData->mByteCode.size()); PushOpcodeParam(0); // Jump index; to be set later PushOpcodeParam (0); // Immed jump index; to be set later --mStackPtr; function = CompileExpression(function + 1); if(!function) return 0; if(*function != ',') return SetErrorType(noCommaError(*function), function); mData->mByteCode.push_back(cJump); const unsigned curByteCodeSize2 = unsigned(mData->mByteCode.size()); const unsigned curImmedSize2 = unsigned(mData->mImmed.size()); PushOpcodeParam(0); // Jump index; to be set later PushOpcodeParam (0); // Immed jump index; to be set later --mStackPtr; function = CompileExpression(function + 1); if(!function) return 0; if(*function != ')') return SetErrorType(noParenthError(*function), function); PutOpcodeParamAt ( mData->mByteCode.back(), unsigned(mData->mByteCode.size()-1) ); // ^Necessary for guarding against if(x,1,2)+1 being changed // into if(x,1,3) by fp_opcode_add.inc // Set jump indices PutOpcodeParamAt( curByteCodeSize2+1, curByteCodeSize ); PutOpcodeParamAt( curImmedSize2, curByteCodeSize+1 ); PutOpcodeParamAt( unsigned(mData->mByteCode.size())-1, curByteCodeSize2); PutOpcodeParamAt( unsigned(mData->mImmed.size()), curByteCodeSize2+1); ++function; SkipSpace(function); return function; } template const char* FunctionParserBase::CompileFunctionParams (const char* function, unsigned requiredParams) { if(*function != '(') return SetErrorType(EXPECT_PARENTH_FUNC, function); if(requiredParams > 0) { const char* function_end = CompileExpression(function+1); if(!function_end) { // If an error occurred, verify whether it was caused by () ++function; SkipSpace(function); if(*function == ')') return SetErrorType(ILL_PARAMS_AMOUNT, function); // Not caused by (), use the error message given by CompileExpression() return 0; } function = function_end; for(unsigned i = 1; i < requiredParams; ++i) { if(*function != ',') return SetErrorType(noCommaError(*function), function); function = CompileExpression(function+1); if(!function) return 0; } // No need for incStackPtr() because each parse parameter calls it mStackPtr -= requiredParams-1; } else { incStackPtr(); // return value of function is pushed onto the stack ++function; SkipSpace(function); } if(*function != ')') return SetErrorType(noParenthError(*function), function); ++function; SkipSpace(function); return function; } template const char* FunctionParserBase::CompileElement(const char* function) { if(BeginsLiteral( (unsigned char) *function)) return CompileLiteral(function); unsigned nameLength = readIdentifier(function); if(nameLength == 0) { // No identifier found if(*function == '(') return CompileParenthesis(function); if(*function == ')') return SetErrorType(MISM_PARENTH, function); return SetErrorType(SYNTAX_ERROR, function); } // Function, variable or constant if(nameLength & 0x80000000U) // Function { OPCODE func_opcode = OPCODE( (nameLength >> 16) & 0x7FFF ); return CompileFunction(function + (nameLength & 0xFFFF), func_opcode); } NamePtr name(function, nameLength); const char* endPtr = function + nameLength; SkipSpace(endPtr); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(name); if(nameIter == mData->mNamePtrs.end()) { // Check if it's an inline variable: for(typename Data::InlineVarNamesContainer::reverse_iterator iter = mData->mInlineVarNames.rbegin(); iter != mData->mInlineVarNames.rend(); ++iter) { if(name == iter->mName) { if( iter->mFetchIndex+1 == mStackPtr) { mData->mByteCode.push_back(cDup); } else { mData->mByteCode.push_back(cFetch); PushOpcodeParam(iter->mFetchIndex); } incStackPtr(); return endPtr; } } return SetErrorType(UNKNOWN_IDENTIFIER, function); } const NameData* nameData = &nameIter->second; switch(nameData->type) { case NameData::VARIABLE: // is variable if(unlikely(!mData->mByteCode.empty() && mData->mByteCode.back() == nameData->index)) mData->mByteCode.push_back(cDup); else mData->mByteCode.push_back(nameData->index); incStackPtr(); return endPtr; case NameData::CONSTANT: // is constant AddImmedOpcode(nameData->value); incStackPtr(); return endPtr; case NameData::UNIT: // is unit (error if appears here) break; case NameData::FUNC_PTR: // is C++ function function = CompileFunctionParams (endPtr, mData->mFuncPtrs[nameData->index].mParams); //if(!function) return 0; mData->mByteCode.push_back(cFCall); PushOpcodeParam(nameData->index); return function; case NameData::PARSER_PTR: // is FunctionParser function = CompileFunctionParams (endPtr, mData->mFuncParsers[nameData->index].mParams); //if(!function) return 0; mData->mByteCode.push_back(cPCall); PushOpcodeParam(nameData->index); return function; } // When it's an unit (or unrecognized type): return SetErrorType(SYNTAX_ERROR, function); } template inline const char* FunctionParserBase::CompileFunction (const char* function, unsigned func_opcode) { SkipSpace(function); const FuncDefinition& funcDef = Functions[func_opcode]; if(func_opcode == cIf) // "if" is a special case return CompileIf(function); unsigned requiredParams = funcDef.params; function = CompileFunctionParams(function, requiredParams); if(!function) return 0; if(mData->mUseDegreeConversion) { if(funcDef.flags & FuncDefinition::AngleIn) AddFunctionOpcode(cRad); AddFunctionOpcode(func_opcode); if(funcDef.flags & FuncDefinition::AngleOut) AddFunctionOpcode(cDeg); } else { AddFunctionOpcode(func_opcode); } return function; } template inline const char* FunctionParserBase::CompileParenthesis(const char* function) { ++function; // Skip '(' SkipSpace(function); if(*function == ')') return SetErrorType(EMPTY_PARENTH, function); function = CompileExpression(function); if(!function) return 0; if(*function != ')') return SetErrorType(MISSING_PARENTH, function); ++function; // Skip ')' SkipSpace(function); return function; } template const char* FunctionParserBase::CompilePossibleUnit(const char* function) { unsigned nameLength = readIdentifier(function); if(nameLength & 0x80000000U) return function; // built-in function name if(nameLength != 0) { NamePtr name(function, nameLength); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(name); if(nameIter != mData->mNamePtrs.end()) { const NameData* nameData = &nameIter->second; if(nameData->type == NameData::UNIT) { AddImmedOpcode(nameData->value); incStackPtr(); AddFunctionOpcode(cMul); --mStackPtr; const char* endPtr = function + nameLength; SkipSpace(endPtr); return endPtr; } } } return function; } template inline const char* FunctionParserBase::CompilePow(const char* function) { function = CompileElement(function); if(!function) return 0; function = CompilePossibleUnit(function); if(*function == '^') { ++function; SkipSpace(function); unsigned op = cPow; if(mData->mByteCode.back() == cImmed) { if(mData->mImmed.back() == fp_const_e()) { op = cExp; mData->mByteCode.pop_back(); mData->mImmed.pop_back(); --mStackPtr; } else if(mData->mImmed.back() == Value_t(2)) { op = cExp2; mData->mByteCode.pop_back(); mData->mImmed.pop_back(); --mStackPtr; } } function = CompileUnaryMinus(function); if(!function) return 0; // add opcode AddFunctionOpcode(op); if(op == cPow) --mStackPtr; } return function; } /* Currently the power operator is skipped for integral types because its usefulness with them is questionable, and in the case of GmpInt, for safety reasons: - With long int almost any power, except for very small ones, would overflow the result, so the usefulness of this is rather questionable. - With GmpInt the power operator could be easily abused to make the program run out of memory (think of a function like "10^10^10^10^1000000"). */ #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline const char* FunctionParserBase::CompilePow(const char* function) { function = CompileElement(function); if(!function) return 0; return CompilePossibleUnit(function); } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> inline const char* FunctionParserBase::CompilePow(const char* function) { function = CompileElement(function); if(!function) return 0; return CompilePossibleUnit(function); } #endif template inline const char* FunctionParserBase::CompileUnaryMinus(const char* function) { char op = *function; switch(op) { case '-': case '!': ++function; SkipSpace(function); function = CompileUnaryMinus(function); if(!function) return 0; AddFunctionOpcode(op=='-' ? cNeg : cNot); return function; default: break; } return CompilePow(function); } template inline const char* FunctionParserBase::CompileMult(const char* function) { function = CompileUnaryMinus(function); if(!function) return 0; Value_t pending_immed(1); #define FP_FlushImmed(do_reset) \ if(pending_immed != Value_t(1)) \ { \ unsigned op = cMul; \ if(!IsIntType::result && mData->mByteCode.back() == cInv) \ { \ /* (...) cInv 5 cMul -> (...) 5 cRDiv */ \ /* ^ ^ | */ \ mData->mByteCode.pop_back(); \ op = cRDiv; \ } \ AddImmedOpcode(pending_immed); \ incStackPtr(); \ AddFunctionOpcode(op); \ --mStackPtr; \ if(do_reset) pending_immed = Value_t(1); \ } while(true) { char c = *function; if(c == '%') { FP_FlushImmed(true); ++function; SkipSpace(function); function = CompileUnaryMinus(function); if(!function) return 0; AddFunctionOpcode(cMod); --mStackPtr; continue; } if(c != '*' && c != '/') break; bool safe_cumulation = (c == '*' || !IsIntType::result); if(!safe_cumulation) { FP_FlushImmed(true); } ++function; SkipSpace(function); if(mData->mByteCode.back() == cImmed && (safe_cumulation || mData->mImmed.back() == Value_t(1))) { // 5 (...) cMul --> (...) ||| 5 cMul // 5 (...) cDiv --> (...) cInv ||| 5 cMul // ^ | ^ pending_immed *= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); --mStackPtr; function = CompileUnaryMinus(function); if(!function) return 0; if(c == '/') AddFunctionOpcode(cInv); continue; } if(safe_cumulation && mData->mByteCode.back() == cMul && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) 5 cMul (...) cMul -> (:::) (...) cMul ||| 5 cMul // (:::) 5 cMul (...) cDiv -> (:::) (...) cDiv ||| 5 cMul // ^ ^ pending_immed *= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } // cDiv is not tested here because the bytecode // optimizer will convert this kind of cDivs into cMuls. bool lhs_inverted = false; if(!IsIntType::result && c == '*' && mData->mByteCode.back() == cInv) { // (:::) cInv (...) cMul -> (:::) (...) cRDiv // (:::) cInv (...) cDiv -> (:::) (...) cMul cInv // ^ ^ | mData->mByteCode.pop_back(); lhs_inverted = true; } function = CompileUnaryMinus(function); if(!function) return 0; if(safe_cumulation && mData->mByteCode.back() == cMul && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cMul cMul -> (:::) (...) cMul ||| 5 Mul // (:::) (...) 5 cMul cDiv -> (:::) (...) cDiv ||| /5 Mul // ^ ^ if(c == '*') pending_immed *= mData->mImmed.back(); else pending_immed /= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } else if(safe_cumulation && mData->mByteCode.back() == cRDiv && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cRDiv cMul -> (:::) (...) cDiv ||| 5 cMul // (:::) (...) 5 cRDiv cDiv -> (:::) (...) cMul ||| /5 cMul // ^ ^ if(c == '*') { c = '/'; pending_immed *= mData->mImmed.back(); } else { c = '*'; pending_immed /= mData->mImmed.back(); } mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } if(!lhs_inverted) // if (/x/y) was changed to /(x*y), add missing cInv { AddFunctionOpcode(c == '*' ? cMul : cDiv); --mStackPtr; } else if(c == '*') // (/x)*y -> rdiv(x,y) { AddFunctionOpcode(cRDiv); --mStackPtr; } else // (/x)/y -> /(x*y) { AddFunctionOpcode(cMul); --mStackPtr; AddFunctionOpcode(cInv); } } FP_FlushImmed(false); #undef FP_FlushImmed return function; } template inline const char* FunctionParserBase::CompileAddition(const char* function) { function = CompileMult(function); if(!function) return 0; Value_t pending_immed(0); #define FP_FlushImmed(do_reset) \ if(pending_immed != Value_t(0)) \ { \ unsigned op = cAdd; \ if(mData->mByteCode.back() == cNeg) \ { \ /* (...) cNeg 5 cAdd -> (...) 5 cRSub */ \ /* ^ ^ | */ \ mData->mByteCode.pop_back(); \ op = cRSub; \ } \ AddImmedOpcode(pending_immed); \ incStackPtr(); \ AddFunctionOpcode(op); \ --mStackPtr; \ if(do_reset) pending_immed = Value_t(0); \ } while(true) { char c = *function; if(c != '+' && c != '-') break; ++function; SkipSpace(function); if(mData->mByteCode.back() == cImmed) { // 5 (...) cAdd --> (...) ||| 5 cAdd // 5 (...) cSub --> (...) cNeg ||| 5 cAdd // ^ | ^ pending_immed += mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); --mStackPtr; function = CompileMult(function); if(!function) return 0; if(c == '-') AddFunctionOpcode(cNeg); continue; } if(mData->mByteCode.back() == cAdd && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) 5 cAdd (...) cAdd -> (:::) (...) cAdd ||| 5 cAdd // (:::) 5 cAdd (...) cSub -> (:::) (...) cSub ||| 5 cAdd // ^ ^ pending_immed += mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } // cSub is not tested here because the bytecode // optimizer will convert this kind of cSubs into cAdds. bool lhs_negated = false; if(mData->mByteCode.back() == cNeg) { // (:::) cNeg (...) cAdd -> (:::) (...) cRSub // (:::) cNeg (...) cSub -> (:::) (...) cAdd cNeg // ^ ^ | mData->mByteCode.pop_back(); lhs_negated = true; } function = CompileMult(function); if(!function) return 0; if(mData->mByteCode.back() == cAdd && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cAdd cAdd -> (:::) (...) cAdd ||| 5 Add // (:::) (...) 5 cAdd cSub -> (:::) (...) cSub ||| -5 Add // ^ ^ if(c == '+') pending_immed += mData->mImmed.back(); else pending_immed -= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } else if(mData->mByteCode.back() == cRSub && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cRSub cAdd -> (:::) (...) cSub ||| 5 cAdd // (:::) (...) 5 cRSub cSub -> (:::) (...) cAdd ||| -5 cAdd // ^ ^ if(c == '+') { c = '-'; pending_immed += mData->mImmed.back(); } else { c = '+'; pending_immed -= mData->mImmed.back(); } mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } if(!lhs_negated) // if (-x-y) was changed to -(x+y), add missing cNeg { AddFunctionOpcode(c == '+' ? cAdd : cSub); --mStackPtr; } else if(c == '+') // (-x)+y -> rsub(x,y) { AddFunctionOpcode(cRSub); --mStackPtr; } else // (-x)-y -> -(x+y) { AddFunctionOpcode(cAdd); --mStackPtr; AddFunctionOpcode(cNeg); } } FP_FlushImmed(false); #undef FP_FlushImmed return function; } template inline const char* FunctionParserBase::CompileComparison(const char* function) { unsigned op=0; while(true) { function = CompileAddition(function); if(!function) return 0; if(op) { AddFunctionOpcode(op); --mStackPtr; } switch(*function) { case '=': ++function; op = cEqual; break; case '!': if(function[1] == '=') { function += 2; op = cNEqual; break; } // If '=' does not follow '!', a syntax error will // be generated at the outermost parsing level return function; case '<': if(function[1] == '=') { function += 2; op = cLessOrEq; break; } ++function; op = cLess; break; case '>': if(function[1] == '=') { function += 2; op = cGreaterOrEq; break; } ++function; op = cGreater; break; default: return function; } SkipSpace(function); } return function; } template inline const char* FunctionParserBase::CompileAnd(const char* function) { std::size_t param0end=0; while(true) { function = CompileComparison(function); if(!function) return 0; if(param0end) { if(mData->mByteCode.back() == cNotNot) mData->mByteCode.pop_back(); AddFunctionOpcode(cAnd); --mStackPtr; } if(*function != '&') break; ++function; SkipSpace(function); param0end = mData->mByteCode.size(); } return function; } template const char* FunctionParserBase::CompileExpression(const char* function) { std::size_t param0end=0; while(true) { SkipSpace(function); function = CompileAnd(function); if(!function) return 0; if(param0end) { if(mData->mByteCode.back() == cNotNot) mData->mByteCode.pop_back(); AddFunctionOpcode(cOr); --mStackPtr; } if(*function != '|') break; ++function; param0end = mData->mByteCode.size(); } return function; } template const char* FunctionParserBase::Compile(const char* function) { while(true) { // Check if an identifier appears as first token: SkipSpace(function); unsigned nameLength = readIdentifier(function); if(nameLength > 0 && !(nameLength & 0x80000000U)) { typename Data::InlineVariable inlineVar = { NamePtr(function, nameLength), 0 }; // Check if it's an unknown identifier: typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(inlineVar.mName); if(nameIter == mData->mNamePtrs.end()) { const char* function2 = function + nameLength; SkipSpace(function2); // Check if ":=" follows the unknown identifier: if(function2[0] == ':' && function2[1] == '=') { // Parse the expression that follows and create the // inline variable: function2 = CompileExpression(function2 + 2); if(!function2) return 0; if(*function2 != ';') return function2; inlineVar.mFetchIndex = mStackPtr - 1; mData->mInlineVarNames.push_back(inlineVar); // Continue with the expression after the ';': function = function2 + 1; continue; } } } break; } return CompileExpression(function); } template template inline void FunctionParserBase::PushOpcodeParam (unsigned value) { mData->mByteCode.push_back(value | (PutFlag ? FP_ParamGuardMask : 0u)); if(PutFlag) mData->mHasByteCodeFlags = true; } template template inline void FunctionParserBase::PutOpcodeParamAt (unsigned value, unsigned offset) { mData->mByteCode[offset] = value | (PutFlag ? FP_ParamGuardMask : 0u); if(PutFlag) mData->mHasByteCodeFlags = true; } //=========================================================================== // Function evaluation //=========================================================================== template Value_t FunctionParserBase::Eval(const Value_t* Vars) { if(mData->mParseErrorType != FP_NO_ERROR) return Value_t(0); const unsigned* const byteCode = &(mData->mByteCode[0]); const Value_t* const immed = mData->mImmed.empty() ? 0 : &(mData->mImmed[0]); const unsigned byteCodeSize = unsigned(mData->mByteCode.size()); unsigned IP, DP=0; int SP=-1; #ifdef FP_USE_THREAD_SAFE_EVAL /* If Eval() may be called by multiple threads simultaneously, * then Eval() must allocate its own stack. */ #ifdef FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA /* alloca() allocates room from the hardware stack. * It is automatically freed when the function returns. */ Value_t* const Stack = (Value_t*)alloca(mData->mStackSize*sizeof(Value_t)); #else /* Allocate from the heap. Ensure that it is freed * automatically no matter which exit path is taken. */ struct AutoDealloc { Value_t* ptr; ~AutoDealloc() { delete[] ptr; } } AutoDeallocStack = { new Value_t[mData->mStackSize] }; Value_t*& Stack = AutoDeallocStack.ptr; #endif #else /* No thread safety, so use a global stack. */ std::vector& Stack = mData->mStack; #endif for(IP=0; IP::result == false && (Stack[SP] < Value_t(-1) || Stack[SP] > Value_t(1))) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_acos(Stack[SP]); break; case cAcosh: if(IsComplexType::result == false && Stack[SP] < Value_t(1)) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_acosh(Stack[SP]); break; case cAsin: if(IsComplexType::result == false && (Stack[SP] < Value_t(-1) || Stack[SP] > Value_t(1))) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_asin(Stack[SP]); break; case cAsinh: Stack[SP] = fp_asinh(Stack[SP]); break; case cAtan: Stack[SP] = fp_atan(Stack[SP]); break; case cAtan2: Stack[SP-1] = fp_atan2(Stack[SP-1], Stack[SP]); --SP; break; case cAtanh: if(IsComplexType::result ? (Stack[SP] == Value_t(-1) || Stack[SP] == Value_t(1)) : (Stack[SP] <= Value_t(-1) || Stack[SP] >= Value_t(1))) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_atanh(Stack[SP]); break; case cCbrt: Stack[SP] = fp_cbrt(Stack[SP]); break; case cCeil: Stack[SP] = fp_ceil(Stack[SP]); break; case cCos: Stack[SP] = fp_cos(Stack[SP]); break; case cCosh: Stack[SP] = fp_cosh(Stack[SP]); break; case cCot: { const Value_t t = fp_tan(Stack[SP]); if(t == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/t; break; } case cCsc: { const Value_t s = fp_sin(Stack[SP]); if(s == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/s; break; } case cExp: Stack[SP] = fp_exp(Stack[SP]); break; case cExp2: Stack[SP] = fp_exp2(Stack[SP]); break; case cFloor: Stack[SP] = fp_floor(Stack[SP]); break; case cHypot: Stack[SP-1] = fp_hypot(Stack[SP-1], Stack[SP]); --SP; break; case cIf: if(fp_truth(Stack[SP--])) IP += 2; else { const unsigned* buf = &byteCode[IP+1]; IP = buf[0]; DP = buf[1]; } break; case cInt: Stack[SP] = fp_int(Stack[SP]); break; case cLog: if(IsComplexType::result ? Stack[SP] == Value_t(0) : !(Stack[SP] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP] = fp_log(Stack[SP]); break; case cLog10: if(IsComplexType::result ? Stack[SP] == Value_t(0) : !(Stack[SP] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP] = fp_log10(Stack[SP]); break; case cLog2: if(IsComplexType::result ? Stack[SP] == Value_t(0) : !(Stack[SP] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP] = fp_log2(Stack[SP]); break; case cMax: Stack[SP-1] = fp_max(Stack[SP-1], Stack[SP]); --SP; break; case cMin: Stack[SP-1] = fp_min(Stack[SP-1], Stack[SP]); --SP; break; case cPow: // x:Negative ^ y:NonInteger is failure, // except when the reciprocal of y forms an integer /*if(IsComplexType::result == false && Stack[SP-1] < Value_t(0) && !isInteger(Stack[SP]) && !isInteger(1.0 / Stack[SP])) { mEvalErrorType=3; return Value_t(0); }*/ // x:0 ^ y:negative is failure if(Stack[SP-1] == Value_t(0) && Stack[SP] < Value_t(0)) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP-1] = fp_pow(Stack[SP-1], Stack[SP]); --SP; break; case cTrunc: Stack[SP] = fp_trunc(Stack[SP]); break; case cSec: { const Value_t c = fp_cos(Stack[SP]); if(c == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/c; break; } case cSin: Stack[SP] = fp_sin(Stack[SP]); break; case cSinh: Stack[SP] = fp_sinh(Stack[SP]); break; case cSqrt: if(IsComplexType::result == false && Stack[SP] < Value_t(0)) { mData->mEvalErrorType=2; return Value_t(0); } Stack[SP] = fp_sqrt(Stack[SP]); break; case cTan: Stack[SP] = fp_tan(Stack[SP]); break; case cTanh: Stack[SP] = fp_tanh(Stack[SP]); break; // Misc: case cImmed: Stack[++SP] = immed[DP++]; break; case cJump: { const unsigned* buf = &byteCode[IP+1]; IP = buf[0]; DP = buf[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] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP-1] /= Stack[SP]; --SP; break; case cMod: if(Stack[SP] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP-1] = fp_mod(Stack[SP-1], Stack[SP]); --SP; break; case cEqual: Stack[SP-1] = fp_equal(Stack[SP-1], Stack[SP]); --SP; break; case cNEqual: Stack[SP-1] = fp_nequal(Stack[SP-1], Stack[SP]); --SP; break; case cLess: Stack[SP-1] = fp_less(Stack[SP-1], Stack[SP]); --SP; break; case cLessOrEq: Stack[SP-1] = fp_lessOrEq(Stack[SP-1], Stack[SP]); --SP; break; case cGreater: Stack[SP-1] = fp_less(Stack[SP], Stack[SP-1]); --SP; break; case cGreaterOrEq: Stack[SP-1] = fp_lessOrEq(Stack[SP], Stack[SP-1]); --SP; break; case cNot: Stack[SP] = fp_not(Stack[SP]); break; case cNotNot: Stack[SP] = fp_notNot(Stack[SP]); break; case cAnd: Stack[SP-1] = fp_and(Stack[SP-1], Stack[SP]); --SP; break; case cOr: Stack[SP-1] = fp_or(Stack[SP-1], 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: { const unsigned index = byteCode[++IP]; const unsigned params = mData->mFuncPtrs[index].mParams; const Value_t retVal = mData->mFuncPtrs[index].mRawFuncPtr ? mData->mFuncPtrs[index].mRawFuncPtr(&Stack[SP-params+1]) : mData->mFuncPtrs[index].mFuncWrapperPtr->callFunction (&Stack[SP-params+1]); SP -= int(params)-1; Stack[SP] = retVal; break; } case cPCall: { unsigned index = byteCode[++IP]; unsigned params = mData->mFuncParsers[index].mParams; Value_t retVal = mData->mFuncParsers[index].mParserPtr->Eval (&Stack[SP-params+1]); SP -= int(params)-1; Stack[SP] = retVal; const int error = mData->mFuncParsers[index].mParserPtr->EvalError(); if(error) { mData->mEvalErrorType = error; return 0; } break; } case cFetch: { unsigned stackOffs = byteCode[++IP]; Stack[SP+1] = Stack[stackOffs]; ++SP; break; } #ifdef FP_SUPPORT_OPTIMIZER case cPopNMov: { unsigned stackOffs_target = byteCode[++IP]; unsigned stackOffs_source = byteCode[++IP]; Stack[stackOffs_target] = Stack[stackOffs_source]; SP = stackOffs_target; break; } case cLog2by: if(IsComplexType::result ? Stack[SP-1] == Value_t(0) : !(Stack[SP-1] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP-1] = fp_log2(Stack[SP-1]) * Stack[SP]; --SP; break; case cNop: break; #endif // FP_SUPPORT_OPTIMIZER case cSinCos: fp_sinCos(Stack[SP], Stack[SP+1], Stack[SP]); ++SP; break; case cSinhCosh: fp_sinhCosh(Stack[SP], Stack[SP+1], Stack[SP]); ++SP; break; case cAbsNot: Stack[SP] = fp_absNot(Stack[SP]); break; case cAbsNotNot: Stack[SP] = fp_absNotNot(Stack[SP]); break; case cAbsAnd: Stack[SP-1] = fp_absAnd(Stack[SP-1], Stack[SP]); --SP; break; case cAbsOr: Stack[SP-1] = fp_absOr(Stack[SP-1], Stack[SP]); --SP; break; case cAbsIf: if(fp_absTruth(Stack[SP--])) IP += 2; else { const unsigned* buf = &byteCode[IP+1]; IP = buf[0]; DP = buf[1]; } break; case cDup: Stack[SP+1] = Stack[SP]; ++SP; break; case cInv: if(Stack[SP] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/Stack[SP]; break; case cSqr: Stack[SP] = Stack[SP]*Stack[SP]; break; case cRDiv: if(Stack[SP-1] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP-1] = Stack[SP] / Stack[SP-1]; --SP; break; case cRSub: Stack[SP-1] = Stack[SP] - Stack[SP-1]; --SP; break; case cRSqrt: if(Stack[SP] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1) / fp_sqrt(Stack[SP]); break; #ifdef FP_SUPPORT_COMPLEX_NUMBERS case cReal: Stack[SP] = fp_real(Stack[SP]); break; case cImag: Stack[SP] = fp_imag(Stack[SP]); break; case cArg: Stack[SP] = fp_arg(Stack[SP]); break; case cConj: Stack[SP] = fp_conj(Stack[SP]); break; case cPolar: Stack[SP-1] = fp_polar(Stack[SP-1], Stack[SP]); --SP; break; #endif // Variables: default: Stack[++SP] = Vars[byteCode[IP]-VarBegin]; } } mData->mEvalErrorType=0; return Stack[SP]; } //=========================================================================== // Variable deduction //=========================================================================== namespace { template int deduceVariables(FunctionParserBase& fParser, const char* funcStr, std::string& destVarString, int* amountOfVariablesFound, std::vector* destVarNames, bool useDegrees) { typedef std::set StrSet; StrSet varNames; int oldIndex = -1; while(true) { destVarString.clear(); for(StrSet::iterator iter = varNames.begin(); iter != varNames.end(); ++iter) { if(iter != varNames.begin()) destVarString += ","; destVarString += *iter; } const int index = fParser.Parse(funcStr, destVarString, useDegrees); if(index < 0) break; if(index == oldIndex) return index; unsigned nameLength = readIdentifier(funcStr + index); if(nameLength & 0x80000000U) return index; if(nameLength == 0) return index; varNames.insert(std::string(funcStr + index, nameLength)); oldIndex = index; } if(amountOfVariablesFound) *amountOfVariablesFound = int(varNames.size()); if(destVarNames) destVarNames->assign(varNames.begin(), varNames.end()); return -1; } } template int FunctionParserBase::ParseAndDeduceVariables (const std::string& function, int* amountOfVariablesFound, bool useDegrees) { std::string varString; return deduceVariables(*this, function.c_str(), varString, amountOfVariablesFound, 0, useDegrees); } template int FunctionParserBase::ParseAndDeduceVariables (const std::string& function, std::string& resultVarString, int* amountOfVariablesFound, bool useDegrees) { std::string varString; const int index = deduceVariables(*this, function.c_str(), varString, amountOfVariablesFound, 0, useDegrees); if(index < 0) resultVarString = varString; return index; } template int FunctionParserBase::ParseAndDeduceVariables (const std::string& function, std::vector& resultVars, bool useDegrees) { std::string varString; std::vector vars; const int index = deduceVariables(*this, function.c_str(), varString, 0, &vars, useDegrees); if(index < 0) resultVars.swap(vars); return index; } #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING //=========================================================================== // Bytecode injection //=========================================================================== template void FunctionParserBase::InjectRawByteCode (const unsigned* bytecode, unsigned bytecodeAmount, const Value_t* immed, unsigned immedAmount, unsigned stackSize) { CopyOnWrite(); mData->mByteCode.assign(bytecode, bytecode + bytecodeAmount); mData->mImmed.assign(immed, immed + immedAmount); mData->mStackSize = stackSize; #ifndef FP_USE_THREAD_SAFE_EVAL mData->mStack.resize(stackSize); #endif } //=========================================================================== // Debug output //=========================================================================== #include #include namespace { inline void printHex(std::ostream& dest, unsigned n) { std::ios::fmtflags flags = dest.flags(); dest.width(4); dest.fill('0'); std::hex(dest); //uppercase(dest); dest << n; dest.flags(flags); } void padLine(std::ostringstream& dest, unsigned destLength) { for(std::size_t currentLength = dest.str().length(); currentLength < destLength; ++currentLength) { dest << ' '; } } const struct PowiMuliType { unsigned opcode_square; unsigned opcode_cumulate; unsigned opcode_invert; unsigned opcode_half; unsigned opcode_invhalf; } iseq_powi = {cSqr,cMul,cInv,cSqrt,cRSqrt}, iseq_muli = {~unsigned(0), cAdd,cNeg, ~unsigned(0),~unsigned(0) }; template Value_t ParsePowiMuli( const PowiMuliType& opcodes, const std::vector& ByteCode, unsigned& IP, unsigned limit, std::size_t factor_stack_base, std::vector& stack, bool IgnoreExcess) { Value_t result = Value_t(1); while(IP < limit) { if(ByteCode[IP] == opcodes.opcode_square) { if(!isInteger(result)) break; result *= Value_t(2); ++IP; continue; } if(ByteCode[IP] == opcodes.opcode_invert) { if(result < Value_t(0)) break; result = -result; ++IP; continue; } if(ByteCode[IP] == opcodes.opcode_half) { if(result > Value_t(0) && isEvenInteger(result)) break; if(isInteger(result * Value_t(0.5))) break; result *= Value_t(0.5); ++IP; continue; } if(ByteCode[IP] == opcodes.opcode_invhalf) { if(result > Value_t(0) && isEvenInteger(result)) break; if(isInteger(result * Value_t(-0.5))) break; result *= Value_t(-0.5); ++IP; continue; } unsigned dup_fetch_pos = IP; Value_t lhs = Value_t(1); if(ByteCode[IP] == cFetch) { unsigned index = ByteCode[++IP]; if(index < factor_stack_base || std::size_t(index-factor_stack_base) >= stack.size()) { // It wasn't a powi-fetch after all IP = dup_fetch_pos; break; } lhs = stack[index - factor_stack_base]; // Note: ^This assumes that cFetch of recentmost // is always converted into cDup. goto dup_or_fetch; } if(ByteCode[IP] == cDup) { lhs = result; goto dup_or_fetch; dup_or_fetch: stack.push_back(result); ++IP; Value_t subexponent = ParsePowiMuli (opcodes, ByteCode, IP, limit, factor_stack_base, stack, IgnoreExcess); if(IP >= limit && IgnoreExcess) return lhs*subexponent; if(IP >= limit || ByteCode[IP] != opcodes.opcode_cumulate) { // It wasn't a powi-dup after all IP = dup_fetch_pos; break; } ++IP; // skip opcode_cumulate stack.pop_back(); result += lhs*subexponent; continue; } break; } return result; } template Value_t ParsePowiSequence(const std::vector& ByteCode, unsigned& IP, unsigned limit, std::size_t factor_stack_base, bool IgnoreExcess = false) { std::vector stack; stack.push_back(Value_t(1)); return ParsePowiMuli(iseq_powi, ByteCode, IP, limit, factor_stack_base, stack, IgnoreExcess); } template Value_t ParseMuliSequence(const std::vector& ByteCode, unsigned& IP, unsigned limit, std::size_t factor_stack_base, bool IgnoreExcess = false) { std::vector stack; stack.push_back(Value_t(1)); return ParsePowiMuli(iseq_muli, ByteCode, IP, limit, factor_stack_base, stack, IgnoreExcess); } struct IfInfo { std::pair condition; std::pair thenbranch; unsigned endif_location; IfInfo() : condition(), thenbranch(), endif_location() { } }; } template void FunctionParserBase::PrintByteCode(std::ostream& dest, bool showExpression) const { dest << "Size of stack: " << mData->mStackSize << "\n"; std::ostringstream outputBuffer; std::ostream& output = (showExpression ? outputBuffer : dest); const std::vector& ByteCode = mData->mByteCode; const std::vector& Immed = mData->mImmed; std::vector > stack; std::vector if_stack; for(unsigned IP = 0, DP = 0; IP <= ByteCode.size(); ++IP) { after_powi_or_muli:; std::string n; bool out_params = false; unsigned params = 2, produces = 1, opcode = 0; if(showExpression && !if_stack.empty() && ( // Normal If termination rule: if_stack.back().endif_location == IP // This rule matches when cJumps are threaded: || (IP < ByteCode.size() && ByteCode[IP] == cJump && !if_stack.back().thenbranch.second.empty()) )) { printHex(output, IP); if(if_stack.back().endif_location == IP) output << ": ----- (phi)"; else output << ": ----- (phi+)"; stack.resize(stack.size()+2); std::swap(stack[stack.size()-3], stack[stack.size()-1]); std::swap(if_stack.back().condition, stack[stack.size()-3]); std::swap(if_stack.back().thenbranch, stack[stack.size()-2]); opcode = cIf; params = 3; --IP; if_stack.pop_back(); } else { if(IP >= ByteCode.size()) break; opcode = ByteCode[IP]; if(showExpression && ( opcode == cSqr || opcode == cDup || opcode == cInv || opcode == cSqrt || opcode == cRSqrt || opcode == cFetch )) { unsigned changed_ip = IP; Value_t exponent = ParsePowiSequence (ByteCode, changed_ip, if_stack.empty() ? (unsigned)ByteCode.size() : if_stack.back().endif_location, stack.size()-1); std::string operation_prefix; std::ostringstream operation_value; int prio = 0; if(exponent == Value_t(1.0)) { if(opcode != cDup) goto not_powi_or_muli; Value_t factor = ParseMuliSequence (ByteCode, changed_ip, if_stack.empty() ? (unsigned)ByteCode.size() : if_stack.back().endif_location, stack.size()-1); if(factor == Value_t(1) || factor == Value_t(-1)) goto not_powi_or_muli; operation_prefix = "*"; operation_value << factor; prio = 3; } else { prio = 2; operation_prefix = "^"; operation_value << exponent; } //unsigned explanation_before = changed_ip-2; unsigned explanation_before = changed_ip-1; const char* explanation_prefix = "_"; for(const unsigned first_ip = IP; IP < changed_ip; ++IP) { printHex(output, IP); output << ": "; const char* sep = "|"; if(first_ip+1 == changed_ip) { sep = "="; explanation_prefix = " "; } else if(IP == first_ip) sep = "\\"; else if(IP+1 == changed_ip) sep = "/"; else explanation_prefix = "="; switch(ByteCode[IP]) { case cInv: output << "inv"; break; case cNeg: output << "neg"; break; case cDup: output << "dup"; break; case cSqr: output << "sqr"; break; case cMul: output << "mul"; break; case cAdd: output << "add"; break; case cCbrt: output << "cbrt"; break; case cSqrt: output << "sqrt"; break; case cRSqrt: output << "rsqrt"; break; case cFetch: { unsigned index = ByteCode[++IP]; output << "cFetch(" << index << ")"; break; } default: break; } padLine(outputBuffer, 20); output << sep; if(IP >= explanation_before) { explanation_before = (unsigned)ByteCode.size(); output << explanation_prefix << '[' << (stack.size()-1) << ']'; std::string last = stack.back().second; if(stack.back().first >= prio) last = "(" + last + ")"; output << last; output << operation_prefix; output << operation_value.str(); } else { unsigned p = first_ip; Value_t exp = operation_prefix=="^" ? ParsePowiSequence (ByteCode, p, IP+1, stack.size()-1, true) : ParseMuliSequence (ByteCode, p, IP+1, stack.size()-1, true); std::string last = stack.back().second; if(stack.back().first >= prio) last = "(" + last + ")"; output << " ..." << last; output << operation_prefix; output << exp; } dest << outputBuffer.str() << std::endl; outputBuffer.str(""); } std::string& last = stack.back().second; if(stack.back().first >= prio) last = "(" + last + ")"; last += operation_prefix; last += operation_value.str(); stack.back().first = prio; goto after_powi_or_muli; } not_powi_or_muli:; printHex(output, IP); output << ": "; switch(opcode) { case cIf: { unsigned label = ByteCode[IP+1]+1; output << "jz "; printHex(output, label); params = 1; produces = 0; IP += 2; if_stack.resize(if_stack.size() + 1); std::swap( if_stack.back().condition, stack.back() ); if_stack.back().endif_location = (unsigned) ByteCode.size(); stack.pop_back(); break; } case cAbsIf: { unsigned dp = ByteCode[IP+2]; unsigned label = ByteCode[IP+1]+1; output << "jz_abs " << dp << ","; printHex(output, label); params = 1; produces = 0; IP += 2; if_stack.resize(if_stack.size() + 1); std::swap( if_stack.back().condition, stack.back() ); if_stack.back().endif_location = (unsigned) ByteCode.size(); stack.pop_back(); break; } case cJump: { unsigned dp = ByteCode[IP+2]; unsigned label = ByteCode[IP+1]+1; if(!if_stack.empty() && !stack.empty()) { std::swap(if_stack.back().thenbranch, stack.back()); if_stack.back().endif_location = label; stack.pop_back(); } output << "jump " << dp << ","; printHex(output, label); params = 0; produces = 0; IP += 2; break; } case cImmed: { if(showExpression) { std::ostringstream buf; buf.precision(8); buf << Immed[DP]; stack.push_back( std::make_pair(0, buf.str()) ); } output.precision(8); output << "push " << Immed[DP]; ++DP; produces = 0; break; } case cFCall: { const unsigned index = ByteCode[++IP]; params = mData->mFuncPtrs[index].mParams; static std::string name; name = "f:" + findName(mData->mNamePtrs, index, NameData::FUNC_PTR); n = name.c_str(); out_params = true; break; } case cPCall: { const unsigned index = ByteCode[++IP]; params = mData->mFuncParsers[index].mParams; static std::string name; name = "p:" + findName(mData->mNamePtrs, index, NameData::PARSER_PTR); n = name.c_str(); out_params = true; break; } default: if(IsVarOpcode(opcode)) { if(showExpression) { stack.push_back(std::make_pair(0, (findName(mData->mNamePtrs, opcode, NameData::VARIABLE)))); } output << "push Var" << opcode-VarBegin; produces = 0; } else { switch(OPCODE(opcode)) { case cNeg: n = "neg"; params = 1; 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 cNEqual: n = "neq"; break; case cLess: n = "lt"; break; case cLessOrEq: n = "le"; break; case cGreater: n = "gt"; break; case cGreaterOrEq: n = "ge"; break; case cAnd: n = "and"; break; case cOr: n = "or"; break; case cNot: n = "not"; params = 1; break; case cNotNot: n = "notnot"; params = 1; break; case cDeg: n = "deg"; params = 1; break; case cRad: n = "rad"; params = 1; break; case cFetch: { unsigned index = ByteCode[++IP]; if(showExpression && index < stack.size()) stack.push_back(stack[index]); output << "cFetch(" << index << ")"; produces = 0; break; } #ifdef FP_SUPPORT_OPTIMIZER case cLog2by: n = "log2by"; params = 2; out_params = 1; break; case cPopNMov: { std::size_t a = ByteCode[++IP]; std::size_t b = ByteCode[++IP]; if(showExpression && b < stack.size()) { std::pair stacktop(0, "?"); if(b < stack.size()) stacktop = stack[b]; stack.resize(a); stack.push_back(stacktop); } output << "cPopNMov(" << a << ", " << b << ")"; produces = 0; break; } case cNop: output << "nop"; params = 0; produces = 0; break; #endif case cSinCos: { if(showExpression) { std::pair sin = stack.back(); std::pair cos( 0, "cos(" + sin.second + ")"); sin.first = 0; sin.second = "sin(" + sin.second + ")"; stack.back() = sin; stack.push_back(cos); } output << "sincos"; produces = 0; break; } case cSinhCosh: { if(showExpression) { std::pair sinh = stack.back(); std::pair cosh( 0, "cosh(" + sinh.second + ")"); sinh.first = 0; sinh.second = "sinh(" + sinh.second + ")"; stack.back() = sinh; stack.push_back(cosh); } output << "sinhcosh"; produces = 0; break; } case cAbsAnd: n = "abs_and"; break; case cAbsOr: n = "abs_or"; break; case cAbsNot: n = "abs_not"; params = 1; break; case cAbsNotNot: n = "abs_notnot"; params = 1; break; case cDup: { if(showExpression) stack.push_back(stack.back()); output << "dup"; produces = 0; break; } case cInv: n = "inv"; params = 1; break; case cSqr: n = "sqr"; params = 1; break; case cRDiv: n = "rdiv"; break; case cRSub: n = "rsub"; break; case cRSqrt: n = "rsqrt"; params = 1; break; default: n = Functions[opcode-cAbs].name; params = Functions[opcode-cAbs].params; out_params = params != 1; } } } } if(produces) output << n; if(out_params) output << " (" << params << ")"; if(showExpression) { padLine(outputBuffer, 20); if(produces > 0) { std::ostringstream buf; const char *paramsep = ",", *suff = ""; int prio = 0; bool commutative = false; switch(opcode) { case cIf: buf << "if("; suff = ")"; break; case cAbsIf: buf << "if("; suff = ")"; break; case cOr: prio = 6; paramsep = "|"; commutative = true; break; case cAnd: prio = 5; paramsep = "&"; commutative = true; break; case cAdd: prio = 4; paramsep = "+"; commutative = true; break; case cSub: prio = 4; paramsep = "-"; break; case cMul: prio = 3; paramsep = "*"; commutative = true; break; case cDiv: prio = 3; paramsep = "/"; break; case cPow: prio = 2; paramsep = "^"; break; case cAbsOr: prio = 6; paramsep = "|"; commutative = true; break; case cAbsAnd: prio = 5; paramsep = "&"; commutative = true; break; case cSqr: prio = 2; suff = "^2"; break; case cNeg: buf << "(-("; suff = "))"; break; case cNot: buf << "(!("; suff = "))"; break; default: buf << n << '('; suff = ")"; } const char* sep = ""; for(unsigned a=0; a& prev = stack[stack.size() - params + a]; if(prio > 0 && (prev.first > prio || (prev.first==prio && !commutative))) buf << '(' << prev.second << ')'; else buf << prev.second; } sep = paramsep; } if(stack.size() >= params) stack.resize(stack.size() - params); else stack.clear(); buf << suff; stack.push_back(std::make_pair(prio, buf.str())); //if(n.size() <= 4 && !out_params) padLine(outputBuffer, 20); } //padLine(outputBuffer, 20); output << "= "; if(((opcode == cIf || opcode == cAbsIf) && params != 3) || opcode == cJump #ifdef FP_SUPPORT_OPTIMIZER || opcode == cNop #endif ) output << "(void)"; else if(stack.empty()) output << "[?] ?"; else output << '[' << (stack.size()-1) << ']' << stack.back().second; } if(showExpression) { dest << outputBuffer.str() << std::endl; outputBuffer.str(""); } else output << std::endl; } dest << std::flush; } #endif #ifndef FP_SUPPORT_OPTIMIZER template void FunctionParserBase::Optimize() { // Do nothing if no optimizations are supported. } #endif #define FUNCTIONPARSER_INSTANTIATE_CLASS(type) \ template class FunctionParserBase< type >; #ifndef FP_DISABLE_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(double) #endif #ifdef FP_SUPPORT_FLOAT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(float) #endif #ifdef FP_SUPPORT_LONG_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(long double) #endif #ifdef FP_SUPPORT_LONG_INT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(long) #endif #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(MpfrFloat) #endif #ifdef FP_SUPPORT_GMP_INT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(GmpInt) #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(std::complex) #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(std::complex) #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(std::complex) #endif