You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeaddons/kicker-applets/math/parser.cpp

814 lines
17 KiB

/*
* 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;
}
void Parser::setAngleMode(int angle)
{ if(angle==0)
m_anglemode = 1;
else
m_anglemode = M_PI/180;
}
double Parser::anglemode()
{ return m_anglemode;
}
double Parser::eval(TQString 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.;
}
}
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;
}
}
}
int Parser::getNextIndex()
{
int ix = 0;
while( ( ix < ufanz ) && !ufkt[ ix ].fname.isEmpty() ) ix++;
if( ix == ufanz ) ix = -1;
return ix;
}
int Parser::addfkt(TQString 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()==TQChar::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()==TQChar::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;
}
int Parser::delfkt(TQString name)
{ int ix;
ix=getfix(name);
if(ix!=-1) ufkt[ix].fname=""; //.resize(1); // Name l�chen
return ix;
}
int Parser::delfkt(int ix)
{ if(ix<0 || ix>=ufanz) return -1; // ungltiger Index
ufkt[ix].fname=""; //.resize(1); // Name l�chen
return ix;
}
double Parser::fkt(TQString 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);
}
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;
}
int Parser::getfkt(int ix, TQString& name, TQString& 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;
}
int Parser::getfix(TQString 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;
}
int Parser::errmsg()
{ switch(err)
{ case 1: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"Syntax error").arg(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 2: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"Missing parenthesis").arg(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 3: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"Function name unknown").arg(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 4: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"Void function variable").arg(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 5: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"Too many functions").arg(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 6: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"Token-memory overflow").arg(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 7: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"Stack overflow").arg(TQString::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(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 9: KMessageBox::error(0, i18n("Parser error at position %1:\n"
"recursive function not allowed").arg(TQString::number(errpos)), i18n("Math Expression Evaluator"));
break;
case 10: KMessageBox::error(0, i18n("Could not find a defined constant at position %1" ).arg(TQString::number(errpos)),
i18n("Math Expression Evaluator"));
break;
case 11: KMessageBox::error(0, i18n("Empty function"), i18n("Math Expression Evaluator"));
break;
}
return err;
}
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();
}