forked from 0ad/0ad
janwas
5bf9bca9ef
there are too many W4 and "potentially uninitialized", so those are disabled by 0ad_warning_disable.h. the silly "int x = strlen" and very dangerous "int x = (void*)p" (and vice versa) problems are fixed. This was SVN commit r5526.
514 lines
19 KiB
C++
514 lines
19 KiB
C++
#include "precompiled.h"
|
|
#include "0ad_warning_disable.h"
|
|
//***************************************************************************
|
|
//
|
|
// SrExpTABLE.H
|
|
// By Marcelo Kallmann 08/98 - Brazil
|
|
//
|
|
//***************************************************************************
|
|
|
|
# include <math.h>
|
|
# include <string.h>
|
|
# include <stdlib.h>
|
|
|
|
# include "sr_array.h"
|
|
# include "sr_string.h"
|
|
# include "sr_exp_table.h"
|
|
|
|
//# define SR_USE_TRACE1 // Parse
|
|
//# define SR_USE_TRACE2 // Eval
|
|
//# define SR_USE_TRACE3 // Table
|
|
# include "sr_trace.h"
|
|
|
|
// ============================= EToken ========================================
|
|
|
|
typedef SrExpTable::Token EToken;
|
|
|
|
inline void setNumb ( EToken& t, double d ) { t.type=(char)EToken::Numb; t.data.realval=d; }
|
|
inline void setVar ( EToken& t, int id ) { t.type=(char)EToken::Var; t.data.index=id; }
|
|
inline void setFunc ( EToken& t, int id ) { t.type=(char)EToken::Func; t.data.index=id; }
|
|
inline void setBinOp ( EToken& t, char op ) { t.type=(char)EToken::BinOp; t.data.code=op; }
|
|
inline void setUnaOp ( EToken& t, char op ) { t.type=(char)EToken::UnaOp; t.data.code=op; }
|
|
inline void setLeftPar ( EToken& t ) { t.type=(char)EToken::LeftPar; }
|
|
inline void setRightPar ( EToken& t ) { t.type=(char)EToken::RightPar; }
|
|
|
|
//======================= Modes while parsing =================================
|
|
|
|
enum srParMode { srParModeUnary, srParModeBinary, srParModeFunction };
|
|
|
|
//======================== functions methods ===================================
|
|
|
|
enum srFunc { srFuncAbs, srFuncAcos, srFuncAsin, srFuncAtan, srFuncCeil,
|
|
srFuncCos, srFuncExp, srFuncFact, srFuncFloor, srFuncLn,
|
|
srFuncLog, srFuncRound, srFuncSign, srFuncSin, srFuncSqrt,
|
|
srFuncTan, srFuncUndefined }; // Alphabetical order !
|
|
|
|
static char *Functions[] = { "abs", "acos", "asin", "atan", "ceil",
|
|
"cos", "exp", "fact", "floor", "ln",
|
|
"log", "round", "sign", "sin", "sqrt",
|
|
"tan" }; // Alphabetical order !
|
|
|
|
static int compfunc ( const void *a, const void *b )
|
|
{
|
|
return strcmp ( ((char*)a), *((char**)b) );
|
|
}
|
|
|
|
static srFunc func_code ( const char *st )
|
|
{
|
|
void *result = bsearch ( (const void*)st, // to search
|
|
(const void*)Functions, // table
|
|
(size_t)srFuncUndefined, // num elems in table
|
|
(size_t)sizeof(char*), // size of each elem in table
|
|
compfunc // compare function
|
|
);
|
|
if (!result) return srFuncUndefined;
|
|
return (srFunc) ( ((intptr_t)result-(intptr_t)Functions)/sizeof(int) );
|
|
}
|
|
|
|
const char *SrExpTable::function_name ( int index )
|
|
{
|
|
if ( index<0 || index>=(int)srFuncUndefined ) return 0;
|
|
return Functions[index];
|
|
}
|
|
|
|
static int oprank ( char op )
|
|
{
|
|
switch ( op )
|
|
{ case '[' : return 8;
|
|
case '^' : case '%' : return 7;
|
|
case '/' : case '*' : case '|' : return 6;
|
|
case '+' : case '-' : return 5;
|
|
case '>' : case '<' :
|
|
case ')' : case '(' : // >= <=
|
|
case '~' : case '=' : return 4;
|
|
case 'a' : case 'o' : return 3; // and or
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//===================================== messages ==============================
|
|
// Global functions :
|
|
|
|
SrExpTable::Msg SrExpTable::translate_error ( SrInput::ErrorType e )
|
|
{
|
|
switch ( e )
|
|
{ case SrInput::UndefChar : return MsgUndefChar;
|
|
case SrInput::TooBig : return MsgNameTooBig;
|
|
case SrInput::OpenString : return MsgStringNotClosed;
|
|
case SrInput::InvalidPoint : return MsgMisplacedDecPoint;
|
|
default : return MsgOk;
|
|
}
|
|
}
|
|
|
|
const char* SrExpTable::msg_desc ( SrExpTable::Msg m )
|
|
{
|
|
switch ( m )
|
|
{ // messages translated from sr_input.h :
|
|
case MsgUndefChar : return SrInput::error_desc ( SrInput::UndefChar );
|
|
case MsgNameTooBig : return SrInput::error_desc ( SrInput::TooBig );
|
|
case MsgStringNotClosed : return SrInput::error_desc ( SrInput::OpenString );
|
|
case MsgMisplacedDecPoint : return SrInput::error_desc ( SrInput::InvalidPoint );
|
|
// new messages :
|
|
case MsgOk : return "Ok";
|
|
case MsgUnexpectedEndOfFile : return "Unexpected end of file encountered";
|
|
case MsgUndefinedName : return "Undefined variable";
|
|
case MsgUndefBinOp : return "Undefined binary operator: internal error";
|
|
case MsgUndefUnaOp : return "Undefined unary operator: internal error";
|
|
case MsgUndefPreDefFunc : return "Undefined pre-defined function: internal error";
|
|
case MsgOperandExpected : return "Operand expected in expression";
|
|
case MsgOperatorExpected : return "Operator expected in expression";
|
|
case MsgFuncWithoutPar : return "Function name must be followed by a left parenthesis";
|
|
case MsgDivideByZero : return "Divide by zero";
|
|
case MsgRaisedZeroToNeg : return "Cannot raise zero to a negative power";
|
|
case MsgRaisedNegToReal : return "Cannot raise a negative number to a non-integer power";
|
|
case MsgUnmatchedParentesis : return "Unmatched parentesis";
|
|
case MsgAcosArgsOutOfRange : return "Arc cos arguments out of range";
|
|
case MsgAsinArgsOutOfRange : return "Arc sin arguments out of range";
|
|
case MsgStackEmpty : return "Stack is empty";
|
|
case MsgSqrtArgIsNeg : return "Sqrt argument is negative";
|
|
default : return NULL;
|
|
}
|
|
}
|
|
|
|
//============================ internal ======================================
|
|
|
|
# define ISBINARY(c) strchr("+-*/^%|><=[",c)
|
|
# define ISUNARY(c) strchr("+-!",c)
|
|
|
|
static bool is_binary ( const SrString& token )
|
|
{
|
|
if ( strchr("+-*/^%|><~=[",token[0]) ) return true;
|
|
if ( token=="and" || token=="or" ) return true;
|
|
return false;
|
|
}
|
|
|
|
static SrExpTable::Msg get_translated_token ( SrInput& in,
|
|
int& curline,
|
|
EToken& etok,
|
|
srParMode mode,
|
|
const char* delimeters,
|
|
SrExpTableVarManager* varman )
|
|
{
|
|
SrInput::TokenType toktype;
|
|
SrString token;
|
|
|
|
toktype = in.get_token(token);
|
|
curline = in.curline();
|
|
if ( toktype==SrInput::Error ) return SrExpTable::translate_error ( in.last_error() );
|
|
if ( toktype==SrInput::EndOfFile ) return SrExpTable::MsgUnexpectedEndOfFile;
|
|
|
|
if ( delimeters && toktype==SrInput::Delimiter && strchr(delimeters,token[0]) )
|
|
{ in.unget_token ( token, toktype );
|
|
return SrExpTable::MsgUnexpectedEndOfFile; // permits to stop the parser before end of file
|
|
}
|
|
|
|
if ( mode==srParModeUnary && ISUNARY(token[0]) )
|
|
{ setUnaOp ( etok, token[0] );
|
|
SR_TRACE1 ( "Got a unary op: ["<<token<<"]" );
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
|
|
if ( (mode==srParModeBinary || mode==srParModeFunction) && is_binary(token) )
|
|
{ char op = token[0];
|
|
if ( op=='>' || op=='<' )
|
|
{ toktype = in.get_token(token);
|
|
if ( toktype==SrInput::Error ) return SrExpTable::translate_error ( in.last_error() );
|
|
curline = in.curline();
|
|
if ( op=='>' && token[0]=='=' ) op = ')'; // that will mean >=
|
|
else
|
|
if ( op=='<' && token[0]=='=' ) op = '('; // that will mean <=
|
|
else
|
|
if ( op=='<' && token[0]=='>' ) op = '~'; // <> mean difference op ~
|
|
else
|
|
in.unget_token ( token, toktype );
|
|
}
|
|
else if ( op=='=' )
|
|
{ toktype = in.get_token(token);
|
|
if ( toktype==SrInput::Error ) return SrExpTable::translate_error ( in.last_error() );
|
|
if ( token[0]!='=' ) return SrExpTable::MsgWrongEqualOperator;
|
|
}
|
|
else if ( op=='[' )
|
|
{
|
|
in.unget_token ( "(", SrInput::Delimiter ); // so that arrays indices may form an expression
|
|
}
|
|
setBinOp ( etok, op );
|
|
SR_TRACE1 ( "Got a binary op: ["<<op<<"]");
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
|
|
if ( token[0]=='(' ) { setLeftPar(etok); SR_TRACE1("Got a left par.\n"); return SrExpTable::MsgOk; }
|
|
if ( token[0]==')' ) { setRightPar(etok); SR_TRACE1("Got a right par.\n"); return SrExpTable::MsgOk; }
|
|
if ( token[0]==']' ) { setRightPar(etok); SR_TRACE1("Got array right par.\n"); return SrExpTable::MsgOk; }
|
|
|
|
if ( toktype==SrInput::Name ) // bool(int), func or var
|
|
{ if ( strcmp(token,"true")==0 ) { setNumb(etok,1.0); SR_TRACE1("Got a true.\n"); return SrExpTable::MsgOk; }
|
|
if ( strcmp(token,"false")==0 ) { setNumb(etok,0.0); SR_TRACE1("Got a false.\n"); return SrExpTable::MsgOk; }
|
|
|
|
srFunc f = func_code ( token ); // found a func?
|
|
if ( f!=srFuncUndefined )
|
|
{ setFunc(etok,f); SR_TRACE1("Got func ["<<(Functions[(int)f])<<"]"); return SrExpTable::MsgOk; }
|
|
|
|
if ( !varman ) return SrExpTable::MsgUndefinedName; // found a name that is not a function or variable
|
|
setVar ( etok, varman->get_variable_index(token) ); // get the user index for the variable
|
|
SR_TRACE1("Got a var ["<<token<<"], index "<<etok.data.index );
|
|
if ( etok.data.index<0 ) return SrExpTable::MsgUndefinedName;
|
|
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
|
|
if ( toktype==SrInput::Integer || toktype==SrInput::Real )
|
|
{ setNumb(etok,(double)atof(token)); SR_TRACE1("Got a number: "<<(atof(token))); return SrExpTable::MsgOk; }
|
|
|
|
return SrExpTable::MsgUndefChar;
|
|
}
|
|
|
|
//===================================== SrExpTable ===================================================
|
|
|
|
SrExpTable::SrExpTable ()
|
|
{
|
|
_varman = 0;
|
|
_delimiters = 0;
|
|
}
|
|
|
|
SrExpTable::~SrExpTable ()
|
|
{
|
|
if ( _varman ) _varman->unref();
|
|
delete _delimiters;
|
|
}
|
|
|
|
void SrExpTable::set_var_manager ( SrExpTableVarManager* var_man )
|
|
{
|
|
if ( _varman ) _varman->unref();
|
|
_varman = var_man;
|
|
_varman->ref();
|
|
}
|
|
|
|
void SrExpTable::set_parser_delimiters ( const char *delimiters )
|
|
{
|
|
sr_string_set ( _delimiters, delimiters );
|
|
}
|
|
|
|
SrExpTable::Msg SrExpTable::parse ( SrInput& in, int &curline )
|
|
{
|
|
Msg msg;
|
|
Token etok, t;
|
|
srParMode mode = srParModeUnary;
|
|
|
|
_table.size ( 0 );
|
|
_stack.size ( 0 );
|
|
|
|
while ( true )
|
|
{
|
|
msg = get_translated_token ( in, curline, etok, mode, _delimiters, _varman );
|
|
if ( msg==MsgUnexpectedEndOfFile ) break;
|
|
if ( msg!=MsgOk ) return msg;
|
|
|
|
if ( mode==srParModeUnary && (etok.type==Token::BinOp||etok.type==Token::RightPar) )
|
|
{ return MsgOperandExpected; }
|
|
|
|
if ( mode==srParModeBinary && etok.type!=Token::BinOp && etok.type!=Token::RightPar )
|
|
{ return MsgOperatorExpected; }
|
|
|
|
if ( mode==srParModeFunction && etok.type!=Token::LeftPar )
|
|
{ return MsgFuncWithoutPar; }
|
|
|
|
switch ( etok.type )
|
|
{ case Token::Numb :
|
|
case Token::Var : _stack.push(etok); mode=srParModeBinary; break;
|
|
case Token::LeftPar :
|
|
case Token::UnaOp : _stack.push(etok); mode=srParModeUnary; break;
|
|
case Token::Func : _stack.push(etok); mode=srParModeFunction; break;
|
|
|
|
case Token::BinOp :
|
|
while ( !_stack.empty() )
|
|
{ t=_stack.top ();
|
|
if ( t.type==Token::BinOp && oprank(etok.data.code)>oprank(t.data.code) ) break;
|
|
if ( t.type==Token::LeftPar ) break;
|
|
_stack.pop(); _table.push(t);
|
|
}
|
|
_stack.push(etok); mode=srParModeUnary; break;
|
|
|
|
case Token::RightPar :
|
|
if ( _stack.empty() ) return MsgStackEmpty;
|
|
while ( !_stack.empty() )
|
|
{ t=_stack.pop();
|
|
if (t.type==Token::LeftPar) break;
|
|
_table.push(t);
|
|
}
|
|
if ( t.type!=Token::LeftPar ) return MsgOk; // here the stack will be empty
|
|
if ( !_stack.empty() && _stack.top().type==Token::Func )
|
|
_table.push( _stack.pop() );
|
|
mode=srParModeBinary;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( mode==srParModeUnary) return MsgOperandExpected;
|
|
|
|
while ( !_stack.empty() )
|
|
{ if ( _stack.top().type==Token::LeftPar ) { return MsgUnmatchedParentesis; }
|
|
_table.push( _stack.pop() );
|
|
}
|
|
|
|
_table.push().type=Token::EndMark; // mark the end
|
|
|
|
# ifdef SR_USE_TRACE3
|
|
sr_out << "ExpTable: " << *this << srnl;
|
|
# endif
|
|
|
|
return MsgOk;
|
|
}
|
|
|
|
static SrExpTable::Msg operateBinOp ( double x, double y, char op,
|
|
SrArray<EToken>& stack,
|
|
SrExpTableVarManager* varman )
|
|
{
|
|
double r;
|
|
switch ( op )
|
|
{ case '[' : r = varman->get_variable_value(int(x),int(y)); break;
|
|
case '+' : r = x+y; break;
|
|
case '-' : r = x-y; break;
|
|
case '/' : if (!y) return SrExpTable::MsgDivideByZero; r=x/y; break;
|
|
case '|' : if (!y) return SrExpTable::MsgDivideByZero; r=(double)(((int)x)|((int)y)); break;
|
|
case '*' : r=x*y; break;
|
|
case '%' : r = (x/100.0)*y; break;
|
|
case '^' : if ( x==0.0 && y<0.0 ) return SrExpTable::MsgRaisedZeroToNeg;
|
|
if ( x<0.0 && y!=(int)y ) return SrExpTable::MsgRaisedNegToReal;
|
|
r=pow(x,y); break;
|
|
case 'a' : r = x!=0 && y!=0? 1.0:0.0; break;
|
|
case 'o' : r = x!=0 || y!=0? 1.0:0.0; break;
|
|
case '>' : r = x>y? 1.0:0.0; break;
|
|
case '<' : r = x<y? 1.0:0.0; break;
|
|
case ')' : r = x>=y? 1.0:0.0; break;
|
|
case '(' : r = x<=y? 1.0:0.0; break;
|
|
case '~' : r = x!=y? 1.0:0.0; break;
|
|
case '=' : r = x==y? 1.0:0.0; break;
|
|
default : return SrExpTable::MsgUndefBinOp;
|
|
}
|
|
SR_TRACE2 ( "Operating: "<< x << " " << op << " " << y << " = " << r );
|
|
setNumb ( stack.push(), r );
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
|
|
static SrExpTable::Msg operateUnaOp ( double x, char op, SrArray<EToken>& stack )
|
|
{
|
|
double r;
|
|
switch ( op )
|
|
{ case '+' : r=x; break;
|
|
case '-' : r=-x; break;
|
|
case '!' : r = x==0? 1.0:0.0; break;
|
|
default : return SrExpTable::MsgUndefUnaOp;
|
|
}
|
|
SR_TRACE2 ( "Operating: " << op << " " << x << " = " << r );
|
|
|
|
setNumb ( stack.push(), r );
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
|
|
static double dround ( double x ) { return (x>0.0)? (double)(long int)(x+0.5) : (double)(long int)(x-0.5); }
|
|
static double dsign ( double x ) { return (x<0.0)? -1: (x>0.0)? 1:0; }
|
|
static double dfact ( int x ) { if(x<2)return 1l; long m=(long)x; while(--x>1)m*=(long)x; return(double)m; }
|
|
|
|
static SrExpTable::Msg operateFunc ( double x, srFunc f, SrArray<EToken>& stack )
|
|
{
|
|
double r;
|
|
switch ( f )
|
|
{ case srFuncAbs : r = x<0.0? -x:x; break;
|
|
case srFuncAcos : if ( x<-1.0 || x>1.0 ) return SrExpTable::MsgAcosArgsOutOfRange;
|
|
r=acos(x); break;
|
|
case srFuncAsin : if ( x<-1.0 || x>1.0 ) return SrExpTable::MsgAsinArgsOutOfRange;
|
|
r=asin(x); break;
|
|
case srFuncAtan : r=atan(x); break;
|
|
case srFuncCeil : r=ceil(x); break;
|
|
case srFuncCos : r=cos(x); break;
|
|
case srFuncExp : r=pow(SR_E,x); break;
|
|
case srFuncFact : r=dfact((int)x); break;
|
|
case srFuncFloor : r=floor(x); break;
|
|
case srFuncLn : r=log(x); break;
|
|
case srFuncLog : r=log10(x); break;
|
|
case srFuncSign : r=dsign(x); break;
|
|
case srFuncSin : r=sin(x); break;
|
|
case srFuncSqrt : if ( x<-0.0 ) return SrExpTable::MsgSqrtArgIsNeg;
|
|
r=sqrt(x); break;
|
|
case srFuncTan : r=tan(x); break;
|
|
case srFuncRound : r=dround(x); break;
|
|
default : return SrExpTable::MsgUndefPreDefFunc;
|
|
}
|
|
|
|
SR_TRACE2 ( "Operating: " << Functions[f] << "(" << x << ") = " << r );
|
|
|
|
setNumb ( stack.push(), r );
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
|
|
static SrExpTable::Msg pop_value ( double &v, SrArray<EToken>& stack,
|
|
SrExpTableVarManager* varman, bool isarrayop )
|
|
{
|
|
if ( stack.empty() ) return SrExpTable::MsgStackEmpty;
|
|
EToken t = stack.pop();
|
|
|
|
if ( t.type==EToken::Var )
|
|
{ v = isarrayop? (double)t.data.index : varman->get_variable_value(t.data.index,-1);
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
else if ( t.type==EToken::Numb )
|
|
{ v = t.data.realval;
|
|
return SrExpTable::MsgOk;
|
|
}
|
|
else
|
|
return SrExpTable::MsgOperandExpected;
|
|
}
|
|
|
|
SrExpTable::Msg SrExpTable::evaluate ( double &result )
|
|
{
|
|
int i;
|
|
double u, v;
|
|
Token::Type t;
|
|
Msg msg;
|
|
|
|
_stack.size ( 0 );
|
|
|
|
for ( i=0; i<_table.size(); i++ )
|
|
{ t = (Token::Type) _table[i].type;
|
|
if ( t==Token::EndMark ) break; // Found a mark for the end (only when not compressed)
|
|
|
|
if ( t==Token::Numb || t==Token::Var ) // a value
|
|
{ _stack.push ( _table[i] );
|
|
}
|
|
else
|
|
{ msg = pop_value ( v, _stack, _varman, false );
|
|
if ( msg!=MsgOk ) return msg;
|
|
|
|
switch ( t )
|
|
{ case Token::Func :
|
|
msg = operateFunc ( v, (srFunc)_table[i].data.index, _stack );
|
|
break;
|
|
|
|
case Token::UnaOp :
|
|
msg = operateUnaOp ( v, _table[i].data.code, _stack );
|
|
break;
|
|
|
|
case Token::BinOp :
|
|
msg = pop_value ( u, _stack, _varman, _table[i].data.code=='['?true:false );
|
|
if ( msg!=MsgOk ) break;
|
|
msg = operateBinOp ( u, v, _table[i].data.code, _stack, _varman );
|
|
break;
|
|
}
|
|
|
|
if ( msg!=MsgOk ) return msg;
|
|
}
|
|
}
|
|
|
|
if ( _stack.size()!=1 ) return MsgOperatorExpected;
|
|
|
|
return pop_value ( result, _stack, _varman, false );
|
|
}
|
|
|
|
void SrExpTable::compress ()
|
|
{
|
|
_table.compress ();
|
|
_stack.compress();
|
|
}
|
|
|
|
/*! Takes the internal data of t, and leave t as an empty table. */
|
|
// void take_data ( SrExpTable& t );
|
|
|
|
// void take_data ( SrArray<SrExpTableToken>& tokens );
|
|
// void leave_data ( SrArray<SrExpTableToken>& tokens );
|
|
|
|
/*
|
|
void SrExpTable::take_data ( SrExpTable& t )
|
|
{
|
|
if ( _tokens ) free ( _tokens );
|
|
_tokens = t._tokens; t._tokens = 0;
|
|
_size = t._size; t._size = 0;
|
|
_capacity = t._capacity; t._capacity = 0;
|
|
}
|
|
*/
|
|
|
|
//=============================== friends ============================
|
|
|
|
SrOutput& operator << ( SrOutput& o, const SrExpTable& t )
|
|
{
|
|
EToken tk;
|
|
|
|
for ( int i=0; i<t._table.size(); i++ )
|
|
{ tk = t._table[i];
|
|
|
|
switch ( tk.type )
|
|
{ case EToken::Numb : o<<tk.data.realval<<srspc; break;
|
|
case EToken::Var : o<<"var:"<<tk.data.index<<srspc; break;
|
|
case EToken::Func : o<<Functions[tk.data.index]<<srspc; break;
|
|
case EToken::UnaOp :
|
|
case EToken::BinOp : o<<tk.data.code<<srspc; break;
|
|
case EToken::EndMark : return o;
|
|
}
|
|
}
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
//============================ End of File ==============================
|
|
|