2778 lines
58 KiB
C++
2778 lines
58 KiB
C++
/*
|
|
* This file is a part of TTMath Bignum Library
|
|
* and is distributed under the (new) BSD licence.
|
|
* Author: Tomasz Sowa <t.sowa@ttmath.org>
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2006-2010, 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, ¶m) != 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 base)
|
|
{
|
|
if( character == comma )
|
|
return true;
|
|
|
|
if( comma2!=0 && character==comma2 )
|
|
return true;
|
|
|
|
if( Misc::CharToDigit(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.end() ); // 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
|