ttmath/ttmath/ttmathparser.h

2778 lines
58 KiB
C++

/*
* This file is a part of TTMath Bignum Library
* and is distributed under the 3-Clause BSD Licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* Copyright (c) 2006-2017, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name Tomasz Sowa nor the names of contributors to this
* project may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef headerfilettmathparser
#define headerfilettmathparser
/*!
\file ttmathparser.h
\brief A mathematical parser
*/
#include <cstdio>
#include <vector>
#include <map>
#include <set>
#include "ttmath.h"
#include "ttmathobjects.h"
#include "ttmathmisc.h"
namespace ttmath
{
/*!
\brief Mathematical parser
let x will be an input string meaning an expression for converting:
x = [+|-]Value[operator[+|-]Value][operator[+|-]Value]...
where:
an operator can be:
^ (pow) (the heighest priority)
* (mul) (or multiplication without an operator -- short mul)
/ (div) (* and / have the same priority)
+ (add)
- (sub) (+ and - have the same priority)
< (lower than)
> (greater than)
<= (lower or equal than)
>= (greater or equal than)
== (equal)
!= (not equal) (all above logical operators have the same priority)
&& (logical and)
|| (logical or) (the lowest priority)
short mul:
if the second Value (Var below) is either a variable or function there might not be
an operator between them, e.g.
"[+|-]Value Var" is treated as "[+|-]Value * Var" and the multiplication
has the same priority as a normal multiplication:
4x = 4 * x
2^3m = (2^3)* m
6h^3 = 6 * (h^3)
2sin(pi) = 2 * sin(pi)
etc.
Value can be:
constant e.g. 100, can be preceded by operators for changing the base (radix): [#|&]
# - hex
& - bin
sample: #10 = 16
&10 = 2
variable e.g. pi
another expression between brackets e.g (x)
function e.g. sin(x)
for example a correct input string can be:
"1"
"2.1234"
"2,1234" (they are the same, by default we can either use a comma or a dot)
"1 + 2"
"(1 + 2) * 3"
"pi"
"sin(pi)"
"(1+2)*(2+3)"
"log(2;1234)" there's a semicolon here (not a comma), we use it in functions
for separating parameters
"1 < 2" (the result will be: 1)
"4 < 3" (the result will be: 0)
"2+x" (of course if the variable 'x' is defined)
"4x+10"
"#20+10" = 32 + 10 = 42
"10 ^ -&101" = 10 ^ -5 = 0.00001
"8 * -&10" = 8 * -2 = -16
etc.
we can also use a semicolon for separating any 'x' input strings
for example:
"1+2;4+5"
the result will be on the stack as follows:
stack[0].value=3
stack[1].value=9
*/
template<class ValueType>
class Parser
{
private:
/*!
there are 5 mathematical operators as follows (with their standard priorities):
add (+)
sub (-)
mul (*)
div (/)
pow (^)
and 'shortmul' used when there is no any operators between
a first parameter and a variable or function
(the 'shortmul' has the same priority as the normal multiplication )
*/
class MatOperator
{
public:
enum Type
{
none,add,sub,mul,div,pow,lt,gt,let,get,eq,neq,lor,land,shortmul
};
enum Assoc
{
right, // right-associative
non_right // associative or left-associative
};
Type GetType() const { return type; }
int GetPriority() const { return priority; }
Assoc GetAssoc() const { return assoc; }
void SetType(Type t)
{
type = t;
assoc = non_right;
switch( type )
{
case lor:
priority = 4;
break;
case land:
priority = 5;
break;
case eq:
case neq:
case lt:
case gt:
case let:
case get:
priority = 7;
break;
case add:
case sub:
priority = 10;
break;
case mul:
case shortmul:
case div:
priority = 12;
break;
case pow:
priority = 14;
assoc = right;
break;
default:
Error( err_internal_error );
break;
}
}
MatOperator(): type(none), priority(0), assoc(non_right)
{
}
private:
Type type;
int priority;
Assoc assoc;
}; // end of MatOperator class
public:
/*!
Objects of type 'Item' we are keeping on our stack
*/
struct Item
{
enum Type
{
none, numerical_value, mat_operator, first_bracket,
last_bracket, variable, semicolon
};
// The kind of type which we're keeping
Type type;
// if type == numerical_value
ValueType value;
// if type == mat_operator
MatOperator moperator;
/*
if type == first_bracket
if 'function' is set to true it means that the first recognized bracket
was the bracket from function in other words we must call a function when
we'll find the 'last' bracket
*/
bool function;
// if function is true
std::string function_name;
/*
the sign of value
it can be for type==numerical_value or type==first_bracket
when it's true it means e.g. that value is equal -value
*/
bool sign;
Item(): type(none), function(false), sign(false)
{
}
}; // end of Item struct
/*!
stack on which we're keeping the Items
at the end of parsing we'll have the result here
the result don't have to be one value, it can be
more than one if we have used a semicolon in the global space
e.g. such input string "1+2;3+4" will generate a result:
stack[0].value=3
stack[1].value=7
you should check if the stack is not empty, because if there was
a syntax error in the input string then we do not have any results
on the stack
*/
std::vector<Item> stack;
private:
/*!
size of the stack when we're starting parsing of the string
if it's to small while parsing the stack will be automatically resized
*/
const int default_stack_size;
/*!
index of an object in our stack
it's pointing on the place behind the last element
for example at the beginning of parsing its value is zero
*/
unsigned int stack_index;
/*!
code of the last error
*/
ErrorCode error;
/*!
pointer to the currently reading char
when an error has occured it may be used to count the index of the wrong character
*/
const char * pstring;
/*!
the base (radix) of the mathematic system (for example it may be '10')
*/
int base;
/*!
the unit of angles used in: sin,cos,tan,cot,asin,acos,atan,acot
0 - deg
1 - rad (default)
2 - grad
*/
int deg_rad_grad;
/*!
a pointer to an object which tell us whether we should stop calculating or not
*/
const volatile StopCalculating * pstop_calculating;
/*!
a pointer to the user-defined variables' table
*/
const Objects * puser_variables;
/*!
a pointer to the user-defined functions' table
*/
const Objects * puser_functions;
typedef std::map<std::string, ValueType> FunctionLocalVariables;
/*!
a pointer to the local variables of a function
*/
const FunctionLocalVariables * pfunction_local_variables;
/*!
a temporary set using during parsing user defined variables
*/
std::set<std::string> visited_variables;
/*!
a temporary set using during parsing user defined functions
*/
std::set<std::string> visited_functions;
/*!
pfunction is the type of pointer to a mathematic function
these mathematic functions are private members of this class,
they are the wrappers for standard mathematics function
'pstack' is the pointer to the first argument on our stack
'amount_of_arg' tell us how many argument there are in our stack
'result' is the reference for result of function
*/
typedef void (Parser<ValueType>::*pfunction)(int pstack, int amount_of_arg, ValueType & result);
/*!
pfunction is the type of pointer to a method which returns value of variable
*/
typedef void (ValueType::*pfunction_var)();
/*!
table of mathematic functions
this map consists of:
std::string - function's name
pfunction - pointer to specific function
*/
typedef std::map<std::string, pfunction> FunctionsTable;
FunctionsTable functions_table;
/*!
table of mathematic operators
this map consists of:
std::string - operators's name
MatOperator::Type - type of the operator
*/
typedef std::map<std::string, typename MatOperator::Type> OperatorsTable;
OperatorsTable operators_table;
/*!
table of mathematic variables
this map consists of:
std::string - variable's name
pfunction_var - pointer to specific function which returns value of variable
*/
typedef std::map<std::string, pfunction_var> VariablesTable;
VariablesTable variables_table;
/*!
some coefficients used when calculating the gamma (or factorial) function
*/
CGamma<ValueType> cgamma;
/*!
temporary object for a whole string when Parse(std::wstring) is used
*/
std::string wide_to_ansi;
/*!
group character (used when parsing)
default zero (not used)
*/
int group;
/*!
characters used as a comma
default: '.' and ','
comma2 can be zero (it means it is not used)
*/
int comma, comma2;
/*!
an additional character used as a separator between function parameters
(semicolon is used always)
*/
int param_sep;
/*!
true if something was calculated (at least one mathematical operator was used or a function or a variable)
*/
bool calculated;
/*!
we're using this method for reporting an error
*/
static void Error(ErrorCode code)
{
throw code;
}
/*!
this method skips the white character from the string
it's moving the 'pstring' to the first no-white character
*/
void SkipWhiteCharacters()
{
while( (*pstring==' ' ) || (*pstring=='\t') )
++pstring;
}
/*!
an auxiliary method for RecurrenceParsingVariablesOrFunction(...)
*/
void RecurrenceParsingVariablesOrFunction_CheckStopCondition(bool variable, const std::string & name)
{
if( variable )
{
if( visited_variables.find(name) != visited_variables.end() )
Error( err_variable_loop );
}
else
{
if( visited_functions.find(name) != visited_functions.end() )
Error( err_functions_loop );
}
}
/*!
an auxiliary method for RecurrenceParsingVariablesOrFunction(...)
*/
void RecurrenceParsingVariablesOrFunction_AddName(bool variable, const std::string & name)
{
if( variable )
visited_variables.insert( name );
else
visited_functions.insert( name );
}
/*!
an auxiliary method for RecurrenceParsingVariablesOrFunction(...)
*/
void RecurrenceParsingVariablesOrFunction_DeleteName(bool variable, const std::string & name)
{
if( variable )
visited_variables.erase( name );
else
visited_functions.erase( name );
}
/*!
this method returns the value of a variable or function
by creating a new instance of the mathematical parser
and making the standard parsing algorithm on the given string
this method is used only during parsing user defined variables or functions
(there can be a recurrence here therefore we're using 'visited_variables'
and 'visited_functions' sets to make a stop condition)
*/
ValueType RecurrenceParsingVariablesOrFunction(bool variable, const std::string & name, const char * new_string,
FunctionLocalVariables * local_variables = 0)
{
RecurrenceParsingVariablesOrFunction_CheckStopCondition(variable, name);
RecurrenceParsingVariablesOrFunction_AddName(variable, name);
Parser<ValueType> NewParser(*this);
ErrorCode err;
NewParser.pfunction_local_variables = local_variables;
try
{
err = NewParser.Parse(new_string);
}
catch(...)
{
RecurrenceParsingVariablesOrFunction_DeleteName(variable, name);
throw;
}
RecurrenceParsingVariablesOrFunction_DeleteName(variable, name);
if( err != err_ok )
Error( err );
if( NewParser.stack.size() != 1 )
Error( err_must_be_only_one_value );
if( NewParser.stack[0].type != Item::numerical_value )
// I think there shouldn't be this error here
Error( err_incorrect_value );
return NewParser.stack[0].value;
}
public:
/*!
this method returns the user-defined value of a variable
*/
bool GetValueOfUserDefinedVariable(const std::string & variable_name,ValueType & result)
{
if( !puser_variables )
return false;
const char * string_value;
if( puser_variables->GetValue(variable_name, &string_value) != err_ok )
return false;
result = RecurrenceParsingVariablesOrFunction(true, variable_name, string_value);
calculated = true;
return true;
}
/*!
this method returns the value of a local variable of a function
*/
bool GetValueOfFunctionLocalVariable(const std::string & variable_name, ValueType & result)
{
if( !pfunction_local_variables )
return false;
typename FunctionLocalVariables::const_iterator i = pfunction_local_variables->find(variable_name);
if( i == pfunction_local_variables->end() )
return false;
result = i->second;
return true;
}
/*!
this method returns the value of a variable from variables' table
we make an object of type ValueType then call a method which
sets the correct value in it and finally we'll return the object
*/
ValueType GetValueOfVariable(const std::string & variable_name)
{
ValueType result;
if( GetValueOfFunctionLocalVariable(variable_name, result) )
return result;
if( GetValueOfUserDefinedVariable(variable_name, result) )
return result;
typename std::map<std::string, pfunction_var>::iterator i =
variables_table.find(variable_name);
if( i == variables_table.end() )
Error( err_unknown_variable );
(result.*(i->second))();
calculated = true;
return result;
}
private:
/*!
wrappers for mathematic functions
'sindex' is pointing on the first argument on our stack
(the second argument has 'sindex+2'
because 'sindex+1' is guaranted for the 'semicolon' operator)
the third artument has of course 'sindex+4' etc.
'result' will be the result of the function
(we're using exceptions here for example when function gets an improper argument)
*/
/*!
used by: sin,cos,tan,cot
*/
ValueType ConvertAngleToRad(const ValueType & input)
{
if( deg_rad_grad == 1 ) // rad
return input;
ValueType result;
ErrorCode err;
if( deg_rad_grad == 0 ) // deg
result = ttmath::DegToRad(input, &err);
else // grad
result = ttmath::GradToRad(input, &err);
if( err != err_ok )
Error( err );
return result;
}
/*!
used by: asin,acos,atan,acot
*/
ValueType ConvertRadToAngle(const ValueType & input)
{
if( deg_rad_grad == 1 ) // rad
return input;
ValueType result;
ErrorCode err;
if( deg_rad_grad == 0 ) // deg
result = ttmath::RadToDeg(input, &err);
else // grad
result = ttmath::RadToGrad(input, &err);
if( err != err_ok )
Error( err );
return result;
}
void Gamma(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Gamma(stack[sindex].value, cgamma, &err, pstop_calculating);
if(err != err_ok)
Error( err );
}
/*!
factorial
result = 1 * 2 * 3 * 4 * .... * x
*/
void Factorial(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Factorial(stack[sindex].value, cgamma, &err, pstop_calculating);
if(err != err_ok)
Error( err );
}
void Abs(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ttmath::Abs(stack[sindex].value);
}
void Sin(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Sin( ConvertAngleToRad(stack[sindex].value), &err );
if(err != err_ok)
Error( err );
}
void Cos(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Cos( ConvertAngleToRad(stack[sindex].value), &err );
if(err != err_ok)
Error( err );
}
void Tan(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Tan(ConvertAngleToRad(stack[sindex].value), &err);
if(err != err_ok)
Error( err );
}
void Cot(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Cot(ConvertAngleToRad(stack[sindex].value), &err);
if(err != err_ok)
Error( err );
}
void Int(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ttmath::SkipFraction(stack[sindex].value);
}
void Round(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = stack[sindex].value;
if( result.Round() )
Error( err_overflow );
}
void Ln(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Ln(stack[sindex].value, &err);
if(err != err_ok)
Error( err );
}
void Log(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 2 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Log(stack[sindex].value, stack[sindex+2].value, &err);
if(err != err_ok)
Error( err );
}
void Exp(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Exp(stack[sindex].value, &err);
if(err != err_ok)
Error( err );
}
void Max(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args == 0 )
{
result.SetMax();
return;
}
result = stack[sindex].value;
for(int i=1 ; i<amount_of_args ; ++i)
{
if( result < stack[sindex + i*2].value )
result = stack[sindex + i*2].value;
}
}
void Min(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args == 0 )
{
result.SetMin();
return;
}
result = stack[sindex].value;
for(int i=1 ; i<amount_of_args ; ++i)
{
if( result > stack[sindex + i*2].value )
result = stack[sindex + i*2].value;
}
}
void ASin(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
ValueType temp = ttmath::ASin(stack[sindex].value, &err);
if(err != err_ok)
Error( err );
result = ConvertRadToAngle(temp);
}
void ACos(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
ValueType temp = ttmath::ACos(stack[sindex].value, &err);
if(err != err_ok)
Error( err );
result = ConvertRadToAngle(temp);
}
void ATan(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ConvertRadToAngle(ttmath::ATan(stack[sindex].value));
}
void ACot(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ConvertRadToAngle(ttmath::ACot(stack[sindex].value));
}
void Sgn(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ttmath::Sgn(stack[sindex].value);
}
void Mod(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 2 )
Error( err_improper_amount_of_arguments );
if( stack[sindex+2].value.IsZero() )
Error( err_improper_argument );
result = stack[sindex].value;
uint c = result.Mod(stack[sindex+2].value);
if( c )
Error( err_overflow );
}
void If(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 3 )
Error( err_improper_amount_of_arguments );
if( !stack[sindex].value.IsZero() )
result = stack[sindex+2].value;
else
result = stack[sindex+4].value;
}
void Or(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args < 2 )
Error( err_improper_amount_of_arguments );
for(int i=0 ; i<amount_of_args ; ++i)
{
if( !stack[sindex+i*2].value.IsZero() )
{
result.SetOne();
return;
}
}
result.SetZero();
}
void And(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args < 2 )
Error( err_improper_amount_of_arguments );
for(int i=0 ; i<amount_of_args ; ++i)
{
if( stack[sindex+i*2].value.IsZero() )
{
result.SetZero();
return;
}
}
result.SetOne();
}
void Not(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
if( stack[sindex].value.IsZero() )
result.SetOne();
else
result.SetZero();
}
void DegToRad(int sindex, int amount_of_args, ValueType & result)
{
ErrorCode err = err_ok;
if( amount_of_args == 1 )
{
result = ttmath::DegToRad(stack[sindex].value, &err);
}
else
if( amount_of_args == 3 )
{
result = ttmath::DegToRad( stack[sindex].value, stack[sindex+2].value,
stack[sindex+4].value, &err);
}
else
Error( err_improper_amount_of_arguments );
if( err != err_ok )
Error( err );
}
void RadToDeg(int sindex, int amount_of_args, ValueType & result)
{
ErrorCode err;
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ttmath::RadToDeg(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void DegToDeg(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 3 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::DegToDeg( stack[sindex].value, stack[sindex+2].value,
stack[sindex+4].value, &err);
if( err != err_ok )
Error( err );
}
void GradToRad(int sindex, int amount_of_args, ValueType & result)
{
ErrorCode err;
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ttmath::GradToRad(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void RadToGrad(int sindex, int amount_of_args, ValueType & result)
{
ErrorCode err;
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ttmath::RadToGrad(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void DegToGrad(int sindex, int amount_of_args, ValueType & result)
{
ErrorCode err = err_ok;
if( amount_of_args == 1 )
{
result = ttmath::DegToGrad(stack[sindex].value, &err);
}
else
if( amount_of_args == 3 )
{
result = ttmath::DegToGrad( stack[sindex].value, stack[sindex+2].value,
stack[sindex+4].value, &err);
}
else
Error( err_improper_amount_of_arguments );
if( err != err_ok )
Error( err );
}
void GradToDeg(int sindex, int amount_of_args, ValueType & result)
{
ErrorCode err;
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = ttmath::GradToDeg(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Ceil(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Ceil(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Floor(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Floor(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Sqrt(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Sqrt(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Sinh(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Sinh(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Cosh(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Cosh(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Tanh(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Tanh(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Coth(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Coth(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void Root(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 2 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::Root(stack[sindex].value, stack[sindex+2].value, &err);
if( err != err_ok )
Error( err );
}
void ASinh(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::ASinh(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void ACosh(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::ACosh(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void ATanh(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::ATanh(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void ACoth(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
ErrorCode err;
result = ttmath::ACoth(stack[sindex].value, &err);
if( err != err_ok )
Error( err );
}
void BitAnd(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 2 )
Error( err_improper_amount_of_arguments );
uint err;
result = stack[sindex].value;
err = result.BitAnd(stack[sindex+2].value);
switch(err)
{
case 1:
Error( err_overflow );
break;
case 2:
Error( err_improper_argument );
break;
}
}
void BitOr(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 2 )
Error( err_improper_amount_of_arguments );
uint err;
result = stack[sindex].value;
err = result.BitOr(stack[sindex+2].value);
switch(err)
{
case 1:
Error( err_overflow );
break;
case 2:
Error( err_improper_argument );
break;
}
}
void BitXor(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 2 )
Error( err_improper_amount_of_arguments );
uint err;
result = stack[sindex].value;
err = result.BitXor(stack[sindex+2].value);
switch(err)
{
case 1:
Error( err_overflow );
break;
case 2:
Error( err_improper_argument );
break;
}
}
void Sum(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args == 0 )
Error( err_improper_amount_of_arguments );
result = stack[sindex].value;
for(int i=1 ; i<amount_of_args ; ++i )
if( result.Add( stack[ sindex + i*2 ].value ) )
Error( err_overflow );
}
void Avg(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args == 0 )
Error( err_improper_amount_of_arguments );
result = stack[sindex].value;
for(int i=1 ; i<amount_of_args ; ++i )
if( result.Add( stack[ sindex + i*2 ].value ) )
Error( err_overflow );
if( result.Div( amount_of_args ) )
Error( err_overflow );
}
void Frac(int sindex, int amount_of_args, ValueType & result)
{
if( amount_of_args != 1 )
Error( err_improper_amount_of_arguments );
result = stack[sindex].value;
result.RemainFraction();
}
/*!
we use such a method because 'wvsprintf' is not everywhere defined
*/
void Sprintf(char * buffer, int par)
{
char buf[30]; // char, not wchar_t
int i;
#ifdef _MSC_VER
#pragma warning( disable: 4996 )
//warning C4996: 'sprintf': This function or variable may be unsafe.
#endif
sprintf(buf, "%d", par);
for(i=0 ; buf[i] != 0 ; ++i)
buffer[i] = buf[i];
buffer[i] = 0;
#ifdef _MSC_VER
#pragma warning( default: 4996 )
#endif
}
/*!
this method returns the value from a user-defined function
(look at the description in 'CallFunction(...)')
*/
bool GetValueOfUserDefinedFunction(const std::string & function_name, int amount_of_args, int sindex)
{
if( !puser_functions )
return false;
const char * string_value;
int param;
if( puser_functions->GetValueAndParam(function_name, &string_value, &param) != err_ok )
return false;
if( param != amount_of_args )
Error( err_improper_amount_of_arguments );
FunctionLocalVariables local_variables;
if( amount_of_args > 0 )
{
char buffer[30];
// x = x1
buffer[0] = 'x';
buffer[1] = 0;
local_variables.insert( std::make_pair(buffer, stack[sindex].value) );
for(int i=0 ; i<amount_of_args ; ++i)
{
buffer[0] = 'x';
Sprintf(buffer+1, i+1);
local_variables.insert( std::make_pair(buffer, stack[sindex + i*2].value) );
}
}
stack[sindex-1].value = RecurrenceParsingVariablesOrFunction(false, function_name, string_value, &local_variables);
calculated = true;
return true;
}
/*
we're calling a specific function
function_name - name of the function
amount_of_args - how many arguments there are on our stack
(function must check whether this is a correct value or not)
sindex - index of the first argument on the stack (sindex is greater than zero)
if there aren't any arguments on the stack 'sindex' pointing on
a non existend element (after the first bracket)
result will be stored in 'stack[sindex-1].value'
(we don't have to set the correct type of this element, it'll be set later)
*/
void CallFunction(const std::string & function_name, int amount_of_args, int sindex)
{
if( GetValueOfUserDefinedFunction(function_name, amount_of_args, sindex) )
return;
typename FunctionsTable::iterator i = functions_table.find( function_name );
if( i == functions_table.end() )
Error( err_unknown_function );
/*
calling the specify function
*/
(this->*(i->second))(sindex, amount_of_args, stack[sindex-1].value);
calculated = true;
}
/*!
inserting a function to the functions' table
function_name - name of the function
pf - pointer to the function (to the wrapper)
*/
void InsertFunctionToTable(const char * function_name, pfunction pf)
{
std::string str;
Misc::AssignString(str, function_name);
functions_table.insert( std::make_pair(str, pf) );
}
/*!
inserting a function to the variables' table
(this function returns value of variable)
variable_name - name of the function
pf - pointer to the function
*/
void InsertVariableToTable(const char * variable_name, pfunction_var pf)
{
std::string str;
Misc::AssignString(str, variable_name);
variables_table.insert( std::make_pair(str, pf) );
}
/*!
this method creates the table of functions
*/
void CreateFunctionsTable()
{
InsertFunctionToTable("gamma", &Parser<ValueType>::Gamma);
InsertFunctionToTable("factorial", &Parser<ValueType>::Factorial);
InsertFunctionToTable("abs", &Parser<ValueType>::Abs);
InsertFunctionToTable("sin", &Parser<ValueType>::Sin);
InsertFunctionToTable("cos", &Parser<ValueType>::Cos);
InsertFunctionToTable("tan", &Parser<ValueType>::Tan);
InsertFunctionToTable("tg", &Parser<ValueType>::Tan);
InsertFunctionToTable("cot", &Parser<ValueType>::Cot);
InsertFunctionToTable("ctg", &Parser<ValueType>::Cot);
InsertFunctionToTable("int", &Parser<ValueType>::Int);
InsertFunctionToTable("round", &Parser<ValueType>::Round);
InsertFunctionToTable("ln", &Parser<ValueType>::Ln);
InsertFunctionToTable("log", &Parser<ValueType>::Log);
InsertFunctionToTable("exp", &Parser<ValueType>::Exp);
InsertFunctionToTable("max", &Parser<ValueType>::Max);
InsertFunctionToTable("min", &Parser<ValueType>::Min);
InsertFunctionToTable("asin", &Parser<ValueType>::ASin);
InsertFunctionToTable("acos", &Parser<ValueType>::ACos);
InsertFunctionToTable("atan", &Parser<ValueType>::ATan);
InsertFunctionToTable("atg", &Parser<ValueType>::ATan);
InsertFunctionToTable("acot", &Parser<ValueType>::ACot);
InsertFunctionToTable("actg", &Parser<ValueType>::ACot);
InsertFunctionToTable("sgn", &Parser<ValueType>::Sgn);
InsertFunctionToTable("mod", &Parser<ValueType>::Mod);
InsertFunctionToTable("if", &Parser<ValueType>::If);
InsertFunctionToTable("or", &Parser<ValueType>::Or);
InsertFunctionToTable("and", &Parser<ValueType>::And);
InsertFunctionToTable("not", &Parser<ValueType>::Not);
InsertFunctionToTable("degtorad", &Parser<ValueType>::DegToRad);
InsertFunctionToTable("radtodeg", &Parser<ValueType>::RadToDeg);
InsertFunctionToTable("degtodeg", &Parser<ValueType>::DegToDeg);
InsertFunctionToTable("gradtorad", &Parser<ValueType>::GradToRad);
InsertFunctionToTable("radtograd", &Parser<ValueType>::RadToGrad);
InsertFunctionToTable("degtograd", &Parser<ValueType>::DegToGrad);
InsertFunctionToTable("gradtodeg", &Parser<ValueType>::GradToDeg);
InsertFunctionToTable("ceil", &Parser<ValueType>::Ceil);
InsertFunctionToTable("floor", &Parser<ValueType>::Floor);
InsertFunctionToTable("sqrt", &Parser<ValueType>::Sqrt);
InsertFunctionToTable("sinh", &Parser<ValueType>::Sinh);
InsertFunctionToTable("cosh", &Parser<ValueType>::Cosh);
InsertFunctionToTable("tanh", &Parser<ValueType>::Tanh);
InsertFunctionToTable("tgh", &Parser<ValueType>::Tanh);
InsertFunctionToTable("coth", &Parser<ValueType>::Coth);
InsertFunctionToTable("ctgh", &Parser<ValueType>::Coth);
InsertFunctionToTable("root", &Parser<ValueType>::Root);
InsertFunctionToTable("asinh", &Parser<ValueType>::ASinh);
InsertFunctionToTable("acosh", &Parser<ValueType>::ACosh);
InsertFunctionToTable("atanh", &Parser<ValueType>::ATanh);
InsertFunctionToTable("atgh", &Parser<ValueType>::ATanh);
InsertFunctionToTable("acoth", &Parser<ValueType>::ACoth);
InsertFunctionToTable("actgh", &Parser<ValueType>::ACoth);
InsertFunctionToTable("bitand", &Parser<ValueType>::BitAnd);
InsertFunctionToTable("bitor", &Parser<ValueType>::BitOr);
InsertFunctionToTable("bitxor", &Parser<ValueType>::BitXor);
InsertFunctionToTable("band", &Parser<ValueType>::BitAnd);
InsertFunctionToTable("bor", &Parser<ValueType>::BitOr);
InsertFunctionToTable("bxor", &Parser<ValueType>::BitXor);
InsertFunctionToTable("sum", &Parser<ValueType>::Sum);
InsertFunctionToTable("avg", &Parser<ValueType>::Avg);
InsertFunctionToTable("frac", &Parser<ValueType>::Frac);
}
/*!
this method creates the table of variables
*/
void CreateVariablesTable()
{
InsertVariableToTable("pi", &ValueType::SetPi);
InsertVariableToTable("e", &ValueType::SetE);
}
/*!
converting from a big letter to a small one
*/
int ToLowerCase(int c)
{
if( c>='A' && c<='Z' )
return c - 'A' + 'a';
return c;
}
/*!
this method read the name of a variable or a function
'result' will be the name of a variable or a function
function return 'false' if this name is the name of a variable
or function return 'true' if this name is the name of a function
what should be returned is tested just by a '(' character that means if there's
a '(' character after a name that function returns 'true'
*/
bool ReadName(std::string & result)
{
int character;
result.erase();
character = *pstring;
/*
the first letter must be from range 'a' - 'z' or 'A' - 'Z'
*/
if( ! (( character>='a' && character<='z' ) || ( character>='A' && character<='Z' )) )
Error( err_unknown_character );
do
{
result += static_cast<char>( character );
character = * ++pstring;
}
while( (character>='a' && character<='z') ||
(character>='A' && character<='Z') ||
(character>='0' && character<='9') ||
character=='_' );
SkipWhiteCharacters();
/*
if there's a character '(' that means this name is a name of a function
*/
if( *pstring == '(' )
{
++pstring;
return true;
}
return false;
}
/*!
we're checking whether the first character is '-' or '+'
if it is we'll return 'true' and if it is equally '-' we'll set the 'sign' member of 'result'
*/
bool TestSign(Item & result)
{
SkipWhiteCharacters();
result.sign = false;
if( *pstring == '-' || *pstring == '+' )
{
if( *pstring == '-' )
result.sign = true;
++pstring;
return true;
}
return false;
}
/*!
we're reading the name of a variable or a function
if is there a function we'll return 'true'
*/
bool ReadVariableOrFunction(Item & result)
{
std::string name;
bool is_it_name_of_function = ReadName(name);
if( is_it_name_of_function )
{
/*
we've read the name of a function
*/
result.function_name = name;
result.type = Item::first_bracket;
result.function = true;
}
else
{
/*
we've read the name of a variable and we're getting its value now
*/
result.value = GetValueOfVariable( name );
}
return is_it_name_of_function;
}
/*!
we're reading a numerical value directly from the string
*/
void ReadValue(Item & result, int reading_base)
{
const char * new_stack_pointer;
bool value_read;
Conv conv;
conv.base = reading_base;
conv.comma = comma;
conv.comma2 = comma2;
conv.group = group;
uint carry = result.value.FromString(pstring, conv, &new_stack_pointer, &value_read);
pstring = new_stack_pointer;
if( carry )
Error( err_overflow );
if( !value_read )
Error( err_unknown_character );
}
/*!
this method returns true if 'character' is a proper first digit for the value (or a comma -- can be first too)
*/
bool ValueStarts(int character, int character_base)
{
if( character == comma )
return true;
if( comma2!=0 && character==comma2 )
return true;
if( Misc::CharToDigit(character, character_base) != -1 )
return true;
return false;
}
/*!
we're reading the item
return values:
0 - all ok, the item is successfully read
1 - the end of the string (the item is not read)
2 - the final bracket ')'
*/
int ReadValueVariableOrFunction(Item & result)
{
bool it_was_sign = false;
int character;
if( TestSign(result) )
// 'result.sign' was set as well
it_was_sign = true;
SkipWhiteCharacters();
character = ToLowerCase( *pstring );
if( character == 0 )
{
if( it_was_sign )
// at the end of the string a character like '-' or '+' has left
Error( err_unexpected_end );
// there's the end of the string here
return 1;
}
else
if( character == '(' )
{
// we've got a normal bracket (not a function)
result.type = Item::first_bracket;
result.function = false;
++pstring;
return 0;
}
else
if( character == ')' )
{
// we've got a final bracket
// (in this place we can find a final bracket only when there are empty brackets
// without any values inside or with a sign '-' or '+' inside)
if( it_was_sign )
Error( err_unexpected_final_bracket );
result.type = Item::last_bracket;
// we don't increment 'pstring', this final bracket will be read next by the
// 'ReadOperatorAndCheckFinalBracket(...)' method
return 2;
}
else
if( character == '#' )
{
++pstring;
SkipWhiteCharacters();
// after '#' character we do not allow '-' or '+' (can be white characters)
if( ValueStarts(*pstring, 16) )
ReadValue( result, 16 );
else
Error( err_unknown_character );
}
else
if( character == '&' )
{
++pstring;
SkipWhiteCharacters();
// after '&' character we do not allow '-' or '+' (can be white characters)
if( ValueStarts(*pstring, 2) )
ReadValue( result, 2 );
else
Error( err_unknown_character );
}
else
if( ValueStarts(character, base) )
{
ReadValue( result, base );
}
else
if( character>='a' && character<='z' )
{
if( ReadVariableOrFunction(result) )
// we've read the name of a function
return 0;
}
else
Error( err_unknown_character );
/*
we've got a value in the 'result'
this value is from a variable or directly from the string
*/
result.type = Item::numerical_value;
if( result.sign )
{
result.value.ChangeSign();
result.sign = false;
}
return 0;
}
void InsertOperatorToTable(const char * name, typename MatOperator::Type type)
{
operators_table.insert( std::make_pair(std::string(name), type) );
}
/*!
this method creates the table of operators
*/
void CreateMathematicalOperatorsTable()
{
InsertOperatorToTable("||", MatOperator::lor);
InsertOperatorToTable("&&", MatOperator::land);
InsertOperatorToTable("!=", MatOperator::neq);
InsertOperatorToTable("==", MatOperator::eq);
InsertOperatorToTable(">=", MatOperator::get);
InsertOperatorToTable("<=", MatOperator::let);
InsertOperatorToTable(">", MatOperator::gt);
InsertOperatorToTable("<", MatOperator::lt);
InsertOperatorToTable("-", MatOperator::sub);
InsertOperatorToTable("+", MatOperator::add);
InsertOperatorToTable("/", MatOperator::div);
InsertOperatorToTable("*", MatOperator::mul);
InsertOperatorToTable("^", MatOperator::pow);
}
/*!
returns true if 'str2' is the substring of str1
e.g.
true when str1="test" and str2="te"
*/
bool IsSubstring(const std::string & str1, const std::string & str2)
{
if( str2.length() > str1.length() )
return false;
for(typename std::string::size_type i=0 ; i<str2.length() ; ++i)
if( str1[i] != str2[i] )
return false;
return true;
}
/*!
this method reads a mathematical (or logical) operator
*/
void ReadMathematicalOperator(Item & result)
{
std::string oper;
typename OperatorsTable::iterator iter_old, iter_new;
iter_old = operators_table.end();
for( ; true ; ++pstring )
{
oper += *pstring;
iter_new = operators_table.lower_bound(oper);
if( iter_new == operators_table.end() || !IsSubstring(iter_new->first, oper) )
{
oper.erase(oper.begin() + oper.size() - 1); // we've got mininum one element
if( iter_old != operators_table.end() && iter_old->first == oper )
{
result.type = Item::mat_operator;
result.moperator.SetType( iter_old->second );
break;
}
Error( err_unknown_operator );
}
iter_old = iter_new;
}
}
/*!
this method makes a calculation for the percentage operator
e.g.
1000-50% = 1000-(1000*0,5) = 500
*/
void OperatorPercentage()
{
if( stack_index < 3 ||
stack[stack_index-1].type != Item::numerical_value ||
stack[stack_index-2].type != Item::mat_operator ||
stack[stack_index-3].type != Item::numerical_value )
Error(err_percent_from);
++pstring;
SkipWhiteCharacters();
uint c = 0;
c += stack[stack_index-1].value.Div(100);
c += stack[stack_index-1].value.Mul(stack[stack_index-3].value);
if( c )
Error(err_overflow);
}
/*!
this method reads a mathematic operators
or the final bracket or the semicolon operator
return values:
0 - ok
1 - the string is finished
*/
int ReadOperator(Item & result)
{
SkipWhiteCharacters();
if( *pstring == '%' )
OperatorPercentage();
if( *pstring == 0 )
return 1;
else
if( *pstring == ')' )
{
result.type = Item::last_bracket;
++pstring;
}
else
if( *pstring == ';' || (param_sep!=0 && *pstring==param_sep) )
{
result.type = Item::semicolon;
++pstring;
}
else
if( (*pstring>='a' && *pstring<='z') || (*pstring>='A' && *pstring<='Z') )
{
// short mul (without any operators)
result.type = Item::mat_operator;
result.moperator.SetType( MatOperator::shortmul );
}
else
ReadMathematicalOperator(result);
return 0;
}
/*!
this method is making the standard mathematic operation like '-' '+' '*' '/' and '^'
the operation is made between 'value1' and 'value2'
the result of this operation is stored in the 'value1'
*/
void MakeStandardMathematicOperation(ValueType & value1, typename MatOperator::Type mat_operator,
const ValueType & value2)
{
uint res;
calculated = true;
switch( mat_operator )
{
case MatOperator::land:
(!value1.IsZero() && !value2.IsZero()) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::lor:
(!value1.IsZero() || !value2.IsZero()) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::eq:
(value1 == value2) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::neq:
(value1 != value2) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::lt:
(value1 < value2) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::gt:
(value1 > value2) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::let:
(value1 <= value2) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::get:
(value1 >= value2) ? value1.SetOne() : value1.SetZero();
break;
case MatOperator::sub:
if( value1.Sub(value2) ) Error( err_overflow );
break;
case MatOperator::add:
if( value1.Add(value2) ) Error( err_overflow );
break;
case MatOperator::mul:
case MatOperator::shortmul:
if( value1.Mul(value2) ) Error( err_overflow );
break;
case MatOperator::div:
if( value2.IsZero() ) Error( err_division_by_zero );
if( value1.Div(value2) ) Error( err_overflow );
break;
case MatOperator::pow:
res = value1.Pow( value2 );
if( res == 1 ) Error( err_overflow );
else
if( res == 2 ) Error( err_improper_argument );
break;
default:
/*
on the stack left an unknown operator but we had to recognize its before
that means there's an error in our algorithm
*/
Error( err_internal_error );
}
}
/*!
this method is trying to roll the stack up with the operator's priority
for example if there are:
"1 - 2 +"
we can subtract "1-2" and the result store on the place where is '1' and copy the last
operator '+', that means there'll be '-1+' on our stack
but if there are:
"1 - 2 *"
we can't roll the stack up because the operator '*' has greater priority than '-'
*/
void TryRollingUpStackWithOperatorPriority()
{
while( stack_index>=4 &&
stack[stack_index-4].type == Item::numerical_value &&
stack[stack_index-3].type == Item::mat_operator &&
stack[stack_index-2].type == Item::numerical_value &&
stack[stack_index-1].type == Item::mat_operator &&
(
(
// the first operator has greater priority
stack[stack_index-3].moperator.GetPriority() > stack[stack_index-1].moperator.GetPriority()
) ||
(
// or both operators have the same priority and the first operator is not right associative
stack[stack_index-3].moperator.GetPriority() == stack[stack_index-1].moperator.GetPriority() &&
stack[stack_index-3].moperator.GetAssoc() == MatOperator::non_right
)
)
)
{
MakeStandardMathematicOperation(stack[stack_index-4].value,
stack[stack_index-3].moperator.GetType(),
stack[stack_index-2].value);
/*
copying the last operator and setting the stack pointer to the correct value
*/
stack[stack_index-3] = stack[stack_index-1];
stack_index -= 2;
}
}
/*!
this method is trying to roll the stack up without testing any operators
for example if there are:
"1 - 2"
there'll be "-1" on our stack
*/
void TryRollingUpStack()
{
while( stack_index >= 3 &&
stack[stack_index-3].type == Item::numerical_value &&
stack[stack_index-2].type == Item::mat_operator &&
stack[stack_index-1].type == Item::numerical_value )
{
MakeStandardMathematicOperation( stack[stack_index-3].value,
stack[stack_index-2].moperator.GetType(),
stack[stack_index-1].value );
stack_index -= 2;
}
}
/*!
this method is reading a value or a variable or a function
(the normal first bracket as well) and push it into the stack
*/
int ReadValueVariableOrFunctionAndPushItIntoStack(Item & temp)
{
int code = ReadValueVariableOrFunction( temp );
if( code == 0 )
{
if( stack_index < stack.size() )
stack[stack_index] = temp;
else
stack.push_back( temp );
++stack_index;
}
if( code == 2 )
// there was a final bracket, we didn't push it into the stack
// (it'll be read by the 'ReadOperatorAndCheckFinalBracket' method next)
code = 0;
return code;
}
/*!
this method calculate how many parameters there are on the stack
and the index of the first parameter
if there aren't any parameters on the stack this method returns
'size' equals zero and 'index' pointing after the first bracket
(on non-existend element)
*/
void HowManyParameters(int & size, int & index)
{
size = 0;
index = stack_index;
if( index == 0 )
// we haven't put a first bracket on the stack
Error( err_unexpected_final_bracket );
if( stack[index-1].type == Item::first_bracket )
// empty brackets
return;
for( --index ; index>=1 ; index-=2 )
{
if( stack[index].type != Item::numerical_value )
{
/*
this element must be 'numerical_value', if not that means
there's an error in our algorithm
*/
Error( err_internal_error );
}
++size;
if( stack[index-1].type != Item::semicolon )
break;
}
if( index<1 || stack[index-1].type != Item::first_bracket )
{
/*
we haven't put a first bracket on the stack
*/
Error( err_unexpected_final_bracket );
}
}
/*!
this method is being called when the final bracket ')' is being found
this method's rolling the stack up, counting how many parameters there are
on the stack and if there was a function it's calling the function
*/
void RollingUpFinalBracket()
{
int amount_of_parameters;
int index;
if( stack_index<1 ||
(stack[stack_index-1].type != Item::numerical_value &&
stack[stack_index-1].type != Item::first_bracket)
)
Error( err_unexpected_final_bracket );
TryRollingUpStack();
HowManyParameters(amount_of_parameters, index);
// 'index' will be greater than zero
// 'amount_of_parameters' can be zero
if( amount_of_parameters==0 && !stack[index-1].function )
Error( err_unexpected_final_bracket );
bool was_sign = stack[index-1].sign;
if( stack[index-1].function )
{
// the result of a function will be on 'stack[index-1]'
// and then at the end we'll set the correct type (numerical value) of this element
CallFunction(stack[index-1].function_name, amount_of_parameters, index);
}
else
{
/*
there was a normal bracket (not a funcion)
*/
if( amount_of_parameters != 1 )
Error( err_unexpected_semicolon_operator );
/*
in the place where is the bracket we put the result
*/
stack[index-1] = stack[index];
}
/*
if there was a '-' character before the first bracket
we change the sign of the expression
*/
stack[index-1].sign = false;
if( was_sign )
stack[index-1].value.ChangeSign();
stack[index-1].type = Item::numerical_value;
/*
the pointer of the stack will be pointing on the next (non-existing now) element
*/
stack_index = index;
}
/*!
this method is putting the operator on the stack
*/
void PushOperatorIntoStack(Item & temp)
{
if( stack_index < stack.size() )
stack[stack_index] = temp;
else
stack.push_back( temp );
++stack_index;
}
/*!
this method is reading a operator and if it's a final bracket
it's calling RollingUpFinalBracket() and reading a operator again
*/
int ReadOperatorAndCheckFinalBracket(Item & temp)
{
do
{
if( ReadOperator(temp) == 1 )
{
/*
the string is finished
*/
return 1;
}
if( temp.type == Item::last_bracket )
RollingUpFinalBracket();
}
while( temp.type == Item::last_bracket );
return 0;
}
/*!
we check wheter there are only numerical value's or 'semicolon' operators on the stack
*/
void CheckIntegrityOfStack()
{
for(unsigned int i=0 ; i<stack_index; ++i)
{
if( stack[i].type != Item::numerical_value &&
stack[i].type != Item::semicolon)
{
/*
on the stack we must only have 'numerical_value' or 'semicolon' operator
if there is something another that means
we probably didn't close any of the 'first' bracket
*/
Error( err_stack_not_clear );
}
}
}
/*!
the main loop of parsing
*/
void Parse()
{
Item item;
int result_code;
while( true )
{
if( pstop_calculating && pstop_calculating->WasStopSignal() )
Error( err_interrupt );
result_code = ReadValueVariableOrFunctionAndPushItIntoStack( item );
if( result_code == 0 )
{
if( item.type == Item::first_bracket )
continue;
result_code = ReadOperatorAndCheckFinalBracket( item );
}
if( result_code==1 || item.type==Item::semicolon )
{
/*
the string is finished or the 'semicolon' operator has appeared
*/
if( stack_index == 0 )
Error( err_nothing_has_read );
TryRollingUpStack();
if( result_code == 1 )
{
CheckIntegrityOfStack();
return;
}
}
PushOperatorIntoStack( item );
TryRollingUpStackWithOperatorPriority();
}
}
/*!
this method is called at the end of the parsing process
on our stack we can have another value than 'numerical_values' for example
when someone use the operator ';' in the global scope or there was an error during
parsing and the parser hasn't finished its job
if there was an error the stack is cleaned up now
otherwise we resize stack and leave on it only 'numerical_value' items
*/
void NormalizeStack()
{
if( error!=err_ok || stack_index==0 )
{
stack.clear();
return;
}
/*
'stack_index' tell us how many elements there are on the stack,
we must resize the stack now because 'stack_index' is using only for parsing
and stack has more (or equal) elements than value of 'stack_index'
*/
stack.resize( stack_index );
for(uint i=stack_index-1 ; i!=uint(-1) ; --i)
{
if( stack[i].type != Item::numerical_value )
stack.erase( stack.begin() + i );
}
}
public:
/*!
the default constructor
*/
Parser(): default_stack_size(100)
{
pstop_calculating = 0;
puser_variables = 0;
puser_functions = 0;
pfunction_local_variables = 0;
base = 10;
deg_rad_grad = 1;
error = err_ok;
group = 0;
comma = '.';
comma2 = ',';
param_sep = 0;
CreateFunctionsTable();
CreateVariablesTable();
CreateMathematicalOperatorsTable();
}
/*!
the assignment operator
*/
Parser<ValueType> & operator=(const Parser<ValueType> & p)
{
pstop_calculating = p.pstop_calculating;
puser_variables = p.puser_variables;
puser_functions = p.puser_functions;
pfunction_local_variables = 0;
base = p.base;
deg_rad_grad = p.deg_rad_grad;
error = p.error;
group = p.group;
comma = p.comma;
comma2 = p.comma2;
param_sep = p.param_sep;
/*
we don't have to call 'CreateFunctionsTable()' etc.
we can only copy these tables
*/
functions_table = p.functions_table;
variables_table = p.variables_table;
operators_table = p.operators_table;
visited_variables = p.visited_variables;
visited_functions = p.visited_functions;
return *this;
}
/*!
the copying constructor
*/
Parser(const Parser<ValueType> & p): default_stack_size(p.default_stack_size)
{
operator=(p);
}
/*!
the new base of mathematic system
default is 10
*/
void SetBase(int b)
{
if( b>=2 && b<=16 )
base = b;
}
/*!
the unit of angles used in: sin,cos,tan,cot,asin,acos,atan,acot
0 - deg
1 - rad (default)
2 - grad
*/
void SetDegRadGrad(int angle)
{
if( angle >= 0 && angle <= 2 )
deg_rad_grad = angle;
}
/*!
this method sets a pointer to the object which tell us whether we should stop
calculations
*/
void SetStopObject(const volatile StopCalculating * ps)
{
pstop_calculating = ps;
}
/*!
this method sets the new table of user-defined variables
if you don't want any other variables just put zero value into the 'puser_variables' variable
(you can have only one table at the same time)
*/
void SetVariables(const Objects * pv)
{
puser_variables = pv;
}
/*!
this method sets the new table of user-defined functions
if you don't want any other functions just put zero value into the 'puser_functions' variable
(you can have only one table at the same time)
*/
void SetFunctions(const Objects * pf)
{
puser_functions = pf;
}
/*!
setting the group character
default zero (not used)
*/
void SetGroup(int g)
{
group = g;
}
/*!
setting the main comma operator and the additional comma operator
the additional operator can be zero (which means it is not used)
default are: '.' and ','
*/
void SetComma(int c, int c2 = 0)
{
comma = c;
comma2 = c2;
}
/*!
setting an additional character which is used as a parameters separator
the main parameters separator is a semicolon (is used always)
this character is used also as a global separator
*/
void SetParamSep(int s)
{
param_sep = s;
}
/*!
the main method using for parsing string
*/
ErrorCode Parse(const char * str)
{
stack_index = 0;
pstring = str;
error = err_ok;
calculated = false;
stack.resize( default_stack_size );
try
{
Parse();
}
catch(ErrorCode c)
{
error = c;
calculated = false;
}
NormalizeStack();
return error;
}
/*!
the main method using for parsing string
*/
ErrorCode Parse(const std::string & str)
{
return Parse(str.c_str());
}
#ifndef TTMATH_DONT_USE_WCHAR
/*!
the main method using for parsing string
*/
ErrorCode Parse(const wchar_t * str)
{
Misc::AssignString(wide_to_ansi, str);
return Parse(wide_to_ansi.c_str());
// !! wide_to_ansi clearing can be added here
}
/*!
the main method using for parsing string
*/
ErrorCode Parse(const std::wstring & str)
{
return Parse(str.c_str());
}
#endif
/*!
this method returns true is something was calculated
(at least one mathematical operator was used or a function or variable)
e.g. true if the string to Parse() looked like this:
"1+1"
"2*3"
"sin(5)"
if the string was e.g. "678" the result is false
*/
bool Calculated()
{
return calculated;
}
/*!
initializing coefficients used when calculating the gamma (or factorial) function
this speed up the next calculations
you don't have to call this method explicitly
these coefficients will be calculated when needed
*/
void InitCGamma()
{
cgamma.InitAll();
}
};
} // namespace
#endif