1
///////////////////////////////////////////////////////////////////////////
2
// mathex 0.2.3 (beta) - Copyright (C) 2000-2003, by Sadao Massago //
3
// file: mathex.h (math expression evaluator header file) //
5
// project web page: http://sscilib.sourceforge.net/ //
6
// ----------------------------------------------------------------------//
7
// The mathex library and related files is licenced under the term of //
8
// GNU LGPL (Lesser General Public License) version 2.1 or latter //
9
// with exceptions that allow for static linking. //
10
// See license.txt for detail. //
11
// For GNU LGPL, see lesser.txt. //
12
// For information over GNU or GNU compatible license, visit the site //
13
// http://www.gnu.org. //
14
///////////////////////////////////////////////////////////////////////////
16
////////////////////////////////////////////////////////////////////////////
18
//-------------------------------------------------------------------------
19
// title: Algoritms and Data Structures
20
// author: Niklaus Wirth
21
// publisher: Prentice-Hall
23
//-------------------------------------------------------------------------
24
// title: The C++ Programming Language
26
// author: Bjarne Stroustrup
27
// publisher: Addilson-Wesley
29
//-------------------------------------------------------------------------
30
// title: building your own C interpreter
31
// author: Schildt, Helbert
32
// journal: Dr. Dobb's Jornal (http://www.ddj.com)
33
// number: August/1989
34
// pages: 38-49, 110-122
35
///////////////////////////////////////////////////////////////////////////
56
////////////////////////////////////
57
// local variables, functions, etc
58
////////////////////////////////////
61
//////////////////////////////////
62
// C function with one parameter
63
//--------------------------------
66
// Function return the factorial of integer belong to range 0..170.
67
// in future, will implement aproximation for gamma function to evaluate for x > 170
72
if((x <0) || (x>170)) // maximum admited where
73
throw mathex::error("Error [fac()]: range error");
75
n = static_cast<unsigned>(x+0.5);
86
// convert radian to degree
89
return( x * 180.0 / M_PI);
92
// convert degree to radian
95
return(x * M_PI / 180.0);
99
/* double sqr(double x)
106
double trunc(double x)
108
return static_cast<long>(x);
111
// return rounded value to int
112
double round(double x)
114
return static_cast<long>(x+0.5);
117
// return 0 if x negative, 1 otherwise
118
double step(double x)
120
if ( x < 0 ) return static_cast<long>(0.);
121
return static_cast<long>(1.);
124
// return -1 if x negative, 1 otherwise
125
double sign(double x)
127
if ( x < 0 ) return static_cast<long>(-1.);
128
return static_cast<long>(1.);
131
//////////////////////////////////////////////////////////
132
// arithmetic operators
133
//////////////////////////////////////////////////////////
134
// operators was treated in Parsingr time, to check the
136
// unary operator is the function double op(double) and
137
// binary operator is function double op(double,double)
138
//--------------------------------------------------------
141
// "-" -> unary_minus
143
// "+" -> binary_plus
144
// "-" -> binary_minus
145
// "*" -> binary_times
146
// "/" -> binary_divide
147
// "^" -> binary_power
148
// "%" -> fmod (defined in math.h)
149
///////////////////////////////////////////////////////////
151
//////////////////////
154
double unary_minus(double x)
159
//////////////////////////////////////////
160
// C binary operators
161
//----------------------------------------
163
double binary_plus(double x, double y)
168
double binary_minus(double x, double y)
173
double binary_times(double x, double y)
178
double binary_divide(double x, double y)
179
// in future, put more precisery checking, for overflow ?
182
throw mathex::error("Error [binary_divide()]: divisin by zero");
187
double binary_power(double x, double y)
192
/////////////////////////////////////////
193
// pre-defined user defined functions
194
//---------------------------------------
196
double p_rand(vector <double> const &x)
197
// rand() return value between [0,1)
200
throw mathex::error("Error [p_rand()]: can not use argument");
201
return rand()/(RAND_MAX+1.0);
205
double p_max(vector <double> const & x)
211
throw mathex::error("Error [p_max()]: No arguments");
213
for(unsigned i=0; i<x.size(); i++)
221
double p_min(vector <double> const & x)
227
throw mathex::error("Error [p_min()]: No arguments");
229
for(unsigned i=0; i<x.size(); i++)
237
double p_sum(vector <double> const & x)
243
throw mathex::error("Error [p_sum()]: No arguments");
244
for(unsigned i=0; i<x.size(); i++)
251
double p_med(vector <double> const & x)
254
throw mathex::error("Error [p_med()]: No arguments");
255
return p_sum(x)/x.size();
258
////////////////////////////////
259
// function/constant tables
260
////////////////////////////////
262
//////////////////////////////////
263
// One parameter C function table
265
// Record for one parameter C functions
270
double (*f)(double x); // one parameter function
273
///////////////////////////////////////////////////////////
274
// One parameter internal C defined functions
275
// the unary operator are stored on first part of table
276
//---------------------------------------------------------
278
// if add unary operator, adjust the definiftion of NUM_UNARY_OP
279
#define NUM_UNARY_OP 1
280
CFUNCREC cfunctable[] =
282
// name and funtion pointer
283
// first part of table is unary operators
284
{"-", unary_minus}, // unary operator
285
// seccond part of table is functions
300
{ "deg", deg }, // added
304
{ "fac", fac }, // added
310
// { "pow10", pow10 } // in future, add it?
311
{ "rad", rad }, // added
313
{ "round", round }, // added
321
// { "sqr", sqr }, // added
330
{ "trunc", trunc }, // added
332
{ "floor", floor }, // largest integer not grather than x
334
{ "ceil", ceil }, // smallest integer not less than x
337
{ 0, 0 } // 0 mark the end of table
340
/////////////////////
343
// binary operator's record
347
double (*f)(double, double);
352
BINOPREC binoptable[] =
356
{ '-', binary_minus},
357
{ '*', binary_times},
358
{ '/', binary_divide},
359
{ '^', binary_power},
362
{ '\0' , 0 } // '\0' mark the end of table
377
CONSTREC consttable[] =
383
{ NULL, 0 } // NULL mark the end of table
389
////////////////////////////
391
///////////////////////////
395
/// undefined number of arguments (for user defined functions
396
const int mathex::UNDEFARGS = -1; // for user function arguments
400
void mathex::addstdfunc()
402
addfunc("rand", p_rand, 0);
403
addfunc("Rand", p_rand, 0);
404
addfunc("sum", p_sum, UNDEFARGS);
405
addfunc("Sum", p_sum, UNDEFARGS);
406
addfunc("max", p_max, UNDEFARGS);
407
addfunc("Max", p_max, UNDEFARGS);
408
addfunc("min", p_min, UNDEFARGS);
409
addfunc("Min", p_min, UNDEFARGS);
410
addfunc("med", p_med, UNDEFARGS);
411
addfunc("Med", p_med, UNDEFARGS);
425
/////////////////////////////////
426
// varibles table manipulations
428
bool mathex::addvar(string const &name, double *var)
429
// register the program internal variable
433
for(i=0; (i<vartable.size()) && (vartable[i].name != name);i++);
434
if(i<vartable.size()) { // found! overwrite
435
vartable[i].var = var;
438
else if(!isnewvalidname(name))
440
vartable.push_back(VARREC(name, var));
444
bool mathex::delvar(string const &name)
445
// delete one variable
449
for(i=0; (i<vartable.size()) && (vartable[i].name != name);i++);
450
if(i < vartable.size()) {
452
// vartable.erase(&vartable[i],&vartable[i+1]);
453
for(unsigned j=0; j<vartable.size()-1; j++)
454
vartable[j] = vartable[j+1];
455
vartable.pop_back(); // delete last
463
//////////////////////////////////////////////
464
// user defined function table manipulation
465
//////////////////////////////////////////////
467
bool mathex::addfunc(string const &name, double (*f)(vector<double> const &), int NumArgs)
468
// register the user defined function
472
for(i=0; (i<functable.size()) && (functable[i].name != name);i++);
473
if(i<functable.size()) { // found! overwrite
475
functable[i].numargs = NumArgs;
478
else if(!isnewvalidname(name))
480
functable.push_back(FUNCREC(name, f, NumArgs));
484
bool mathex::delfunc(string const &name)
485
// delete the user defined function
489
for(i=0; (i<functable.size()) && (functable[i].name != name);i++);
490
if(i < functable.size()) {
492
// functable.erase(&vartable[i],&vartable[i+1]);
493
for(unsigned j=0; j<vartable.size()-1; j++)
494
functable[j] = functable[j+1];
495
functable.pop_back(); // delete last
502
////////////////////////////////////////////////////
503
// get the index of variables/constants/functions/
504
// binary operator/user defined functions
505
//--------------------------------------------------
506
// return -1 if not found
507
////////////////////////////////////////////////////
509
int mathex::getconst(string const &name)
510
// get index of const
511
// return -1 if not found
514
// look up the constants
515
for(i=0;consttable[i].name && strcmp(name.c_str(), consttable[i].name);i++);
516
if(consttable[i].name != NULL) // if found
523
int mathex::getvar(string const &name)
524
// get index of variable
525
// return -1 if not found
530
for(i=0;(i<vartable.size()) && strcmp(name.c_str(), vartable[i].name.c_str());i++);
531
if(i<vartable.size()) // if found
538
int mathex::getcfunc(string const &name)
539
// get index of one parameter function
540
// return -1 if not found
543
// look up the constants
544
for(i=NUM_UNARY_OP;cfunctable[i].name && strcmp(name.c_str(), cfunctable[i].name);i++);
545
if(cfunctable[i].name != NULL) // if found
551
int mathex::getunaryop(string const &name)
552
// get index of unary operator
553
// return -1 if not found
556
// look up the constants
557
for(i=0; cfunctable[i].name && strcmp(name.c_str(), cfunctable[i].name);i++);
558
if(cfunctable[i].name != NULL) // if found
564
int mathex::getbinop(char name)
565
// get index of one parameter function
566
// return -1 if not found
569
// look up the constants
570
for(i=0;binoptable[i].name && (name != binoptable[i].name);i++);
571
if(binoptable[i].name != '\0') // if found
578
int mathex::getuserfunc(string const &name)
579
// get index of variable
580
// return -1 if not found
583
// look up the constants
584
for(i=0;(i<functable.size()) && strcmp(name.c_str(), functable[i].name.c_str());i++);
585
if(i<functable.size()) // if found
592
bool mathex::isnewvalidname(string const &name)
595
if(name.empty() || (!isalpha(name[0]) && (name[0] != '_')))
597
// check for rest of characters
598
for(unsigned j=0; j<name.size(); j++)
599
if(!isalnum(name[j]) && (name[j] != '-'))
601
return (getcfunc(name) < 0) && (getconst(name) < 0)
602
&& (getuserfunc(name) < 0) && (getvar(name) < 0);
609
// main evaluator: internal use only
611
// main evaluation function
612
double mathex::eval()
613
// Eval the parsed stack and return
615
static vector <double> x; // suppose that eval does not eval
618
if(status == notparsed) parse();
619
if(status == invalid) throw error("eval()", "invalid expression");
621
for(unsigned i=0; i<bytecode.size(); i++)
623
switch(bytecode[i].state) {
624
case CODETOKEN::VALUE: evalstack.push_back(bytecode[i].value);
626
case CODETOKEN::VARIABLE:
627
// get value of variable as value
628
evalstack.push_back(*vartable[bytecode[i].idx].var);
630
case CODETOKEN::FUNCTION: // Call the C internal functions with one parameter
632
if(evalstack.size()<1) // error: It does not to occur if currect parsed.
633
throw error("eval()", "stack error");
635
evalstack.back() = cfunctable[bytecode[i].idx].f(evalstack.back());
637
case CODETOKEN::BINOP: // Call the intern C function with two parameters
639
if(evalstack.size() < 2) // error: It does not to occur if currect parsed.
640
throw error("eval()", "stack error");
642
evalstack[evalstack.size()-2] = binoptable[bytecode[i].idx].f
643
(evalstack[evalstack.size()-2], evalstack.back());
644
evalstack.pop_back(); // delete last
646
case CODETOKEN::USERFUNC: // Call the user defined functions
648
if(bytecode[i].numargs > evalstack.size())
649
throw error("eval()", "stack error");
651
if(bytecode[i].numargs > 0) {
652
x.resize(bytecode[i].numargs);
653
for(unsigned j=0; j<static_cast<unsigned>(bytecode[i].numargs); j++)
654
x[bytecode[i].numargs-1-j] = evalstack[evalstack.size()-1-j];
655
evalstack.resize(evalstack.size()-bytecode[i].numargs+1);
657
evalstack.back() = functable[bytecode[i].idx].f(x);
659
else // Fixing bug pointed by Hugh Denman <denmanh@tcd.ie> November 06, 2003
660
evalstack.push_back(functable[bytecode[i].idx].f(x));
662
default: // invarid stack. It does not occur if currect parsed
663
throw error("eval()", "invalid code token");
665
} // for(i=0; ByteCode[i].state != EMPTY;i++);
668
if(evalstack.size() != 1)
669
throw error("eval()", "stack error");
681
// get the number token
682
bool mathex::getnumber(double &x)
684
unsigned long i = pos;
688
if((i >= expr.size()) || !strchr("0123456789.", expr[i])) // not a number
691
// getting the number
692
for(decimal=false; i<expr.size(); i++) {
693
if(!isdigit(expr[i]) && ((expr[i] != '.') || decimal) )
695
if(expr[i] == '.') decimal = true;
698
if((i==(pos+1)) && (expr[i]=='.')) // is not a number
701
// if scientific notation
702
if((toupper(expr[i])=='E') && (i<expr.size())) { // cientific notation
703
// decimal = true; // turn on to detect that are double
705
if((i<expr.size()) && ((expr[i]=='+') || (expr[i]=='-')) ) { // if sign
709
while((i<expr.size()) && isdigit(expr[i]))
712
// now, copy the token and conter to number
713
// if decimal is true, the number is double. otherwise, number is integer
714
// for this detection, cientific notation need to enable decimal too.
715
// The integer value are not used for this package
717
x = strtod(expr.substr(pos, i-pos).c_str(), 0);
722
bool mathex::getidentifier(string &name)
727
if((i>=expr.size()) || (!isalpha(expr[i]) && (expr[i] != '_'))) // not a identifier
731
for(;(i<expr.size()) &&(isalnum(expr[i]) || (expr[i] == '_')); i++);
733
name = expr.substr(pos, i-pos);
734
pos = i; // advance the input
740
mathex::PARSERTOKEN::type mathex::nexttoken()
741
// Gets the next token from the expr
745
while((pos<expr.size()) && isspace(expr[pos]) )
748
if(pos == expr.size()) {
749
curtok.state = PARSERTOKEN::END;
753
if(getnumber(curtok.value)) {
754
curtok.state = PARSERTOKEN::VALUE;
757
else if(getidentifier(identifier)) { // if identifier
758
// checking the identifier type
759
if((curtok.idx = getcfunc(identifier))>=0) // internal C functions
760
curtok.state = PARSERTOKEN::FUNCTION;
761
else if((curtok.idx=getuserfunc(identifier))>=0) { // user defined functions
762
curtok.numargs = functable[curtok.idx].numargs;
763
curtok.state = PARSERTOKEN::USERFUNC;
765
else if((curtok.idx=getvar(identifier))>=0) // variable
766
curtok.state = PARSERTOKEN::VARIABLE;
767
else if((curtok.idx=getconst(identifier))>=0) { // constant are treated as NUMBER
768
curtok.state = PARSERTOKEN::VALUE;
769
curtok.value = consttable[curtok.idx].value;
771
else { // new identifier: not supported
772
curtok.state = PARSERTOKEN::INVALID;
775
else { // will be operators or delimiters
777
case '+' : curtok.state = PARSERTOKEN::PLUS;
779
case '-' : curtok.state = PARSERTOKEN::MINUS;
781
case '*' : curtok.state = PARSERTOKEN::TIMES;
783
case '/' : curtok.state = PARSERTOKEN::DIVIDE;
785
case '%' : curtok.state = PARSERTOKEN::MODULE;
787
case '^' : curtok.state = PARSERTOKEN::POWER;
789
case ',' : curtok.state = PARSERTOKEN::COMMA;
791
case '(' : curtok.state = PARSERTOKEN::OPAREN;
793
case ')' : curtok.state = PARSERTOKEN::CPAREN;
795
default : curtok.state = PARSERTOKEN::INVALID;
797
if(curtok.state != PARSERTOKEN::INVALID) {
798
curtok.idx = getbinop(expr[pos]);
806
////////////////////////////
807
// CodeStack operations
808
////////////////////////////
810
void mathex::printcoderec(CODETOKEN const &token)
811
// print the Code Stack status
813
switch(token.state) {
814
// case CODETOKEN::EMPTY: cout << "EMPTY\n";
816
case CODETOKEN::VALUE:
817
cout << "VALUE: " << token.value << endl;
819
case CODETOKEN::VARIABLE:
820
cout << "VARIABLE: " << vartable[token.idx].name << endl;
822
case CODETOKEN::FUNCTION:
823
if(token.idx <NUM_UNARY_OP)
824
cout << "unary operator: " << cfunctable[token.idx].name << endl;
826
cout << "FUNCTION: " << cfunctable[token.idx].name << endl;
828
case CODETOKEN::BINOP:
829
cout << "binary operator: " << binoptable[token.idx].name << endl;
831
case CODETOKEN::USERFUNC:
832
cout << "USERFUNC: " << functable[token.idx].name << endl;
834
default: printf("INVALID\n");
839
void mathex::printbytecode()
841
cout << "codesize = "<< bytecode.size() << endl;
842
for(unsigned i=0; i<bytecode.size(); i++)
843
printcoderec(bytecode[i]);
848
// Parse the expression
850
// parserstatus = true;
859
if(curtok.state != PARSERTOKEN::END) // if remain token
860
throw error("parse()", "End of expression expected");
864
void mathex::parsearithmetic1(void)
865
// level 1 arithmetic operator: binary plus/minus
869
while((curtok.state == PARSERTOKEN::PLUS) || (curtok.state == PARSERTOKEN::MINUS)) {
870
savedidx = curtok.idx;
872
if((curtok.state == PARSERTOKEN::PLUS) || (curtok.state == PARSERTOKEN::MINUS))
873
throw error("parse()", "Invalid expression");
875
bytecode.push_back(CODETOKEN(CODETOKEN::BINOP, savedidx));
877
} // parsearithmetic1
880
void mathex::parsearithmetic2(void)
881
// level 2 arithmetic operator: multiplication, division, module
885
while( (curtok.state == PARSERTOKEN::TIMES) || (curtok.state == PARSERTOKEN::DIVIDE)
886
|| (curtok.state == PARSERTOKEN::MODULE) ) {
887
savedidx = curtok.idx;
889
if((curtok.state == PARSERTOKEN::PLUS) || (curtok.state == PARSERTOKEN::MINUS))
890
throw error("parse()", "Invalid expression");
892
bytecode.push_back(CODETOKEN(CODETOKEN::BINOP, savedidx));
894
} // parsearithmetic3
896
void mathex::parsearithmetic3(void)
897
// level 3 arithmetic operator: power
901
if(curtok.state == PARSERTOKEN::POWER) {
902
savedidx = curtok.idx;
904
if((curtok.state == PARSERTOKEN::PLUS) || (curtok.state == PARSERTOKEN::MINUS))
905
throw error("parse()", "Invalid expression");
907
bytecode.push_back(CODETOKEN(CODETOKEN::BINOP, savedidx));
909
} // parsearithmetic3()
911
void mathex::parsearithmetic4(void)
912
// level 4 arithmetic operator: unary plus/minus
914
PARSERTOKEN::type state;
915
if(((state=curtok.state) == PARSERTOKEN::PLUS) || (state == PARSERTOKEN::MINUS))
917
if((curtok.state == PARSERTOKEN::PLUS) || (curtok.state == PARSERTOKEN::MINUS))
918
throw error("parse()", "Invalid expression");
921
if(state == PARSERTOKEN::MINUS) // stored index are for binary operator. Get correct index
922
bytecode.push_back(CODETOKEN(CODETOKEN::FUNCTION, getunaryop("-")));
923
// unary minus are on function table
924
} // parsearithmetic5()
926
void mathex::parseatom(void)
927
// level 6: literal numbers, variables and functions
931
// parentesis expression
932
if(curtok.state == PARSERTOKEN::OPAREN) {
934
if(curtok.state == PARSERTOKEN::CPAREN)
935
throw error("parseatom()", "No expression inside parentesis");
938
if(curtok.state != PARSERTOKEN::CPAREN)
939
throw error("parseatom()", "\")\" expected");
940
nexttoken(); // Added by Hugh Denman (<denmanh@tcd.ie> or hdenman@cantab.net) Oct/03/2003
943
else if(curtok.state == PARSERTOKEN::VALUE) { // numbers
944
bytecode.push_back(CODETOKEN(curtok.value));
948
else if(curtok.state == PARSERTOKEN::VARIABLE) { // variables
949
bytecode.push_back(CODETOKEN(CODETOKEN::VARIABLE, curtok.idx));
952
// internal C function with one parameters
953
else if(curtok.state == PARSERTOKEN::FUNCTION) { // one parameter C functions
954
parserstack.push_back(curtok);
956
if(curtok.state != PARSERTOKEN::OPAREN)
957
throw error("parseatom()", "\"(\" expected");
959
if(curtok.state == PARSERTOKEN::CPAREN)
960
throw error("parseatom()", "invalid number of arguments");
962
if(curtok.state != PARSERTOKEN::CPAREN)
963
throw error("parseatom()", "\")\" expected");
964
curtok = parserstack.back();
965
parserstack.pop_back();
966
bytecode.push_back(CODETOKEN(CODETOKEN::FUNCTION, curtok.idx));
969
// user defined functions
970
else if(curtok.state == PARSERTOKEN::USERFUNC) { // user defined function
971
parserstack.push_back(curtok);
973
if(curtok.state != PARSERTOKEN::OPAREN)
974
throw error("parseatom()", "\"(\" expected");
976
if(curtok.state == PARSERTOKEN::CPAREN)
978
else { // arguments exist
981
// while(parserstatus && (curtok.state != PARSERTOKEN::END)
982
// &&(curtok.state != PARSERTOKEN::CPAREN)) {
983
while((curtok.state != PARSERTOKEN::END) && (curtok.state != PARSERTOKEN::CPAREN)) {
984
if(curtok.state == PARSERTOKEN::COMMA) {
989
throw error("parseatom()", "unknow error");
992
if(curtok.state != PARSERTOKEN::CPAREN)
993
throw error("parseatom()", "\")\" expected");
995
curtok = parserstack.back();
996
parserstack.pop_back();
998
if ((curtok.numargs != UNDEFARGS) && (i != static_cast<unsigned>(curtok.numargs)))
999
// i is current number of parameters
1000
throw error("parseatom()", "invalid number of arguments");
1002
// number of parameters is correct. Now, put the function
1003
// i is number of arguments
1004
bytecode.push_back(CODETOKEN(CODETOKEN::USERFUNC, curtok.idx, i));
1007
} // user defined functions
1009
else if (curtok.state == PARSERTOKEN::END)
1010
throw error("parseatom()", "unexpected end of expression");
1012
else if (curtok.state == PARSERTOKEN::INVALID)
1013
throw error("parseatom()", "invalid token on expression");
1015
else // it not occur
1016
throw error("parseatom()", "unknow error");
1021
} // namespace smlib {