1 /* 2 KmPlot - a math. function plotter for the KDE-Desktop 3 4 SPDX-FileCopyrightText: 1998, 1999, 2000, 2002 Klaus-Dieter Möller <kd.moeller@t-online.de> 5 SPDX-FileCopyrightText: 2006 David Saxton <david@bluehaze.org> 6 7 This file is part of the KDE Project. 8 KmPlot is part of the KDE-EDU Project. 9 10 SPDX-License-Identifier: GPL-2.0-or-later 11 12 */ 13 14 /** @file parser.h 15 * \brief Contains the parser core class Parser. */ 16 17 #ifndef parser_included 18 #define parser_included 19 20 #include <QMap> 21 #include <QObject> 22 #include <QVector> 23 24 #include "constants.h" 25 #include "function.h" 26 #include "vector.h" 27 28 29 // Various mathematical symbols 30 #define PiSymbol QChar(0x3c0) 31 #define InfinitySymbol QChar(0x221e) 32 #define PmSymbol QChar(0xb1) 33 #define AbsSymbol QChar(0x2223) 34 #define SqrtSymbol QChar(0x221a) 35 #define MinusSymbol QChar(0x2212) 36 #define SubscriptZeroSymbol QChar(0x2080) 37 #define GeSymbol QChar(0x2265) 38 #define LeSymbol QChar(0x2264) 39 40 41 class Parser; 42 43 // Voreinstellungen bei Verwendung des Standardkonstruktors : 44 45 #define STACKSIZE 1000 ///< stack depth 46 47 //@{ 48 /** Token type. */ 49 enum Token 50 { 51 KONST, // 0 - double value follows 52 VAR, // 1 - get a parameter (e.g. x or k) 53 PUSH, // 2 - push value to stack 54 PLUS, // 3 - add 55 MINUS, // 4 - subtract 56 PM, // 5 - plus-minus; add or subtract depending on the current signature 57 MULT, // 6 - multiply 58 DIV, // 7 - divide 59 POW, // 8 - exponentiate 60 NEG, // 9 - negate 61 FKT_1, // 10 - address to function with 1 argument follows 62 FKT_N, // 11 - address to functions with an indefinite number of arguments follows 63 UFKT, // 12 - address to user defined function follows 64 SQRT, // 13 - take square root 65 FACT, // 14 - take factorial 66 GT, // 15 - greater than 67 GE, // 16 - greater than or equal 68 LT, // 17 - less than 69 LE, // 18 - less than or equal 70 ENDE, // 19 - end of function 71 ERROR // 20 - error in function 72 }; 73 74 75 const int legendreCount = 7; // number of legendre polynomials we allow for 76 const int ScalarCount = 40+legendreCount; // number of mathematical scalar functions 77 const int VectorCount = 3; // number of vector functions 78 //@} 79 80 //@{ 81 /** Predefined mathematical function with one variable. */ 82 double sign(double x); 83 double heaviside(double x); 84 double sqr(double x); 85 86 double lsec(double x); 87 double lcosec(double x); 88 double lcot(double x); 89 double larcsec(double x); 90 double larccosec(double x); 91 double larccot(double x); 92 93 double sech(double x); 94 double cosech(double x); 95 double coth(double x); 96 double arsech(double x); 97 double arcosech(double x); 98 double arcoth(double x); 99 100 double lcos(double x); 101 double lsin(double x); 102 double ltan(double x); 103 104 double larccos(double x); 105 double larcsin(double x); 106 double larctan(double x); 107 108 double legendre0(double x); 109 double legendre1(double x); 110 double legendre2(double x); 111 double legendre3(double x); 112 double legendre4(double x); 113 double legendre5(double x); 114 double legendre6(double x); 115 116 double factorial(double x); 117 double lerf(double x); 118 double lerfc(double x); 119 120 /** Predefined mathematical functions with an indefinite number of variables. */ 121 double min( const Vector & x ); 122 double max( const Vector & x ); 123 double mod( const Vector & x ); 124 125 126 struct ScalarFunction 127 { 128 QString name1; 129 QString name2; 130 double (*mfadr)(double); 131 }; 132 133 134 struct VectorFunction 135 { 136 QString name; 137 double (*mfadr)(const Vector &); 138 }; 139 140 141 /** 142 * Fixes user-entered expressions into a form that can be handled by the 143 * parser. Also keeps track of how the string was modified, so that if an error 144 * occurs, then the correct position can be reported to the user. 145 * \note The convention used here is that the first letter in a string 146 * is at position zero. 147 */ 148 class ExpressionSanitizer 149 { 150 public: 151 explicit ExpressionSanitizer( Parser * parent ); 152 153 /** 154 * Lots of changes to make it happy for the parser (e.g. adding extra 155 * *-characters, remove spaces, replace the locale .-character with '.', 156 * etc). This function will initialize m_evalMap. 157 * \param str The string to be fixed. 158 */ 159 void fixExpression( QString * str ); 160 /** 161 * \return the position in the input string (as given to fixExpression) 162 * that corresponds to the outputted string. 163 */ 164 int realPos( int evalPos ); 165 166 protected: 167 /** 168 * Maps the position of the string returned by fixExpression to that 169 * passed to it. This is so that if the parser comes across an error in the 170 * sanitized expression, this gives the corresponding position in the user 171 * string. 172 */ 173 QVector<int> m_map; 174 175 void remove( const QString & str ); 176 void remove( const QChar & str ); 177 void replace( QChar before, QChar after ); 178 void replace( QChar before, const QString & after ); 179 void replace( int pos, int len, const QString & after ); 180 void replace( const QString & before, const QString & after ); 181 void insert( int i, QChar ch ); 182 void append( QChar str ); 183 void stripWhiteSpace(); 184 185 /** 186 * Prints the map and str to stdout; for debugging purposes. 187 */ 188 void displayMap(); 189 190 QString * m_str; 191 192 QString m_decimalSymbol; 193 Parser * m_parser; 194 }; 195 196 197 /** @short Parser. 198 * 199 * Tokenizes a function equation to be evaluated. 200 */ 201 class Parser : public QObject 202 { 203 Q_OBJECT 204 public: 205 206 enum Error 207 { 208 ParseSuccess, 209 SyntaxError, 210 MissingBracket, 211 StackOverflow, 212 FunctionNameReused , ///< function name already used 213 RecursiveFunctionCall, 214 EmptyFunction, 215 NoSuchFunction, 216 ZeroOrder, ///< zero-order differential 217 TooManyPM, ///< too many plus-minus symbols 218 InvalidPM, ///< Not allowed to have a plus-minus symbol, e.g. in a constant expression 219 TooManyArguments, ///< Too many arguments in a function, e.g. "f(x,a,b,c)" 220 IncorrectArgumentCount ///< wrong number of arguments being passed to a function 221 }; 222 223 ~Parser(); 224 225 /** 226 * \param includeAliases whether to return function aliases (e.g. 227 * arsinh for arcsinh). 228 * \return the list of predefined function names. 229 */ 230 QStringList predefinedFunctions( bool includeAliases ) const; 231 /** 232 * \return the list of user defined function names. 233 */ 234 QStringList userFunctions() const; 235 /** 236 * @return A string that is safe to use as a number in a string to be 237 * parsed. This is needed as e.g. "1.2e-3" is not allowed (e is a 238 * constant) - so cannot use the QString::number. 239 */ 240 static QString number( double value ); 241 /** 242 * Calls the array version of this function, after inserting the value 243 * of the equation's parameter into x. 244 */ 245 double fkt( Equation * it, double x ); 246 double fkt( uint id, int eq, double x ); 247 /** 248 * Returns the result of a calculation. \p x are parameters for the 249 * function (which are not necessarily all used). 250 */ 251 double fkt( Equation * it, const Vector & x ); 252 /** 253 * Evaluates the given expression. 254 * \param str the given expression. 255 * \param error if non-null, then will be set to the parser error (or 256 * ParserSuccess if no errors). 257 * \param errorPosition will be set to the position of the error (if 258 * there is one). 259 */ 260 double eval( const QString & str, Error * error = 0, int * errorPosition = 0 ); 261 /** 262 * Adds a user defined function with the given equation. The new 263 * function's ID-number is returned. \p force is used to force use of 264 * \p str1, \p str2, even if they cannot be parsed. 265 */ 266 int addFunction( const QString & str1, const QString & str2, Function::Type type, bool force = false ); 267 /** 268 * Removes the function with the given id. 269 */ 270 bool removeFunction( uint id ); 271 bool removeFunction( Function *item); 272 /** 273 * Removes all functions. 274 */ 275 void removeAllFunctions(); 276 /** 277 * Returns the ID-number of the function "name". If the function 278 * couldn't be found, -1 is returned. 279 */ 280 int fnameToID(const QString &name); 281 /** 282 * \return An error string appropriate for the given error. 283 */ 284 static QString errorString( Error error ); 285 /** 286 * Displays an error dialog appropriate to \p error. 287 */ 288 void displayErrorDialog( Error error ); 289 /** 290 * \return the number of radians per angle-unit that the user has 291 * selected (i.e. this will return 1.0 if the user has selected 292 * radians; and PI/180 if the user has selected degrees). 293 */ radiansPerAngleUnit()294 static double radiansPerAngleUnit() { return m_radiansPerAngleUnit; } 295 296 enum AngleMode { Radians = 0, Degrees = 1 }; 297 /** 298 * Sets the angle mode (in which the calculations are performed). 299 */ 300 void setAngleMode( AngleMode mode ); 301 /** 302 * Initializes the function for evaluation. Called after the functions 303 * fstr is set. 304 */ 305 void initEquation( Equation * equation, Error * error = 0, int * errorPosition = 0 ); 306 307 uint getNewId(); /// Returns the next ID-number 308 uint countFunctions(); /// Returns how many functions there are 309 310 /// The constants used by the parser constants()311 Constants * constants() const { return m_constants; } 312 313 /// @return the function with the given id 314 Function * functionWithID( int id ) const; 315 316 /// Points to the array of user defined functions, index by their IDs. 317 QMap<int, Function *> m_ufkt; 318 319 /// Reparses all functions, e.g. for when the value of a constant changes 320 void reparseAllFunctions(); 321 322 signals: 323 /// emitted when a function is deleted 324 void functionRemoved( int id ); 325 /// emitted when a function is added 326 void functionAdded( int id ); 327 328 private: 329 /** Mathematical function. */ 330 static ScalarFunction scalarFunctions[ScalarCount]; 331 static VectorFunction vectorFunctions[VectorCount]; 332 333 void heir0(); 334 void heir1(); 335 void heir2(); 336 void heir3(); 337 void heir4(); 338 void heir5(); 339 void primary(); 340 bool tryFunction(); 341 bool tryPredefinedFunction(); 342 bool tryUserFunction(); 343 bool tryVariable(); 344 bool tryConstant(); 345 bool tryNumber(); 346 void addToken( Token token ); 347 void addConstant(double); 348 void adduint(uint); 349 void addfptr(double(*)(double)); 350 void addfptr( double(*)(const Vector &), int argCount ); 351 /** 352 * \p id Id of the function 353 * \p eq_id Which equation of the function to use 354 * \p args The number of variables being passed to the function 355 */ 356 void addfptr( uint id, uint eq_id, uint args ); 357 /** 358 * Attempts to \p string to the current evaluation text. If the text at 359 * the current evaluation position is matched, then the evaluation 360 * position is incremented past the length of the string and true is 361 * returned. Else the evaluation position remains unchanged, and false 362 * is returned. 363 */ 364 bool match( const QString & string ); 365 /** 366 * Continues to read the expression inside a brackets of a vector 367 * function until get to the end of the argument list. 368 * \return the number of arguments 369 */ 370 int readFunctionArguments(); 371 372 void growEqMem( int growth ); 373 QByteArray *mem; ///< Pointer to the array of tokens for the current equation being parsed 374 char *mptr; ///< Pointer to the next position of insertion for the parsed equation data 375 double * m_stack; 376 double * stkptr; 377 QString m_eval; 378 int m_evalPos; 379 int m_nextFunctionID; 380 /// @return the m_eval starting at m_evalPos 381 QString evalRemaining(); 382 QString m_evalRemaining; 383 Equation * m_currentEquation; // Pointer to the current function 384 Equation * m_ownEquation; ///< used for parsing constants, etc, and ensures that m_currentEquation is never null 385 static double m_radiansPerAngleUnit; 386 Constants * m_constants; 387 ExpressionSanitizer m_sanitizer; 388 int m_pmAt; ///< When parsing an expression, which plus-minus symbol at 389 Error * m_error; 390 391 private: 392 friend class XParser; 393 friend class ExpressionSanitizer; 394 Parser(); 395 }; 396 397 #endif // parser_included 398