Logo Search packages:      
Sourcecode: kdeaddons version File versions

parser.cpp

/*
* Code based on parser from KmPlot - a math. function plotter for the KDE-Desktop
*
* Original code
* Copyright (C) 1998, 1999  Klaus-Dieter Möller
*               2000, 2002 kd.moeller@t-online.de
*
* Modifications: 2004  Andrew Coles (andrew_coles@yahoo.co.uk)
*               
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/

// standard c(++) includes
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

//KDE includes
#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>

// 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", log10},   // Logarithm base 10
      {"ln", log},            // 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
      {"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
};
                                   

Parser::Parser()
{   ps_init( UFANZ, MEMSIZE, STACKSIZE );
}


Parser::Parser( int anz, int m_size, int s_size )
{   ps_init( anz, m_size, s_size );
}


void Parser::ps_init(int anz, int m_size, int s_size)
{     int ix;

      ufanz=anz;
      memsize=m_size;
      stacksize=s_size;
            ufkt=new Ufkt[ufanz];
      evalflg=ixa=0;
      for(ix=0; ix<ufanz; ++ix)
      {     ufkt[ix].memsize=memsize;
            ufkt[ix].stacksize=stacksize;
            ufkt[ix].fname="";      //.resize(1);
            ufkt[ix].fvar="";       //.resize(1);
            ufkt[ix].fpar="";       //.resize(1);
            ufkt[ix].fstr="";       //.resize(1);
            ufkt[ix].mem=new unsigned char [memsize];
      }
}


Parser::~Parser()
{   delete [] ufkt;
}


Parser::Ufkt::Ufkt()
{
}


Parser::Ufkt::~Ufkt()
{   delete [] mem;
}


00128 void Parser::setAngleMode(int angle)
{     if(angle==0)
            m_anglemode = 1;
      else
            m_anglemode = M_PI/180; 
}

00135 double Parser::anglemode()
{     return m_anglemode;
}

00139 double Parser::eval(QString str)
{     double erg;

      stack=new double [stacksize];
      stkptr=stack;
      evalflg=1;
      lptr=str.latin1();
      err=0;
      heir1();
      if(*lptr!=0 && err==0) err=1;
      evalflg=0;
      erg=*stkptr;
      delete [] stack;
      if(err==0)
      {     errpos=0;
            return erg;
      }
      else
      {     errpos=lptr-(str.latin1())+1;
            return 0.;
      }
}


00163 double Parser::Ufkt::fkt(double x)
{     unsigned char token;
      double *pd, (**pf)(double);
      double erg, *stack, *stkptr;
      Ufkt **puf;
      
      mptr=mem;
      stack=stkptr= new double [stacksize];
      while(1)
      {   switch(token=*mptr++)
            {  case     KONST:  pd=(double*)mptr;
                              *stkptr=*pd++;
                              mptr=(unsigned char*)pd;
                              break;
           case   XWERT:  *stkptr=x;
                              break;
           case   YWERT:  *stkptr=oldy;
                              break;
           case KWERT:  *stkptr=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))mptr;
                              *stkptr=(*pf++)(*stkptr);
                              mptr=(unsigned char*)pf;
                              break;

           case UFKT:   puf=(Ufkt**)mptr;
                              *stkptr=(*puf++)->fkt(*stkptr);
                              mptr=(unsigned char*)puf;
                              break;

           case ENDE:   erg=*stkptr;
                        delete [] stack;
                        return erg;
            }
      }
}

00230 int Parser::getNextIndex()
{
      int ix = 0;
      while( ( ix < ufanz ) && !ufkt[ ix ].fname.isEmpty() ) ix++;
      if( ix == ufanz ) ix = -1;
      return ix;
}

00238 int Parser::addfkt(QString str)
{   
      int ix;
      
      stkptr=stack=0;
      err=0;
      errpos=1;
      str.remove(" " );
      const int p1=str.find('(');
      int p2=str.find(',');
      const int p3=str.find(")=");
      
      //insert '*' when it is needed
      for(int i=p1+3; i < (int) str.length();i++)
      {
            if( (str.at(i).isNumber() || str.at(i).category()==QChar::Letter_Uppercase )&& ( str.at(i-1).isLetter() || str.at(i-1) == ')' ) )
            {
                  str.insert(i,'*');
            }
            else if( (str.at(i).isNumber() || str.at(i) == ')' || str.at(i).category()==QChar::Letter_Uppercase) && ( str.at(i+1).isLetter() || str.at(i+1) == '(' ) )
            {
                  str.insert(i+1,'*');
                  i++;
            }
      }
            
      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(getfix(str.left(p1))!=-1)
      {   err=8;
            return -1;
      }
      else err=0;
      
      if (str.mid(p1+1, p2-p1-1) == "e")
      {   err=4;
            return -1;
      }
      
      for(ix=0; ix<ufanz; ++ix)
      {   if(ufkt[ix].fname.isEmpty())
            {   ufkt[ix].fname=str.left(p1);
                  ufkt[ix].fvar=str.mid(p1+1, p2-p1-1);
                  ufkt[ix].fstr=str;
                  if(p2<p3) ufkt[ix].fpar=str.mid(p2+1, p3-p2-1);
                  else ufkt[ix].fpar="";      //.resize(1);
                  break;
            }
      }
    
      if(ix==ufanz)
      {   err=5;
            return -1;
      }     // zu viele Funktionen

      ixa=ix;
      mem=mptr=ufkt[ix].mem;
      lptr=(str.latin1())+p3+2;
      heir1();
      if(*lptr!=0 && err==0) err=1;       // Syntaxfehler
      addtoken(ENDE);

      if(err!=0)
      {   ufkt[ix].fname="";  //.resize(1);
            errpos=lptr-(str.latin1())+1;
            return -1;
      }

      
      errpos=0;
      return ix;
}


00319 int Parser::delfkt(QString name)
{   int ix;

      ix=getfix(name);
      if(ix!=-1) ufkt[ix].fname="";   //.resize(1);   // Name l�chen
      return ix;
}


00328 int Parser::delfkt(int ix)
{   if(ix<0 || ix>=ufanz) return -1;      // ungltiger Index

      ufkt[ix].fname="";      //.resize(1);                 // Name l�chen
      return ix;
}


00336 double Parser::fkt(QString name, double x)
{   int ix;

      ix=getfix(name);
      if(ix==-1) return 0.;

      return ufkt[ix].fkt(x);
}


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()
{   char *p;
      int i;
      double w;

      if(match("("))
      {   heir1();
            if(match(")")==0) err=2;      // fehlende Klammer
            return;
      }

      for(i=0; i<FANZ; ++i)
      {   if(match(mfkttab[i].mfstr))
            {   primary();
                  addtoken(FKT);
                  addfptr(mfkttab[i].mfadr);
                  return;
            }
      }

      for(i=0; i<ufanz; ++i)
      {   if(ufkt[i].fname[0]==0) continue;
            if(match(ufkt[i].fname.latin1()))
            {   if(i==ixa) {err=9; return;}
            
                  primary();
                  addtoken(UFKT);
                  addfptr(&ufkt[i]);
                  return;
            }
      }
      // A constant
      if(lptr[0] >='A' && lptr[0]<='Z' )
      {   char tmp[2];
            tmp[1] = '\0';
            for( int 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()))
      {   addtoken(XWERT);
            return;
      }
      
      if(match("y"))
      {   addtoken(YWERT);
            return;
      }
      
      if(match(ufkt[ixa].fpar.latin1()))
      {   addtoken(KWERT);
            return;
      }

      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(Ufkt *adr)
{   Ufkt **p=(Ufkt**)mptr;

      if(evalflg==0)
      {   if(mptr>=&mem[memsize-10]) err=6;
            else
            {   *p++=adr;
                  mptr=(unsigned char*)p;
            }
      }
      else *stkptr=adr->fkt(*stkptr);
}


00628 int Parser::chkfix(int ix)
{   if(ix<0 || ix>=ufanz) return -1;            // ungltiger Index
      if(ufkt[ix].fname.isEmpty()) return -1;   // keine Funktion
      return ix;
}


00635 int Parser::getfkt(int ix, QString& name, QString& str)
{   if(ix<0 || ix>=ufanz) return -1;            // ungltiger Index
      if(ufkt[ix].fname.isEmpty()) return -1;   // keine Funktion
      name=ufkt[ix].fname.copy();
      str=ufkt[ix].fstr.copy();
      return ix;
}


00644 int Parser::getfix(QString name)
{   int ix;

      err=0;
      for(ix=0; ix<ufanz; ++ix)
      {   if(name==ufkt[ix].fname) return ix;
      }
      err=3;                              // Name nicht bekannt
      return -1;
}


00656 int Parser::errmsg()
{   switch(err)
      {  case 1:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Syntax error").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;

       case 2:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Missing parenthesis").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;

       case 3:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Function name unknown").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;

       case 4:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Void function variable").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;

       case 5:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Too many functions").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;

       case 6:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Token-memory overflow").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;

       case 7:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Stack overflow").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;

       case 8:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "Name of function not free").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;
         
       case 9:  KMessageBox::error(0, i18n("Parser error at position %1:\n"
                                               "recursive function not allowed").arg(QString::number(errpos)), i18n("Math Expression Evaluator"));
                    break;
       case 10:  KMessageBox::error(0, i18n("Could not find a defined constant at position %1" ).arg(QString::number(errpos)),
                                                   i18n("Math Expression Evaluator"));
                  break;
       case 11:  KMessageBox::error(0, i18n("Empty function"), i18n("Math Expression Evaluator"));
                    break;
      }
    
      return err;
}


00704 double sign(double x)
{   if(x<0.) return -1.;
    else if(x>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();
}

Generated by  Doxygen 1.6.0   Back to index