summaryrefslogtreecommitdiffstats
path: root/scribus/fparser.cpp
diff options
context:
space:
mode:
authorcraig <craig@11d20701-8431-0410-a711-e3c959e3b870>2012-01-01 11:40:09 +0000
committercraig <craig@11d20701-8431-0410-a711-e3c959e3b870>2012-01-01 11:40:09 +0000
commit7ed83b6c6666eb8b6b104c211ae7e52907350372 (patch)
tree4430b556abac0ad660a0aacf1887d77f85d8be02 /scribus/fparser.cpp
downloadscribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.tar.gz
scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.tar.xz
scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.zip
Branch 1.3.5 tree to 1.4.x tree, goodbye 1.3.x
git-svn-id: svn://scribus.net/branches/Version14x/Scribus@17163 11d20701-8431-0410-a711-e3c959e3b870
Diffstat (limited to 'scribus/fparser.cpp')
-rw-r--r--scribus/fparser.cpp1364
1 files changed, 1364 insertions, 0 deletions
diff --git a/scribus/fparser.cpp b/scribus/fparser.cpp
new file mode 100644
index 0000000..5aae5c6
--- /dev/null
+++ b/scribus/fparser.cpp
@@ -0,0 +1,1364 @@
+/*
+For general Scribus (>=1.3.2) copyright and licensing information please refer
+to the COPYING file provided with the program. Following this notice may exist
+a copyright and/or license notice that predates the release of Scribus 1.3.2
+for which a new license (GPL+exception) is in place.
+*/
+//==============================
+// Function parser v2.8 by Warp
+//==============================
+
+#include "fparser.h"
+#include "fpconfig.h"
+#include "fptypes.h"
+using namespace FUNCTIONPARSERTYPES;
+
+#include "scclocale.h"
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+#include <cmath>
+
+using namespace std;
+
+#ifndef M_PI
+#define M_PI 3.1415926535897932384626433832795
+#endif
+
+namespace
+{
+ 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
+//---------------------------------------------------------------------------
+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),
+ evalRecursionLevel(0)
+{
+ 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),
+ evalRecursionLevel(0)
+{
+ ++(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;
+ evalRecursionLevel = cpy.evalRecursionLevel;
+
+ ++(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 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
+ ""
+ };
+
+
+ // 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 std::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(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(isValidName(name))
+ {
+ const char* n = name.c_str();
+ if(FindVariable(n, data->FuncPtrNames) != data->FuncPtrNames.end() ||
+ FindConstant(n) != data->Constants.end())
+ return false;
+
+ if(checkRecursiveLinking(&parser)) return false;
+
+ copyOnWrite();
+
+ data->FuncParserNames[name] = data->FuncParsers.size();
+ data->FuncParsers.push_back(&parser);
+ return true;
+ }
+ return false;
+}
+
+
+
+// Main parsing function
+// ---------------------
+int FunctionParser::Parse(const std::string& Function,
+ const std::string& Vars,
+ bool useDegrees)
+{
+ copyOnWrite();
+
+ data->Variables.clear();
+
+ if(!ParseVars(Vars, data->Variables))
+ {
+ parseErrorType = INVALID_VARS;
+ return Function.size();
+ }
+ data->varAmount = data->Variables.size(); // this is for Eval()
+
+ const char* Func = Function.c_str();
+
+ parseErrorType = FP_NO_ERROR;
+
+ int Result = CheckSyntax(Func);
+ if(Result>=0) return Result;
+
+ data->useDegreeConversion = useDegrees;
+ if(!Compile(Func)) return Function.size();
+
+ data->Variables.clear();
+
+ parseErrorType = FP_NO_ERROR;
+ return -1;
+}
+
+namespace
+{
+ const char* const fpOperators[] =
+ {
+ "+", "-", "*", "/", "%", "^",
+ "=", "!=", "<=", "<", ">=", ">", "&", "|",
+ 0
+ };
+
+ // Is given char an operator?
+ // (Returns 0 if not, else the size of the operator)
+ inline int IsOperator(const char* F)
+ {
+ for(unsigned opInd = 0; fpOperators[opInd]; ++opInd)
+ {
+ const char* op = fpOperators[opInd];
+ for(unsigned n = 0; F[n] == *op; ++n)
+ {
+ ++op;
+ if(*op == 0) return op-fpOperators[opInd];
+ }
+ }
+ return 0;
+ }
+
+ // skip whitespace
+ inline void sws(const char* F, int& Ind)
+ {
+ while(F[Ind] && isspace(F[Ind])) ++Ind;
+ }
+}
+
+// Returns an iterator to the variable with the same name as 'F', or to
+// Variables.end() if no such variable exists:
+inline FunctionParser::Data::VarMap_t::const_iterator
+FunctionParser::FindVariable(const char* F, const Data::VarMap_t& vars) const
+{
+ if(vars.size())
+ {
+ unsigned ind = 0;
+ while(isalnum(F[ind]) || F[ind] == '_') ++ind;
+ if(ind)
+ {
+ string name(F, ind);
+ return vars.find(name);
+ }
+ }
+ return vars.end();
+}
+
+inline FunctionParser::Data::ConstMap_t::const_iterator
+FunctionParser::FindConstant(const char* F) const
+{
+ if(data->Constants.size())
+ {
+ unsigned ind = 0;
+ while(isalnum(F[ind]) || F[ind] == '_') ++ind;
+ if(ind)
+ {
+ string name(F, ind);
+ return data->Constants.find(name);
+ }
+ }
+ return data->Constants.end();
+}
+
+//---------------------------------------------------------------------------
+// Check function string syntax
+// ----------------------------
+int FunctionParser::CheckSyntax(const char* Function)
+{
+ const Data::VarMap_t& Variables = data->Variables;
+ const Data::ConstMap_t& Constants = data->Constants;
+ const Data::VarMap_t& FuncPtrNames = data->FuncPtrNames;
+ const Data::VarMap_t& FuncParserNames = data->FuncParserNames;
+
+ vector<int> functionParenthDepth;
+
+ int Ind=0, ParenthCnt=0, c;
+ char* Ptr;
+
+ while(true)
+ {
+ sws(Function, Ind);
+ c=Function[Ind];
+
+// Check for valid operand (must appear)
+
+ // Check for leading - or !
+ if(c=='-' || c=='!') { sws(Function, ++Ind); c=Function[Ind]; }
+ if(c==0) { parseErrorType=PREMATURE_EOS; return Ind; }
+
+ // Check for math function
+ bool foundFunc = false;
+ const FuncDefinition* fptr = FindFunction(&Function[Ind]);
+ if(fptr)
+ {
+ Ind += fptr->nameLength;
+ foundFunc = true;
+ }
+ else
+ {
+ // Check for user-defined function
+ Data::VarMap_t::const_iterator fIter =
+ FindVariable(&Function[Ind], FuncPtrNames);
+ if(fIter != FuncPtrNames.end())
+ {
+ Ind += fIter->first.size();
+ foundFunc = true;
+ }
+ else
+ {
+ Data::VarMap_t::const_iterator pIter =
+ FindVariable(&Function[Ind], FuncParserNames);
+ if(pIter != FuncParserNames.end())
+ {
+ Ind += pIter->first.size();
+ foundFunc = true;
+ }
+ }
+ }
+
+ if(foundFunc)
+ {
+ sws(Function, Ind);
+ c = Function[Ind];
+ if(c!='(') { parseErrorType=EXPECT_PARENTH_FUNC; return Ind; }
+
+ int Ind2 = Ind+1;
+ sws(Function, Ind2);
+ if(Function[Ind2] == ')')
+ {
+ Ind = Ind2+1;
+ sws(Function, Ind);
+ c = Function[Ind];
+ // Ugly, but other methods would just be uglier...
+ goto CheckOperator;
+ }
+
+ functionParenthDepth.push_back(ParenthCnt+1);
+ }
+
+ // Check for opening parenthesis
+ if(c=='(')
+ {
+ ++ParenthCnt;
+ sws(Function, ++Ind);
+ if(Function[Ind]==')') { parseErrorType=EMPTY_PARENTH; return Ind;}
+ continue;
+ }
+
+ // Check for number
+ if(isdigit(c) || (c=='.' && isdigit(Function[Ind+1])))
+ {
+ ScCLocale::strtod(&Function[Ind], &Ptr);
+ Ind += int(Ptr-&Function[Ind]);
+ sws(Function, Ind);
+ c = Function[Ind];
+ }
+ else
+ { // Check for variable
+ Data::VarMap_t::const_iterator vIter =
+ FindVariable(&Function[Ind], Variables);
+ if(vIter != Variables.end())
+ Ind += vIter->first.size();
+ else
+ {
+ // Check for constant
+ Data::ConstMap_t::const_iterator cIter =
+ FindConstant(&Function[Ind]);
+ if(cIter != Constants.end())
+ Ind += cIter->first.size();
+ else
+ { parseErrorType=SYNTAX_ERROR; return Ind; }
+ }
+ sws(Function, Ind);
+ c = Function[Ind];
+ }
+
+ // Check for closing parenthesis
+ while(c==')')
+ {
+ if(functionParenthDepth.size() &&
+ functionParenthDepth.back() == ParenthCnt)
+ functionParenthDepth.pop_back();
+ if((--ParenthCnt)<0) { parseErrorType=MISM_PARENTH; return Ind; }
+ sws(Function, ++Ind);
+ c=Function[Ind];
+ }
+
+// If we get here, we have a legal operand and now a legal operator or
+// end of string must follow
+
+ CheckOperator:
+ // Check for EOS
+ if(c==0) break; // The only way to end the checking loop without error
+
+ // Check for operator
+ int opSize = 0;
+ if(c == ',' && !functionParenthDepth.empty() &&
+ functionParenthDepth.back() == ParenthCnt)
+ opSize = 1;
+ else
+ opSize = IsOperator(Function+Ind);
+ if(opSize == 0)
+ { parseErrorType=EXPECT_OPERATOR; return Ind; }
+
+// If we get here, we have an operand and an operator; the next loop will
+// check for another operand (must appear)
+ Ind += opSize;
+ } // while
+
+ // Check that all opened parentheses are also closed
+ if(ParenthCnt>0) { parseErrorType=MISSING_PARENTH; return Ind; }
+
+// The string is ok
+ parseErrorType=FP_NO_ERROR;
+ return -1;
+}
+
+
+// Compile function string to bytecode
+// -----------------------------------
+bool FunctionParser::Compile(const char* Function)
+{
+ if(data->ByteCode) { delete[] data->ByteCode; data->ByteCode=0; }
+ if(data->Immed) { delete[] data->Immed; data->Immed=0; }
+ if(data->Stack) { delete[] data->Stack; data->Stack=0; }
+
+ vector<unsigned> byteCode; byteCode.reserve(1024);
+ tempByteCode = &byteCode;
+
+ vector<double> immed; immed.reserve(1024);
+ tempImmed = &immed;
+
+ data->StackSize = StackPtr = 0;
+
+ CompileExpression(Function, 0);
+ if(parseErrorType != FP_NO_ERROR) return false;
+
+ data->ByteCodeSize = byteCode.size();
+ data->ImmedSize = immed.size();
+
+ if(data->ByteCodeSize)
+ {
+ data->ByteCode = new unsigned[data->ByteCodeSize];
+ memcpy(data->ByteCode, &byteCode[0],
+ sizeof(unsigned)*data->ByteCodeSize);
+ }
+ if(data->ImmedSize)
+ {
+ data->Immed = new double[data->ImmedSize];
+ memcpy(data->Immed, &immed[0],
+ sizeof(double)*data->ImmedSize);
+ }
+ if(data->StackSize)
+ data->Stack = new double[data->StackSize];
+
+ return true;
+}
+
+
+inline void FunctionParser::AddCompiledByte(unsigned c)
+{
+ tempByteCode->push_back(c);
+}
+
+inline void FunctionParser::AddImmediate(double i)
+{
+ tempImmed->push_back(i);
+}
+
+inline void FunctionParser::AddFunctionOpcode(unsigned opcode)
+{
+ if(data->useDegreeConversion)
+ switch(opcode)
+ {
+ case cCos:
+ case cCosh:
+ case cCot:
+ case cCsc:
+ case cSec:
+ case cSin:
+ case cSinh:
+ case cTan:
+ case cTanh:
+ AddCompiledByte(cRad);
+ }
+
+ AddCompiledByte(opcode);
+
+ if(data->useDegreeConversion)
+ switch(opcode)
+ {
+ case cAcos:
+#ifndef NO_ASINH
+ case cAcosh:
+ case cAsinh:
+ case cAtanh:
+#endif
+ case cAsin:
+ case cAtan:
+ case cAtan2:
+ AddCompiledByte(cDeg);
+ }
+}
+
+inline void FunctionParser::incStackPtr()
+{
+ if(++StackPtr > data->StackSize) ++(data->StackSize);
+}
+
+
+// Compile if()
+int FunctionParser::CompileIf(const char* F, int ind)
+{
+ int ind2 = CompileExpression(F, ind, true); // condition
+ sws(F, ind2);
+ if(F[ind2] != ',') { parseErrorType=ILL_PARAMS_AMOUNT; return ind2; }
+ AddCompiledByte(cIf);
+ unsigned curByteCodeSize = tempByteCode->size();
+ AddCompiledByte(0); // Jump index; to be set later
+ AddCompiledByte(0); // Immed jump index; to be set later
+
+ --StackPtr;
+
+ ind2 = CompileExpression(F, ind2+1, true); // then
+ sws(F, ind2);
+ if(F[ind2] != ',') { parseErrorType=ILL_PARAMS_AMOUNT; return ind2; }
+ AddCompiledByte(cJump);
+ unsigned curByteCodeSize2 = tempByteCode->size();
+ unsigned curImmedSize2 = tempImmed->size();
+ AddCompiledByte(0); // Jump index; to be set later
+ AddCompiledByte(0); // Immed jump index; to be set later
+
+ --StackPtr;
+
+ ind2 = CompileExpression(F, ind2+1, true); // else
+ sws(F, ind2);
+ if(F[ind2] != ')') { parseErrorType=ILL_PARAMS_AMOUNT; return ind2; }
+
+ // Set jump indices
+ (*tempByteCode)[curByteCodeSize] = curByteCodeSize2+1;
+ (*tempByteCode)[curByteCodeSize+1] = curImmedSize2;
+ (*tempByteCode)[curByteCodeSize2] = tempByteCode->size()-1;
+ (*tempByteCode)[curByteCodeSize2+1] = tempImmed->size();
+
+ return ind2+1;
+}
+
+int FunctionParser::CompileFunctionParams(const char* F, int ind,
+ unsigned requiredParams)
+{
+ int ind2 = ind;
+ if(requiredParams > 0)
+ {
+ unsigned curStackPtr = StackPtr;
+ ind2 = CompileExpression(F, ind);
+
+ if(StackPtr != curStackPtr+requiredParams)
+ { parseErrorType=ILL_PARAMS_AMOUNT; return ind; }
+
+ StackPtr -= requiredParams - 1;
+ }
+ else
+ {
+ incStackPtr();
+ }
+
+ sws(F, ind2);
+ return ind2+1; // F[ind2] is ')'
+}
+
+// Compiles element
+int FunctionParser::CompileElement(const char* F, int ind)
+{
+ sws(F, ind);
+ char c = F[ind];
+
+ if(c == '(')
+ {
+ ind = CompileExpression(F, ind+1);
+ sws(F, ind);
+ return ind+1; // F[ind] is ')'
+ }
+
+ if(isdigit(c) || c=='.' /*|| c=='-'*/) // Number
+ {
+ const char* startPtr = &F[ind];
+ char* endPtr;
+ double val = ScCLocale::strtod(startPtr, &endPtr);
+ AddImmediate(val);
+ AddCompiledByte(cImmed);
+ incStackPtr();
+ return ind+(endPtr-startPtr);
+ }
+
+ if(isalpha(c) || c == '_') // Function, variable or constant
+ {
+ const FuncDefinition* func = FindFunction(F+ind);
+ if(func) // is function
+ {
+ int ind2 = ind + func->nameLength;
+ sws(F, ind2); // F[ind2] is '('
+ if(strcmp(func->name, "if") == 0) // "if" is a special case
+ {
+ return CompileIf(F, ind2+1);
+ }
+
+#ifndef DISABLE_EVAL
+ unsigned requiredParams =
+ strcmp(func->name, "eval") == 0 ?
+ data->Variables.size() : func->params;
+#else
+ unsigned requiredParams = func->params;
+#endif
+ ind2 = CompileFunctionParams(F, ind2+1, requiredParams);
+ AddFunctionOpcode(func->opcode);
+ return ind2; // F[ind2-1] is ')'
+ }
+
+ Data::VarMap_t::const_iterator vIter =
+ FindVariable(F+ind, data->Variables);
+ if(vIter != data->Variables.end()) // is variable
+ {
+ AddCompiledByte(vIter->second);
+ incStackPtr();
+ return ind + vIter->first.size();
+ }
+
+ Data::ConstMap_t::const_iterator cIter = FindConstant(F+ind);
+ if(cIter != data->Constants.end()) // is constant
+ {
+ AddImmediate(cIter->second);
+ AddCompiledByte(cImmed);
+ incStackPtr();
+ return ind + cIter->first.size();
+ }
+
+ Data::VarMap_t::const_iterator fIter =
+ FindVariable(F+ind, data->FuncPtrNames);
+ if(fIter != data->FuncPtrNames.end()) // is user-defined func pointer
+ {
+ unsigned index = fIter->second;
+
+ int ind2 = ind + fIter->first.length();
+ sws(F, ind2); // F[ind2] is '('
+
+ ind2 = CompileFunctionParams(F, ind2+1,
+ data->FuncPtrs[index].params);
+
+ AddCompiledByte(cFCall);
+ AddCompiledByte(index);
+ return ind2;
+ }
+
+ Data::VarMap_t::const_iterator pIter =
+ FindVariable(F+ind, data->FuncParserNames);
+ if(pIter != data->FuncParserNames.end()) // is user-defined func parser
+ {
+ unsigned index = pIter->second;
+
+ int ind2 = ind + pIter->first.length();
+ sws(F, ind2); // F[ind2] is '('
+
+ ind2 = CompileFunctionParams
+ (F, ind2+1, data->FuncParsers[index]->data->varAmount);
+
+ AddCompiledByte(cPCall);
+ AddCompiledByte(index);
+ return ind2;
+ }
+ }
+
+ parseErrorType = UNEXPECTED_ERROR;
+ return ind;
+}
+
+// Compiles '^'
+int FunctionParser::CompilePow(const char* F, int ind)
+{
+ int ind2 = CompileElement(F, ind);
+ sws(F, ind2);
+
+ while(F[ind2] == '^')
+ {
+ ind2 = CompileUnaryMinus(F, ind2+1);
+ sws(F, ind2);
+ AddCompiledByte(cPow);
+ --StackPtr;
+ }
+
+ return ind2;
+}
+
+// Compiles unary '-'
+int FunctionParser::CompileUnaryMinus(const char* F, int ind)
+{
+ sws(F, ind);
+ if(F[ind] == '-' || F[ind] == '!')
+ {
+ int ind2 = ind+1;
+ sws(F, ind2);
+ ind2 = CompilePow(F, ind2);
+ sws(F, ind2);
+
+ // if we are negating a constant, negate the constant itself:
+ if(F[ind] == '-' && tempByteCode->back() == cImmed)
+ tempImmed->back() = -tempImmed->back();
+
+ // if we are negating a negation, we can remove both:
+ else if((F[ind] == '-' && tempByteCode->back() == cNeg))
+ tempByteCode->pop_back();
+
+ else
+ AddCompiledByte(F[ind] == '-' ? cNeg : cNot);
+
+ return ind2;
+ }
+
+ int ind2 = CompilePow(F, ind);
+ sws(F, ind2);
+ return ind2;
+}
+
+// Compiles '*', '/' and '%'
+int FunctionParser::CompileMult(const char* F, int ind)
+{
+ int ind2 = CompileUnaryMinus(F, ind);
+ sws(F, ind2);
+ char op;
+
+ while((op = F[ind2]) == '*' || op == '/' || op == '%')
+ {
+ ind2 = CompileUnaryMinus(F, ind2+1);
+ sws(F, ind2);
+ switch(op)
+ {
+ case '*': AddCompiledByte(cMul); break;
+ case '/': AddCompiledByte(cDiv); break;
+ case '%': AddCompiledByte(cMod); break;
+ }
+ --StackPtr;
+ }
+
+ return ind2;
+}
+
+// Compiles '+' and '-'
+int FunctionParser::CompileAddition(const char* F, int ind)
+{
+ int ind2 = CompileMult(F, ind);
+ sws(F, ind2);
+ char op;
+
+ while((op = F[ind2]) == '+' || op == '-')
+ {
+ ind2 = CompileMult(F, ind2+1);
+ sws(F, ind2);
+ AddCompiledByte(op=='+' ? cAdd : cSub);
+ --StackPtr;
+ }
+
+ return ind2;
+}
+
+// Compiles '=', '<' and '>'
+int FunctionParser::CompileComparison(const char* F, int ind)
+{
+ int ind2 = CompileAddition(F, ind);
+ sws(F, ind2);
+ char op;
+
+ while((op = F[ind2]) == '=' || op == '<' || op == '>' || op == '!')
+ {
+ int opSize = (F[ind2+1] == '=' ? 2 : 1);
+ ind2 = CompileAddition(F, ind2+opSize);
+ sws(F, ind2);
+ switch(op)
+ {
+ case '=':
+ AddCompiledByte(cEqual); break;
+ case '<':
+ AddCompiledByte(opSize == 1 ? cLess : cLessOrEq); break;
+ case '>':
+ AddCompiledByte(opSize == 1 ? cGreater : cGreaterOrEq); break;
+ case '!':
+ AddCompiledByte(cNEqual); break;
+ }
+ --StackPtr;
+ }
+
+ return ind2;
+}
+
+// Compiles '&'
+int FunctionParser::CompileAnd(const char* F, int ind)
+{
+ int ind2 = CompileComparison(F, ind);
+ sws(F, ind2);
+
+ while(F[ind2] == '&')
+ {
+ ind2 = CompileComparison(F, ind2+1);
+ sws(F, ind2);
+ AddCompiledByte(cAnd);
+ --StackPtr;
+ }
+
+ return ind2;
+}
+
+// Compiles '|'
+int FunctionParser::CompileOr(const char* F, int ind)
+{
+ int ind2 = CompileAnd(F, ind);
+ sws(F, ind2);
+
+ while(F[ind2] == '|')
+ {
+ ind2 = CompileAnd(F, ind2+1);
+ sws(F, ind2);
+ AddCompiledByte(cOr);
+ --StackPtr;
+ }
+
+ return ind2;
+}
+
+// Compiles ','
+int FunctionParser::CompileExpression(const char* F, int ind, bool stopAtComma)
+{
+ int ind2 = CompileOr(F, ind);
+ sws(F, ind2);
+
+ if(stopAtComma) return ind2;
+
+ while(F[ind2] == ',')
+ {
+ ind2 = CompileOr(F, ind2+1);
+ sws(F, ind2);
+ }
+
+ return ind2;
+}
+
+
+// Return parse error message
+// --------------------------
+const char* FunctionParser::ErrorMsg() const
+{
+ if(parseErrorType != FP_NO_ERROR) return ParseErrorMessage[parseErrorType];
+ return 0;
+}
+
+//---------------------------------------------------------------------------
+// Function evaluation
+//---------------------------------------------------------------------------
+//===========================================================================
+namespace
+{
+ inline int doubleToInt(double d)
+ {
+ return d<0 ? -int((-d)+.5) : int(d+.5);
+ }
+
+ inline double Min(double d1, double d2)
+ {
+ return d1<d2 ? d1 : d2;
+ }
+ inline double Max(double d1, double d2)
+ {
+ return d1>d2 ? d1 : d2;
+ }
+
+
+ inline double DegreesToRadians(double degrees)
+ {
+ return degrees*(M_PI/180.0);
+ }
+ inline double RadiansToDegrees(double radians)
+ {
+ return radians*(180.0/M_PI);
+ }
+}
+
+double FunctionParser::Eval(const double* Vars)
+{
+ const unsigned* const ByteCode = data->ByteCode;
+ const double* const Immed = data->Immed;
+ double* const Stack = data->Stack;
+ const unsigned ByteCodeSize = data->ByteCodeSize;
+ unsigned IP, DP=0;
+ int SP=-1;
+
+ for(IP=0; IP<ByteCodeSize; ++IP)
+ {
+ switch(ByteCode[IP])
+ {
+// Functions:
+ case cAbs: Stack[SP] = fabs(Stack[SP]); break;
+ case cAcos: if(Stack[SP] < -1 || Stack[SP] > 1)
+ { evalErrorType=4; return 0; }
+ Stack[SP] = acos(Stack[SP]); break;
+#ifndef NO_ASINH
+ case cAcosh: Stack[SP] = acosh(Stack[SP]); break;
+#endif
+ case cAsin: if(Stack[SP] < -1 || Stack[SP] > 1)
+ { evalErrorType=4; return 0; }
+ Stack[SP] = asin(Stack[SP]); break;
+#ifndef NO_ASINH
+ case cAsinh: Stack[SP] = asinh(Stack[SP]); break;
+#endif
+ case cAtan: Stack[SP] = atan(Stack[SP]); break;
+ case cAtan2: Stack[SP-1] = atan2(Stack[SP-1], Stack[SP]);
+ --SP; break;
+#ifndef NO_ASINH
+ case cAtanh: Stack[SP] = atanh(Stack[SP]); break;
+#endif
+ case cCeil: Stack[SP] = ceil(Stack[SP]); break;
+ case cCos: Stack[SP] = cos(Stack[SP]); break;
+ case cCosh: Stack[SP] = cosh(Stack[SP]); break;
+
+ case cCot:
+ {
+ double t = tan(Stack[SP]);
+ if(t == 0) { evalErrorType=1; return 0; }
+ Stack[SP] = 1/t; break;
+ }
+ case cCsc:
+ {
+ double s = sin(Stack[SP]);
+ if(s == 0) { evalErrorType=1; return 0; }
+ Stack[SP] = 1/s; break;
+ }
+
+
+#ifndef DISABLE_EVAL
+ case cEval:
+ {
+ double retVal = 0;
+ if(evalRecursionLevel == EVAL_MAX_REC_LEVEL)
+ {
+ evalErrorType = 5;
+ }
+ else
+ {
+ data->Stack = new double[data->StackSize];
+ ++evalRecursionLevel;
+ retVal = Eval(&Stack[SP-data->varAmount+1]);
+ --evalRecursionLevel;
+ delete[] data->Stack;
+ data->Stack = Stack;
+ }
+ SP -= data->varAmount-1;
+ Stack[SP] = retVal;
+ break;
+ }
+#endif
+
+ case cExp: Stack[SP] = exp(Stack[SP]); break;
+ case cFloor: Stack[SP] = floor(Stack[SP]); break;
+
+ case cIf:
+ {
+ unsigned jumpAddr = ByteCode[++IP];
+ unsigned immedAddr = ByteCode[++IP];
+ if(doubleToInt(Stack[SP]) == 0)
+ {
+ IP = jumpAddr;
+ DP = immedAddr;
+ }
+ --SP; break;
+ }
+
+ case cInt: Stack[SP] = floor(Stack[SP]+.5); break;
+ case cLog: if(Stack[SP] <= 0) { evalErrorType=3; return 0; }
+ Stack[SP] = log(Stack[SP]); break;
+ case cLog10: if(Stack[SP] <= 0) { evalErrorType=3; return 0; }
+ Stack[SP] = log10(Stack[SP]); break;
+ case cMax: Stack[SP-1] = Max(Stack[SP-1], Stack[SP]);
+ --SP; break;
+ case cMin: Stack[SP-1] = Min(Stack[SP-1], Stack[SP]);
+ --SP; break;
+ case cSec:
+ {
+ double c = cos(Stack[SP]);
+ if(c == 0) { evalErrorType=1; return 0; }
+ Stack[SP] = 1/c; break;
+ }
+ case cSin: Stack[SP] = sin(Stack[SP]); break;
+ case cSinh: Stack[SP] = sinh(Stack[SP]); break;
+ case cSqrt: if(Stack[SP] < 0) { evalErrorType=2; return 0; }
+ Stack[SP] = sqrt(Stack[SP]); break;
+ case cTan: Stack[SP] = tan(Stack[SP]); break;
+ case cTanh: Stack[SP] = tanh(Stack[SP]); break;
+
+
+// Misc:
+ case cImmed: Stack[++SP] = Immed[DP++]; break;
+ case cJump: DP = ByteCode[IP+2];
+ IP = ByteCode[IP+1];
+ break;
+
+// Operators:
+ case cNeg: Stack[SP] = -Stack[SP]; break;
+ case cAdd: Stack[SP-1] += Stack[SP]; --SP; break;
+ case cSub: Stack[SP-1] -= Stack[SP]; --SP; break;
+ case cMul: Stack[SP-1] *= Stack[SP]; --SP; break;
+ case cDiv: if(Stack[SP] == 0) { evalErrorType=1; return 0; }
+ Stack[SP-1] /= Stack[SP]; --SP; break;
+ case cMod: if(Stack[SP] == 0) { evalErrorType=1; return 0; }
+ Stack[SP-1] = fmod(Stack[SP-1], Stack[SP]);
+ --SP; break;
+ case cPow: Stack[SP-1] = pow(Stack[SP-1], Stack[SP]);
+ --SP; break;
+
+#ifdef FP_EPSILON
+ case cEqual: Stack[SP-1] =
+ (fabs(Stack[SP-1]-Stack[SP]) <= FP_EPSILON);
+ --SP; break;
+ case cNEqual: Stack[SP-1] =
+ (fabs(Stack[SP-1] - Stack[SP]) >= FP_EPSILON);
+ --SP; break;
+ case cLess: Stack[SP-1] = (Stack[SP-1] < Stack[SP]-FP_EPSILON);
+ --SP; break;
+ case cLessOrEq: Stack[SP-1] = (Stack[SP-1] <= Stack[SP]+FP_EPSILON);
+ --SP; break;
+ case cGreater: Stack[SP-1] = (Stack[SP-1]-FP_EPSILON > Stack[SP]);
+ --SP; break;
+ case cGreaterOrEq: Stack[SP-1] =
+ (Stack[SP-1]+FP_EPSILON >= Stack[SP]);
+ --SP; break;
+#else
+ case cEqual: Stack[SP-1] = (Stack[SP-1] == Stack[SP]);
+ --SP; break;
+ case cNEqual: Stack[SP-1] = (Stack[SP-1] != Stack[SP]);
+ --SP; break;
+ case cLess: Stack[SP-1] = (Stack[SP-1] < Stack[SP]);
+ --SP; break;
+ case cLessOrEq: Stack[SP-1] = (Stack[SP-1] <= Stack[SP]);
+ --SP; break;
+ case cGreater: Stack[SP-1] = (Stack[SP-1] > Stack[SP]);
+ --SP; break;
+ case cGreaterOrEq: Stack[SP-1] = (Stack[SP-1] >= Stack[SP]);
+ --SP; break;
+#endif
+
+ case cAnd: Stack[SP-1] =
+ (doubleToInt(Stack[SP-1]) &&
+ doubleToInt(Stack[SP]));
+ --SP; break;
+ case cOr: Stack[SP-1] =
+ (doubleToInt(Stack[SP-1]) ||
+ doubleToInt(Stack[SP]));
+ --SP; break;
+ case cNot: Stack[SP] = !doubleToInt(Stack[SP]); break;
+
+// Degrees-radians conversion:
+ case cDeg: Stack[SP] = RadiansToDegrees(Stack[SP]); break;
+ case cRad: Stack[SP] = DegreesToRadians(Stack[SP]); break;
+
+// User-defined function calls:
+ case cFCall:
+ {
+ unsigned index = ByteCode[++IP];
+ unsigned params = data->FuncPtrs[index].params;
+ double retVal =
+ data->FuncPtrs[index].ptr(&Stack[SP-params+1]);
+ SP -= int(params)-1;
+ Stack[SP] = retVal;
+ break;
+ }
+
+ case cPCall:
+ {
+ unsigned index = ByteCode[++IP];
+ unsigned params = data->FuncParsers[index]->data->varAmount;
+ double retVal =
+ data->FuncParsers[index]->Eval(&Stack[SP-params+1]);
+ SP -= int(params)-1;
+ Stack[SP] = retVal;
+ break;
+ }
+
+
+#ifdef SUPPORT_OPTIMIZER
+ case cVar: break; // Paranoia. These should never exist
+ case cDup: Stack[SP+1] = Stack[SP]; ++SP; break;
+ case cInv:
+ if(Stack[SP] == 0.0) { evalErrorType=1; return 0; }
+ Stack[SP] = 1.0/Stack[SP];
+ break;
+#endif
+
+// Variables:
+ default:
+ Stack[++SP] = Vars[ByteCode[IP]-VarBegin];
+ }
+ }
+
+ evalErrorType=0;
+ return Stack[SP];
+}
+
+
+#ifdef FUNCTIONPARSER_SUPPORT_DEBUG_OUTPUT
+namespace
+{
+ inline void printHex(std::ostream& dest, unsigned n)
+ {
+ dest.width(8); dest.fill('0'); hex(dest); //uppercase(dest);
+ dest << n;
+ }
+}
+
+void FunctionParser::PrintByteCode(std::ostream& dest) const
+{
+ const unsigned* const ByteCode = data->ByteCode;
+ const double* const Immed = data->Immed;
+
+ for(unsigned IP=0, DP=0; IP<data->ByteCodeSize; ++IP)
+ {
+ printHex(dest, IP);
+ dest << ": ";
+
+ unsigned opcode = ByteCode[IP];
+
+ switch(opcode)
+ {
+ case cIf:
+ dest << "jz\t";
+ printHex(dest, ByteCode[IP+1]+1);
+ dest << endl;
+ IP += 2;
+ break;
+
+ case cJump:
+ dest << "jump\t";
+ printHex(dest, ByteCode[IP+1]+1);
+ dest << endl;
+ IP += 2;
+ break;
+ case cImmed:
+ dest.precision(10);
+ dest << "push\t" << Immed[DP++] << endl;
+ break;
+
+ case cFCall:
+ {
+ unsigned index = ByteCode[++IP];
+ Data::VarMap_t::const_iterator iter =
+ data->FuncPtrNames.begin();
+ while(iter->second != index) ++iter;
+ dest << "fcall\t" << iter->first
+ << " (" << data->FuncPtrs[index].params << ")" << endl;
+ break;
+ }
+
+ case cPCall:
+ {
+ unsigned index = ByteCode[++IP];
+ Data::VarMap_t::const_iterator iter =
+ data->FuncParserNames.begin();
+ while(iter->second != index) ++iter;
+ dest << "pcall\t" << iter->first
+ << " (" << data->FuncParsers[index]->data->varAmount
+ << ")" << endl;
+ break;
+ }
+
+ default:
+ if(opcode < VarBegin)
+ {
+ string n;
+ unsigned params = 1;
+ switch(opcode)
+ {
+ case cNeg: n = "neg"; break;
+ case cAdd: n = "add"; break;
+ case cSub: n = "sub"; break;
+ case cMul: n = "mul"; break;
+ case cDiv: n = "div"; break;
+ case cMod: n = "mod"; break;
+ case cPow: n = "pow"; break;
+ case cEqual: n = "eq"; break;
+ case 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"; break;
+ case cDeg: n = "deg"; break;
+ case cRad: n = "rad"; break;
+
+#ifndef DISABLE_EVAL
+ case cEval: n = "call\t0"; break;
+#endif
+
+#ifdef SUPPORT_OPTIMIZER
+ case cVar: n = "(var)"; break;
+ case cDup: n = "dup"; break;
+ case cInv: n = "inv"; break;
+#endif
+
+ default:
+ n = Functions[opcode-cAbs].name;
+ params = Functions[opcode-cAbs].params;
+ }
+ dest << n;
+ if(params != 1) dest << " (" << params << ")";
+ dest << endl;
+ }
+ else
+ {
+ dest << "push\tVar" << opcode-VarBegin << endl;
+ }
+ }
+ }
+}
+#endif
+
+
+
+
+#ifndef SUPPORT_OPTIMIZER
+void FunctionParser::MakeTree(void *) const {}
+void FunctionParser::Optimize()
+{
+ // Do nothing if no optimizations are supported.
+}
+#endif