/* * KmPlot - a math. function plotter for the KDE-Desktop * * Copyright (C) 1998, 1999 Klaus-Dieter M�ler * 2000, 2002 kd.moeller@t-online.de * * This file is part of the KDE Project. * KmPlot is part of the KDE-EDU Project. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. * */ // standard c(++) includes #include #include #include //KDE includes #include #include #include // local includes #include "parser.h" #include "settings.h" #include "xparser.h" double Parser::m_anglemode = 0; /// List of predefined functions. Parser::Mfkt Parser::mfkttab[ FANZ ]= { {"tanh", ltanh}, // Tangens hyperbolicus {"tan", ltan}, // Tangens {"sqrt", sqrt}, // Square root {"sqr", sqr}, // Square {"sinh", lsinh}, // Sinus hyperbolicus {"sin", lsin}, // Sinus {"sign", sign}, // Signum {"sech", sech}, // Secans hyperbolicus {"sec", sec}, // Secans {"log", llog}, // Logarithm base 10 {"ln", ln}, // Logarithm base e {"exp", exp}, // Exponential function base e {"coth", coth}, // Co-Tangens hyperbolicus {"cot", cot}, // Co-Tangens = 1/tan {"cosh", lcosh}, // Cosinus hyperbolicus {"cosech", cosech}, // Co-Secans hyperbolicus {"cosec", cosec}, // Co-Secans {"cos", lcos}, // Cosinus {"artanh", artanh}, // Area-tangens hyperbolicus = inverse of tanh {"arsinh", arsinh}, // Area-sinus hyperbolicus = inverse of sinh {"arsech", arsech}, // Area-secans hyperbolicus = invers of sech {"arctanh", artanh}, // The same as artanh {"arcsinh", arsinh}, // The same as arsinh {"arccosh", arcosh}, // The same as arcosh {"arctan", arctan}, // Arcus tangens = inverse of tan {"arcsin", arcsin}, // Arcus sinus = inverse of sin {"arcsec", arcsec}, // Arcus secans = inverse of sec {"arcoth", arcoth}, // Area-co-tangens hyperbolicus = inverse of coth {"arcosh", arcosh}, // Area-cosinus hyperbolicus = inverse of cosh {"arcosech", arcosech}, // Area-co-secans hyperbolicus = inverse of cosech {"arccot", arccot}, // Arcus co-tangens = inverse of cotan {"arccosec", arccosec}, // Arcus co-secans = inverse of cosec {"arccos", arccos}, // Arcus cosinus = inverse of cos {"abs", fabs} // Absolute value }; Ufkt::Ufkt() { id = 0; mem = 0; mptr = 0; k = 0; oldy = 0; f_mode = true; f1_mode = false; f2_mode = false; integral_mode = false; integral_use_precision = false; linewidth = 0; f1_linewidth = 0; f2_linewidth = 0; integral_linewidth = 0; double dmin = 0.0; dmax = 0.0; oldyprim = 0.0; oldx = 0.0; starty = 0.0; startx = 0.0; integral_precision = 0.0; use_slider = -1; usecustomxmin = false; usecustomxmax = false; } Ufkt::~Ufkt() { } Parser::Parser() { ps_init(); } void Parser::ps_init() { evalflg=0; Ufkt temp; temp.fname = temp.fvar = temp.fpar = temp.fstr = ""; temp.mem=new unsigned char [MEMSIZE]; ufkt.append(temp ); current_item = ufkt.begin(); } Parser::~Parser() { kdDebug() << "Exiting......" << endl; for( TQValueVector::iterator it = ufkt.begin(); it != ufkt.end(); ++it) { kdDebug() << "Deleting something... :-)" << endl; delete [](*it).mem; } } void Parser::setAngleMode(int angle) { if(angle==0) m_anglemode = 1; else m_anglemode = M_PI/180; } void Parser::setDecimalSymbol(const TQString c) { m_decimalsymbol = c; } double Parser::anglemode() { return m_anglemode; } uint Parser::getNewId() { uint i = 0; bool found = false; while (1 ) { found = false; for( TQValueVector::iterator it = ufkt.begin(); it != ufkt.end(); ++it) { if (it->id == i && !it->fname.isEmpty()) { found = true; break; } } if (!found) return i; ++i; } } double Parser::eval(TQString str) { stack=new double [STACKSIZE]; stkptr=stack; evalflg=1; fix_expression(str,0); if ( str.contains('y')!=0) { err=9; delete []stack; return 0; } for (uint i=0;i=0 && ix<(int)ufkt.count() ) // range check { if ( !( ufkt.count()==1 && ufkt[0].fname.isEmpty() ) ) return ufkt[ix].id; } return -1; } int Parser::ixValue(uint const id) { int ix=0; for( TQValueVector::iterator it = ufkt.begin(); it != ufkt.end(); ++it) { if ( it->id ==id) return ix; ix++; } return -1; } double Parser::fkt(uint const id, double const x) { for( TQValueVector::iterator it = ufkt.begin(); it != ufkt.end(); ++it) { if ( it->id == id) return fkt(it,x); } err=13; return 0; } double Parser::fkt(Ufkt *it, double const x) { double *pd, (**pf)(double); double *stack, *stkptr; uint *puf; it->mptr=it->mem; stack=stkptr= new double [STACKSIZE]; while(1) { switch(*it->mptr++) { case KONST: pd=(double*)it->mptr; *stkptr=*pd++; it->mptr=(unsigned char*)pd; break; case XWERT: *stkptr=x; break; case YWERT: *stkptr=it->oldy; break; case KWERT: *stkptr=it->k; break; case PUSH: ++stkptr; break; case PLUS: stkptr[-1]+=*stkptr; --stkptr; break; case MINUS: stkptr[-1]-=*stkptr; --stkptr; break; case MULT: stkptr[-1]*=*stkptr; --stkptr; break; case DIV: if(*stkptr==0.)*(--stkptr)=HUGE_VAL; else { stkptr[-1]/=*stkptr; --stkptr; } break; case POW: stkptr[-1]=pow(*(stkptr-1), *stkptr); --stkptr; break; case NEG: *stkptr=-*stkptr; break; case FKT: pf=(double(**)(double))it->mptr; *stkptr=(*pf++)(*stkptr); it->mptr=(unsigned char*)pf; break; case UFKT: { puf=(uint*)it->mptr; uint id = *puf++; for( TQValueVector::iterator ite = ufkt.begin(); ite != ufkt.end(); ++ite) { if ( ite->id == id) { *stkptr=fkt(ite, *stkptr); break; } } it->mptr=(unsigned char*)puf; break; } case ENDE: double const erg=*stkptr; delete [] stack; return erg; } } } int Parser::addfkt(TQString str) { TQString const extstr = str; stkptr=stack=0; err=0; errpos=1; const int p1=str.find('('); int p2=str.find(','); const int p3=str.find(")="); fix_expression(str,p1+4); if(p1==-1 || p3==-1 || p1>p3) { err=4; return -1; } if ( p3+2 == (int) str.length()) //empty function { err=11; return -1; } if(p2==-1 || p2>p3) p2=p3; if( fnameToId(str.left(p1))!=-1 ) { err=8; return -1; } else err=0; if (str.mid(p1+1, p2-p1-1) == "e") { err=4; return -1; } if ( ufkt.begin()->fname.isEmpty() ) { ufkt.begin()->id = 0; //kdDebug() << "ufkt.begin()->id:" << ufkt.begin()->id << endl; } else { Ufkt temp; if ( !temp.fstr.isEmpty() && temp.fstr.at(0) == 'y') temp.id = ufkt.last().id; //the function belongs to the last inserted function else temp.id = getNewId(); temp.mem=new unsigned char [MEMSIZE]; ufkt.append(temp ); } TQString const fname = str.left(p1); Ufkt *temp = &ufkt.last(); temp->fstr=extstr; temp->mptr = 0; temp->fname=fname; temp->fvar=str.mid(p1+1, p2-p1-1); if(p2fpar=str.mid(p2+1, p3-p2-1); else temp->fpar=""; //.resize(1); kdDebug() << "temp.id:" << temp->id << endl; if ( temp->fname != temp->fname.lower() ) //isn't allowed to contain capital letters { delfkt(temp); err=12; return -1; } current_item = temp; mem=mptr=temp->mem; lptr=(str.latin1())+p3+2; heir1(); if(*lptr!=0 && err==0) err=1; // Syntaxfehler addtoken(ENDE); if(err!=0) { errpos=lptr-(str.latin1())+1; delfkt(temp); return -1; } errpos=0; return temp->id; //return the unique ID-number for the function } void Parser::reparse(int ix) { reparse( &ufkt[ix] ); } void Parser::reparse(Ufkt *item) { kdDebug() << "Reparsing: " << item->fstr << endl; TQString str = item->fstr.latin1(); err=0; errpos=1; const int p1=str.find('('); int p2=str.find(','); const int p3=str.find(")="); fix_expression(str,p1+4); if(p1==-1 || p3==-1 || p1>p3) { err=4; return; } if ( p3+2 == (int) str.length()) //empty function { err=11; return; } if(p2==-1 || p2>p3) p2=p3; if (str.mid(p1+1, p2-p1-1) == "e") { err=4; return; } item->fname=str.left(p1); item->fvar=str.mid(p1+1, p2-p1-1); if(p2fpar=str.mid(p2+1, p3-p2-1); else item->fpar=""; if ( item->fname != item->fname.lower() ) //isn't allowed to contain capital letters { err=12; return; } //ixa=ix; current_item = item; mem=mptr=item->mem; lptr=(str.latin1())+p3+2; heir1(); if(*lptr!=0 && err==0) err=1; // Syntaxfehler addtoken(ENDE); errpos=0; } void Parser::fix_expression(TQString &str, int const pos) { str.remove(" " ); //insert '*' when it is needed TQChar ch; bool function = false; for(uint i=pos; i < str.length();i++) { ch = str.at(i); if ( str.at(i+1)=='(' && ch.category()==TQChar::Letter_Lowercase ) { TQString str_function(ch); int n=i-1; while (n>0 && str.at(n).category() == TQChar::Letter_Lowercase ) { str_function.prepend(str.at(n)); --n; } if (str_function == "tanh" || str_function == "tan" || str_function =="sqrt" || str_function =="sqr" || str_function =="sin" || str_function =="sinh" || str_function =="sign" || str_function =="sech" || str_function =="sec" || str_function =="log" || str_function =="ln" || str_function =="exp" || str_function =="coth" || str_function =="cot" || str_function =="cosh" || str_function =="cosech" || str_function =="cosec" || str_function =="cos" || str_function =="artanh" || str_function =="arsinh" || str_function =="arsech" || str_function =="arctan" || str_function =="arcsin" || str_function =="arcsec" || str_function =="arcoth" || str_function =="arcosh" || str_function =="arcosech" || str_function =="arccot" || str_function =="arccosec" || str_function =="arccos" || str_function =="abs" || str_function=="arctanh" || str_function=="arcsinh" || str_function=="arccosh") function = true; else for( TQValueVector::iterator it = ufkt.begin(); it != ufkt.end(); ++it) { for ( int j=i; j>0 && (str.at(j).isLetter() || str.at(j).isNumber() ) ; --j) { if ( it->fname == str.mid(j,i-j+1) ) function = true; } } } else if (function) function = false; if( (ch.isNumber() || ch.category()==TQChar::Letter_Uppercase )&& ( str.at(i-1).isLetter() || str.at(i-1) == ')' ) || (ch.isLetter() && str.at(i-1)==')') ) str.insert(i,'*'); else if( (ch.isNumber() || ch == ')' || ch.category()==TQChar::Letter_Uppercase) && ( str.at(i+1).isLetter() || str.at(i+1) == '(' ) || (ch.isLetter() && str.at(i+1)=='(' && !function ) ) { str.insert(i+1,'*'); i++; } } TQString str_end = str.mid(pos); str_end = str_end.replace(m_decimalsymbol, "."); //replace the locale decimal symbol with a '.' str.truncate(pos); str.append(str_end); //kdDebug() << "str:" << str << endl; } bool Parser::delfkt( Ufkt *item) { kdDebug() << "Deleting id:" << item->id << endl; if (!item->dep.isEmpty()) { KMessageBox::error(0,i18n("This function is depending on an other function")); return false; } for(TQValueVector::iterator it1=ufkt.begin(); it1!=ufkt.end(); ++it1) { if (it1==item) continue; for(TQValueList::iterator it2=it1->dep.begin(); it2!=it1->dep.end(); ++it2) if ( (uint)*it2 == item->id ) it2 = it1->dep.erase(it2); } if ( ufkt.count()==1 ) { //kdDebug() << "first item, don't delete" << endl; item->fname=""; } else { //kdDebug() << "Deleting something" << endl; TQChar const extstr_c = item->fstr.at(0); uint const id = item->id; delete []item->mem; ufkt.erase(item); if ( extstr_c == 'x') { int const ix = ixValue(id+1); if (ix!= -1 && ufkt[ix].fstr.at(0) == 'y') delfkt( &ufkt[ix]); } else if ( extstr_c == 'y') { int const ix = ixValue(id-1); if (ix!= -1 && ufkt[ix].fstr.at(0) == 'x') delfkt( &ufkt[ix]); } } return true; } bool Parser::delfkt(uint id) { int ix = ixValue(id); if ( ix!=-1 && delfkt(&ufkt[ix])) return true; else return false; } uint Parser::countFunctions() { uint const count = ufkt.count(); if (count == 1 && ufkt.begin()->fname.isEmpty()) return 0; else return count; } void Parser::heir1() { char c; heir2(); if(err!=0) return ; while(1) { switch(c=*lptr) { default: return ; case ' ': ++lptr; continue; case '+': case '-': ++lptr; addtoken(PUSH); heir2(); if(err!=0) return; } switch(c) { case '+': addtoken(PLUS); break; case '-': addtoken(MINUS); } } } void Parser::heir2() { if(match("-")) { heir2(); if(err!=0) return; addtoken(NEG); } else heir3(); } void Parser::heir3() { char c; heir4(); if(err!=0) return; while(1) { switch(c=*lptr) { default: return; case ' ': ++lptr; continue; case '*': case '/': ++lptr; addtoken(PUSH); heir4(); if(err!=0) return ; } switch(c) { case '*': addtoken(MULT); break; case '/': addtoken(DIV); } } } void Parser::heir4() { primary(); if(err!=0) return; while(match("^")) { addtoken(PUSH); primary(); if(err!=0) return; addtoken(POW); } } void Parser::primary() { if(match("(")) { heir1(); if(match(")")==0) err=2; // fehlende Klammer return; } int i; for(i=0; i::iterator it = ufkt.begin(); it != ufkt.end(); ++it) { if(TQString(lptr)=="pi" || TQString(lptr)=="e") continue; if( match(it->fname.latin1()) ) { if (it == current_item) { err=9; return; } primary(); addtoken(UFKT); addfptr( it->id ); it->dep.append(current_item->id); return; } } // A constant if(lptr[0] >='A' && lptr[0]<='Z' ) { char tmp[2]; tmp[1] = '\0'; for( i = 0; i< (int)constant.size();i++) { tmp[0] = constant[i].constant; if ( match( tmp) ) { addtoken(KONST); addwert(constant[i].value); return; } } err = 10; return; } if(match("pi")) { addtoken(KONST); addwert(M_PI); return; } if(match("e")) { addtoken(KONST); addwert(M_E); return; } //if(match(ufkt[ixa].fvar.latin1())) if(match(current_item->fvar.latin1())) { addtoken(XWERT); return; } if(match("y")) { addtoken(YWERT); return; } //if(match(ufkt[ixa].fpar.latin1())) if(match(current_item->fpar.latin1())) { addtoken(KWERT); return; } char *p; double const w=strtod(lptr, &p); if(lptr!=p) { lptr=p; addtoken(KONST); addwert(w); } else err=1; // Syntax-Fehler } int Parser::match(const char *lit) { const char *p; if(*lit==0) return 0; while(*lptr==' ') ++lptr; p=lptr; while(*lit) { if(*lit++!=*p++) return 0; } lptr=p; return 1; } void Parser::addtoken(unsigned char token) { if(stkptr>=stack+STACKSIZE-1) { err=7; return; } if(evalflg==0) { if(mptr>=&mem[MEMSIZE-10]) err=6; else *mptr++=token; switch(token) { case PUSH: ++stkptr; break; case PLUS: case MINUS: case MULT: case DIV: case POW: --stkptr; } } else switch(token) { case PUSH: ++stkptr; break; case PLUS: stkptr[-1]+=*stkptr; --stkptr; break; case MINUS: stkptr[-1]-=*stkptr; --stkptr; break; case MULT: stkptr[-1]*=*stkptr; --stkptr; break; case DIV: if(*stkptr==0.) *(--stkptr)=HUGE_VAL; else { stkptr[-1]/=*stkptr; --stkptr; } break; case POW: stkptr[-1]=pow(*(stkptr-1), *stkptr); --stkptr; break; case NEG: *stkptr=-*stkptr; } } void Parser::addwert(double x) { double *pd=(double*)mptr; if(evalflg==0) { if(mptr>=&mem[MEMSIZE-10]) err=6; else { *pd++=x; mptr=(unsigned char*)pd; } } else *stkptr=x; } void Parser::addfptr(double(*fadr)(double)) { double (**pf)(double)=(double(**)(double))mptr; if( evalflg==0 ) { if( mptr>=&mem[MEMSIZE-10] ) err=6; else { *pf++=fadr; mptr=(unsigned char*)pf; } } else *stkptr=(*fadr)(*stkptr); } void Parser::addfptr(uint id) { uint *p=(uint*)mptr; if(evalflg==0) { if(mptr>=&mem[MEMSIZE-10]) err=6; else { *p++=id; mptr=(unsigned char*)p; } } else { for( TQValueVector::iterator it = ufkt.begin(); it != ufkt.end(); ++it) if ( it->id == id) { *stkptr=fkt(it, *stkptr); break; } } } int Parser::fnameToId(const TQString &name) { for( TQValueVector::iterator it = ufkt.begin(); it != ufkt.end(); ++it) { if(name==it->fname) return it->id; } return -1; // Name nicht bekannt } int Parser::parserError(bool showMessageBox) { if (!showMessageBox) return err; switch(err) { case 1: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Syntax error").arg(TQString::number(errpos)), "KmPlot"); break; case 2: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Missing parenthesis").arg(TQString::number(errpos)), "KmPlot"); break; case 3: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Function name unknown").arg(TQString::number(errpos)), "KmPlot"); break; case 4: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Void function variable").arg(TQString::number(errpos)), "KmPlot"); break; case 5: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Too many functions").arg(TQString::number(errpos)), "KmPlot"); break; case 6: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Token-memory overflow").arg(TQString::number(errpos)), "KmPlot"); break; case 7: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Stack overflow").arg(TQString::number(errpos)), "KmPlot"); break; case 8: KMessageBox::error(0, i18n("Parser error at position %1:\n" "Name of function not free.").arg(TQString::number(errpos)), "KmPlot"); break; case 9: KMessageBox::error(0, i18n("Parser error at position %1:\n" "recursive function not allowed.").arg(TQString::number(errpos)), "KmPlot"); break; case 10: KMessageBox::error(0, i18n("Could not find a defined constant at position %1." ).arg(TQString::number(errpos)), "KmPlot"); break; case 11: KMessageBox::error(0, i18n("Empty function"), "KmPlot"); break; case 12: KMessageBox::error(0, i18n("The function name is not allowed to contain capital letters."), "KmPlot"); break; case 13: KMessageBox::error(0, i18n("Function could not be found."), "KmPlot"); break; case 14: KMessageBox::error(0, i18n("The expression must not contain user-defined constants."), "KmPlot"); break; } return err; } // static TQString Parser::number( double value ) { TQString str = TQString::number( value, 'g', 6 ); str.replace( 'e', "*10^" ); // kDebug() << "returning str="<0.) return 1.; return 0.; } double sqr(double x) { return x*x; } double arsinh(double x) { return log(x+sqrt(x*x+1)); } double arcosh(double x) { return log(x+sqrt(x*x-1)); } double artanh(double x) { return log((1+x)/(1-x))/2; } // sec, cosec, cot and their inverses double sec(double x) { return (1 / cos(x*Parser::anglemode())); } double cosec(double x) { return (1 / sin(x*Parser::anglemode())); } double cot(double x) { return (1 / tan(x*Parser::anglemode())); } double arcsec(double x) { if ( !Parser::anglemode() ) return ( 1/acos(x)* 180/M_PI ); else return acos(1/x); } double arccosec(double x) { return asin(1/x)* 1/Parser::anglemode(); } double arccot(double x) { return atan(1/x)* 1/Parser::anglemode(); } // sech, cosech, coth and their inverses double sech(double x) { return (1 / cosh(x*Parser::anglemode())); } double cosech(double x) { return (1 / sinh(x*Parser::anglemode())); } double coth(double x) { return (1 / tanh(x*Parser::anglemode())); } double arsech(double x) { return arcosh(1/x)* 1/Parser::anglemode(); } double arcosech(double x) { return arsinh(1/x)* 1/Parser::anglemode(); } double arcoth(double x) { return artanh(1/x)* 1/Parser::anglemode(); } //basic trigonometry functions double lcos(double x) { return cos(x*Parser::anglemode()); } double lsin(double x) { return sin(x*Parser::anglemode()); } double ltan(double x) { return tan(x*Parser::anglemode()); } double lcosh(double x) { return cosh(x*Parser::anglemode()); } double lsinh(double x) { return sinh(x*Parser::anglemode()); } double ltanh(double x) { return tanh(x*Parser::anglemode()); } double arccos(double x) { return acos(x) * 1/Parser::anglemode(); } double arcsin(double x) { return asin(x)* 1/Parser::anglemode(); } double arctan(double x) { return atan(x)* 1/Parser::anglemode(); }