Description
This library contains two C++ classes. One that is the interface to the algebraic expression evaluator and one that adds mathematical functions. The function are in the form of C/C++ function. The function class uses the expression class as a base and simply extending it. The expressions are standard C/C++ including the precedence. The mathematical functions included are from C++11.
It has been tested with the GNU C++ compiler as well as the LLVM clang++ compiler. It uses the RE/Flex lexer which requires there library. It have been tested on FreeBSD, the Raspberry Pi © and Linux Debian. GNU Automake was used to create the package so you should be able to use it on most Linux and BSD systems. I do not have access to a Windows system so do not know if it will compiled on Windows. If someone wants to test it on Windows and send me any changes required to get it to work I will try to include them or least put the information here on the site. Note that some versions of the GNU C++ compiler will issue warnings about dynamic exceptions. There has been constant changes to the C++ standard concerning exceptions and was not able to find a consistent way to specify them.
General Functional Description
The standard algebraic form also known as the infix form is parsed and converted to the postfix form sometimes referred to as reverse Polish notation. It is then categorized and stored on an execution stack. This stack is then evaluated. The primary function of this conversion form infix to postfix is to reorganize the parentheses making evaluation much simpler.Doxygen Document
Installation Instructions
- First you will need to install RE/Flex. You will find installation instructions there.
- Then download the tar file.
- Then un-tar it in your home directory.
- Change directory to algebraicexpr.
- Then execute the configure script.
- Then make.
$ tar -xf algebraicexpr.tar $ cd algebraicexpr $ ./configure $ make $ sudo make install
Note: Currently on FreeBSD with RE/Flex 3.3.5 the GNU g++ compiler causes the RE/Flex library to seg fault. To force the configure to use the LLVM clang++ compiler do this when configuring. This is not a problem if the GNU g++ compiler has not been install.
$ ./configure CXX=/usr/bin/clang++
At this point if all has gone well you should have a copy of the library libalgebraicexpr.a in /usr/local/lib and copies of the header files algebraicexpr.h and algebraicfun.h in /usr/local/include. Note that this include directory may not be in your include path and may need to be added. For most compiles the -I /usr/local/include will work or setting the CPLUS_INCLUDE_PATH environment variable to include /usr/local/include. Also the library is installed in /usr/local/lib. So you may need to use the -L /usr/local/lib linker option or again set the LIBRARY_PATH environment variable to include /usr/local/lib.
Now you can try it out with the test program math.
$ cd test $ ./build
Then try it. (Red text are just comments.)
$ ./math x = 4 * 5; y = 8/2; x = x + y << You enter this followed by return. [x = 4 * 5] << If you place a semicolon at the end RESULT> 20 << math will terminate afterwards. [ y = 8/2] RESULT> 4 [ x = x + y] RESULT> 24 ? << You enter this followed by return. [?] Enter the character followed by Return . Quit ? Help (This) = Variable List ! Toggle debug mode
Usage
For the best example of how to use the library look at math.cpp. Too use the library you need to include either algebraicexpr.h or algebraicfun.h. And then link in libalgebraicexpr.a. For an example of this look at the build script in the subdirectory test. It's also below among the source files. There is also full description of class interface here.Source
- algebraicexpr.h
- algebraicexpr.cpp
- algebraicfun.h
- algebraicfun.cpp
- token.h
- token.cpp
- token.l
- lex.tab
- mklexer
- math.cpp
- build
Header
This class evaluates mathematical expression written in algebraic form. It can return the value calculated or if turned on, assign the value to and allocate variable. The syntax is that of standard C.
/////////////////////////////////////////////////////////////////////////////////////
///
/// algebraicexpr.h
///
/// Acme Software Works, Inc.
///
/// Created January 27, 2004 by Don Dugger
///
/// <PRE>
/// Copyright 2019 Acme Software Works, Inc.
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
/// this list of conditions and the following disclaimer.
///
/// 2. 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.
///
/// 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 HOLDER 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.
/// </PRE>
/////////////////////////////////////////////////////////////////////////////////////
#ifndef _algebraicexpr_h_
#define _algebraicexpr_h_
///////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <errno.h>
#include <string.h>
#include <string>
#include <vector>
#include <map>
///////////////////////////////////////////////////////////////
#include "token_enum.h"
#include "token.h"
///////////////////////////////////////////////////////////////
using namespace std;
///////////////////////////////////////////////////////////////
/// Classes
///
/// This class evaluates mathematical expression written
/// in algebraic form. It can return the value calculated
/// or if turned on, assign the value to and allocate variable.
/// The syntax is that of standard C.
///\n
/// The standard algebraic form also known as the infix form
/// is parsed and converted to the postfix form sometimes
/// referred to as reverse Polish notion. It is then categorized
/// and stored on an execution stack. This stack is then evaluated.
/// The primary function of this conversion form infix to postfix
/// is to reorganize the parentheses making evaluation much simpler.
///\n
/// @brief This is a algebraic expression evaluator.
////
class AlgebraicExpr {
public:
/// The two major categories of tokens.
enum TOKEN_TYPE { OPERATOR,OPERAND };
/// The categories of operators, note operand is a single category.
enum TOKEN_CATEGORY {
NO_CAT = 0,
ASSIGN_CAT = 1,
BIT_CAT = 2,
COMMENT_CAT = 3,
COMPARE_CAT = 4,
FILE_CAT = 5,
FUNCTION_CAT = 6,
MATH_CAT = 7,
OPERAND_CAT = 8,
PAREN_CAT = 9,
PUCH_CAT = 10,
SPECUNARY_CAT = 11,
TYPE_CAT = 12,
UNARY_CAT = 13
};
/// The storage class is used primarily for syntax checking.
enum STORAGE_CLASS {
VARIABLE_CLASS = 0,
CONSTANT_CLASS = 1,
OPERATOR_CLASS = 2
};
private:
/// The lexer
/// This is the lexer produced by RE/Flex
Lexer lexer;
/// The size of the token.
int size;
/// The variable list. It is either allocated or pass in.
map<string,double>* var_list;
/// The flag that indicates that the builtin variable list was allocated and will need to be freed.
bool var_list_allocated;
/// The flag that indicates that allocating variable is to be allowed.
/// This allow for situations that require variables to be preallocated.
bool allow_allocation;
/// This is a table that contains the precedence of the operators.
/// This table is generated by the script mklexer.ksh using the file lex.tab.
static int token_prec[];
/// This is a table that contains the categories of the operators.
/// This table is generated by the script mklexer.ksh using the file lex.tab.
static TOKEN_CATEGORY token_cat[];
protected:
/// The debug flag it currently is on off however
/// we may want to have debug levels in the future.
int debug_flag;
/// This is a index to the current token the input expression.
int cur;
/// The expression being evaluated
string expr;
/// This is a class that creates an object that defines an expression element.
/// These objects are what is stored on the execution stack.
/// @brief For storage of an expression element.
class ExprElement {
public:
ExprElement() {
token_str = "";
token = NOP_TOK;
value = 0;
};
TOKEN token;
STORAGE_CLASS storage_class;
double value;
string token_str;
};
/// Interface to the precedence table.
int tokenPrecedence(int op) { return(token_prec[op]); };
/// Converts single char forms ('x') to an eight bit value.
/// It uses the library function stol() and a table of
/// the standard escape value ie '0xff' or '\\b'. This, if
/// functioning correctly should duplicate the C language.
/// @param s A point to the string.
/// @return The binary value.
char string2Byte(string s);
/// This evaluates a list of expression elements (ExprElement)
/// in postfix order.
/// @param post The postfix element list.
/// @return The evaluated value.
double evalPostfix(vector<ExprElement*>& post);
/// This is the method that calls the lexer. Which determines the current token and returns it
/// as a string and notes it's size.
/// @param expr The full expression.
/// @param token A string to store the current token.
/// @param size A int to store the size of the token.
/// @return The token's enumerator.
TOKEN processToken(const string& expr,string& token_str,int& size);
///////////////////////////////////////////////////////////////////////////////////////////////
/// The virtual methods which will be defined by the math function derived class
/// If the math function derived class is not present we throw errors.
///
virtual int parseFun(string n,int paren) {
throw ExprError("Syntax error (3)",&expr[cur]);
return(0); };
///
virtual void commaFun(int paren)
{ throw ExprError("Syntax error (4)",&expr[cur]); };
///
virtual bool closeParenFun(int paren) { return(false); };
///
virtual void exeFun(string n,vector<ExprElement*>& stack)
{ throw ExprError("Syntax error (5)",&expr[cur]); };
///
///////////////////////////////////////////////////////////////////////////////////////////////
public:
/// This constructor is used to create an AlgebraicExpr object
/// that when used in the null form will not allow variable allocation
/// and will allocate an internal variable list. It can optionally
/// allow variable allocation when it is invoked with argument "true".
/// When variable allocation is false all variable must be allocated
/// with the method <em>AddVar()</em>.
/// @param allow The optional variable allocation flag.
/// @brief The null constructor.
AlgebraicExpr(bool allow = false) {
var_list = NULL;
var_list_allocated = false;
allow_allocation = allow;
debug_flag = 0;
};
/// This constructor is used to create an AlgebraicExpr object
/// that is given a variable list and will not allocate an internal
/// variable list. It can optionally allow variable allocation when
/// it is invoked with a second argument "true".
/// When variable allocation is false variable must be allocated
/// with the method <em>AddVar()</em> or added to the provided list.
/// @param var The external variable list.
/// @param allow The optional variable allocation flag.
/// @brief The constructor with the external variable list.
AlgebraicExpr(map<string,double>& var,bool allow = true) {
var_list = &var;
var_list_allocated = false;
allow_allocation = allow;
debug_flag = 0;
};
/// The destructor that must, if allocated, free the internal variable list.
~AlgebraicExpr() { if(var_list_allocated) delete var_list; };
// The Public methods.
/// This is the exception class.
class ExprError {
public:
string text;
string loc;
ExprError(const string text,const string loc) {
this->text = text;
this->loc = loc;
};
};
/// Change the debug flag
void debug(int d) { debug_flag = d; };
int debug() { return(debug_flag); };
/// Prints to stdout the text name of the token, used for debugging only.
void printTok(int token);
/// If you choose to let AlgebraicExpr allocate variables these
/// methods are how you access them. They will also work if you
/// supply the variable list however you would have direct access.
/// Note that the methods are virtual so they can be overridden by
/// derived classes.
///
/// Retrieve the value of a given variable.
/// @param n The variable name.
/// @return The value of the variable.
virtual double getVar(string n) { return((*var_list)[n]); };
/// Add a variable to the variable list. If no value is given
/// set it to zero.
/// @param n The variable name.
/// @param val The initial value.
/// @return If variable does not exist return true if it does false.
virtual bool addVar(string n, double val = 0.0 ) {
if ( var_list->find(n) == var_list->end() ) {
(*var_list)[n] = val;
return true;
} else {
return false;
}
};
/// Set the value of a variable to the given value.
/// @param n The variable name.
/// @param val The new value.
/// @return If variable does exist return true if it does not false.
virtual bool setVar(string n,double val) {
if ( var_list->find(n) != var_list->end() ) {
(*var_list)[n] = val;
return true;
} else {
return false;
}
};
/// The interface to the variable allocation flag.
/// @parma allow The allow allocation flag.
void variableAllocation(bool allow = true) { allow_allocation = allow; };
/// The interface to the variable allocation flag turning it on or off with the ability
/// to set the variable list.
/// @parma var The vaiable list.
void variableAllocation(map<string,double>& var, bool allow = true) {
if(var_list_allocated) delete var_list;
var_list = &var;
allow_allocation = allow;
};
/// This is the core method that takes an algebraic expression and returns the result
/// as well as records the values of variable that were set. There are two variations
/// of this method one which simply takes an expression and returns the resulting value.
/// And one that is also given a variable list.
/// @param ex The expression.
/// @return The resulting value.
double solve(string ex);
/// @param ex The expression.
/// @param var The variable list.
/// @return The resulting value.
double solve(string ex,map<string,double>& var) {
var_list = &var;
return(solve(ex));
};
};
///////////////////////////////////////////////////////////////
#endif // _algebraicexpr_h_
Implementation
/////////////////////////////////////////////////////////////////////////////////////
///
/// algebraicexpr.cpp
///
/// Acme Software Works, Inc.
///
/// Created January 27, 2004 by Don Dugger
///
/// <PRE>
/// Copyright 2019 Acme Software Works, Inc.
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
/// this list of conditions and the following disclaimer.
///
/// 2. 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.
///
/// 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 HOLDER 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.
/// </PRE>
/////////////////////////////////////////////////////////////////////////////////////
#include "algebraicexpr.h"
///////////////////////////////////////////////////////////////
int AlgebraicExpr::token_prec[] = {
#include "token_prec.cpp"
};
///////////////////////////////////////////////////////////////
AlgebraicExpr::TOKEN_CATEGORY AlgebraicExpr::token_cat[] = {
#include "token_cat.cpp"
};
///////////////////////////////////////////////////////////////
void AlgebraicExpr::printTok(int token)
{
#include "token_print.cpp"
} // End of printTok()
///////////////////////////////////////////////////////////////
double AlgebraicExpr::solve(string ex)
{
TOKEN token = (TOKEN)0;
int parens = 0;
bool done = false;
TOKEN_TYPE last_oper = OPERATOR;
ExprElement* oper;
vector<ExprElement*> opers;
vector<ExprElement*> post;
string token_str;
// Used to indicate unassigned variables
bool must_be_asignment = false;
// Used to tell the name of the non-existent
// variable in the error message
string token_str_added;
// If the user did not give us a
// variable list then we need to
// allocate one.
if(var_list == NULL) {
var_list = new map<string,double>;
var_list_allocated = true;
}
this->expr = ex;
cur = 0;
// Remove spaces
for (int i=0;i<expr.size();++i) {
if ( expr[i] == ' ' ) expr.erase(i,1);
}
if (debug_flag) cout << "PARSING> " << expr << '\n';
do {
if (debug_flag == 2 && token != 0 ) {
cout << endl << "--------post->token-----------" << endl;
vector<ExprElement*>::iterator pit = post.begin();
while(pit != post.end() ) {
ExprElement* tmp = *pit;
cout << tmp->value << " ";
printTok(tmp->token);
++pit;
}
cout << "--------opers->token-----------" << endl;
vector<ExprElement*>::iterator oit = opers.begin();
while(oit != opers.end() ) {
ExprElement* tmp = *oit;
cout << tmp->value << " ";
printTok(tmp->token);
++oit;
}
cout << "-----------END-----------------" << endl << endl;
}
// Get the next token
token = processToken(&expr[cur],token_str,size);
if (debug_flag) printTok(token);
// If the previous token was a new unassigned variable
// this token must be an "=" (ASSIGN_TOK)
if(must_be_asignment && (token != ASSIGN_TOK)) {
throw ExprError("No such variable",token_str_added);
} else { // alls well
must_be_asignment = false;
}
switch((TOKEN)token) {
case EOF_TOK:
done = true;
break;
// Tokens that are currently errors
case ERROR_TOK:
case STRING_CONST_TOK:
case BUILTIN_FUNCTION_TOK :
case CONTROL_FUNCTION_TOK :
case OPEN_BRACKET_TOK :
case SEMICOLON_TOK :
case ATSIGN_TOK: // This was used in raw2a below as a variable
throw ExprError("Syntax error (1)",&expr[cur]);
done = true;
break;
// Operands ////////////////////////////////////////////////////////////////////////////////////////
// Tokens which are constant values
case FLOAT_TOK:
case INTEGER_TOK:
oper = new ExprElement;
oper->token = token;
oper->storage_class = CONSTANT_CLASS;
last_oper = OPERAND;
oper->value = stod(token_str);
if ( oper->value == HUGE_VAL) throw ExprError("Value too large",&expr[cur]);
post.push_back(oper);
break;
case CHAR_TOK :
case CTRL_CHAR_TOK :
case OCT_CHAR_TOK :
case HEX_CHAR_TOK :
oper = new ExprElement;
oper->token = token;
oper->storage_class = CONSTANT_CLASS;
oper->value = (double)string2Byte(token_str);
last_oper = OPERAND;
post.push_back(oper);
break;
case OCTAL_TOK:
case HEX_TOK:
oper = new ExprElement;
oper->token = token;
oper->storage_class = CONSTANT_CLASS;
last_oper = OPERAND;
oper->value = stoll(token_str,NULL,0);
if ( oper->value == HUGE_VAL) throw ExprError("Value too large",&expr[cur]);
post.push_back(oper);
break;
case BIN_TOK:
oper = new ExprElement;
oper->token = token;
oper->storage_class = CONSTANT_CLASS;
last_oper = OPERAND;
oper->value = stoll(&token_str[2],NULL,2);
if ( oper->value == HUGE_VAL) throw ExprError("Value too large",&expr[cur]);
post.push_back(oper);
break;
// Variables
case VARIABLE_TOK:
case SHELL_VARIABLE_TOK:
// case ATSIGN_TOK: // This was used in raw2a
oper = new ExprElement;
oper->token = VARIABLE_TOK;
oper->storage_class = VARIABLE_CLASS;
last_oper = OPERAND;
// Check to see if this variable has been allocated
if ( var_list->find(token_str) == var_list->end() ) {
if ( ! allow_allocation ) throw ExprError("Unknown variable",token_str);
(*var_list)[token_str]=0;
must_be_asignment = true;
token_str_added = token_str;
} else { // It has
oper->value = var_list->at(token_str);
}
oper->token_str = token_str;
post.push_back(oper);
break;
// Determine if it's add or subtract or if it's designating a values polarity
case UNARY_MINUS_TOK:
case UNARY_PLUS_TOK:
if(last_oper == OPERAND) {
oper = new ExprElement;
if(token == UNARY_PLUS_TOK) oper->token = ADD_TOK;
else oper->token = SUBTRACT_TOK;
} else {
oper = new ExprElement;
oper->token = token;
}
oper->storage_class = OPERATOR_CLASS;
last_oper = OPERATOR;
while(!opers.empty() && (tokenPrecedence((int)oper->token) < tokenPrecedence((int)opers.back()->token))) {
post.push_back(opers.back());
opers.pop_back();
}
opers.push_back(oper);
break;
// Operator tokens ///////////////////////////////////////////////////////////////////////////////
// Increment and decrement
case POST_INC_TOK:
case POST_DEC_TOK:
if(last_oper == OPERAND) {
oper = new ExprElement;
oper->token = token;
} else {
oper = new ExprElement;
if(token == POST_INC_TOK) oper->token = PRE_INC_TOK;
else oper->token = PRE_DEC_TOK;
}
oper->storage_class = OPERATOR_CLASS;
last_oper = OPERATOR;
while(!opers.empty() && (tokenPrecedence((int)oper->token) < tokenPrecedence((int)opers.back()->token))) {
post.push_back(opers.back());
opers.pop_back();
}
opers.push_back(oper);
break;
// Parentheses
case OPEN_PAREN_TOK :
++parens;
oper = new ExprElement;
oper->token = token;
oper->storage_class = OPERATOR_CLASS;
last_oper = OPERATOR;
opers.push_back(oper);
break;
case CLOSE_PAREN_TOK:
if(!closeParenFun(parens)) {
if(--parens < 0) {
done = true;
break;
}
last_oper = OPERAND;
while(!opers.empty() && (opers.back()->token != OPEN_PAREN_TOK)) {
post.push_back(opers.back());
opers.pop_back();
}
if(!opers.empty() && opers.back()->token == OPEN_PAREN_TOK) {
delete opers.back();
opers.pop_back();
}
}
break;
// To separate function arguments.
case COMMA_TOK :
commaFun(parens);
break;
// Math functions
case FUNCTION_TOK :
if(!parseFun(token_str,parens)) {
// Unknown function
done = true;
break;
} // Now just drop through to the operator code
// Every thing else a math operators
default:
oper = new ExprElement;
oper->token = token;
oper->storage_class = OPERATOR_CLASS;
last_oper = OPERATOR;
oper->token_str = token_str;
while(!opers.empty() && (tokenPrecedence((int)token) < tokenPrecedence((int)opers.back()->token))) {
post.push_back(opers.back());
opers.pop_back();
}
opers.push_back(oper);
break;
}
if (debug_flag) cout << "[" << &expr[cur] << "] " << '\n';
if (expr[cur]) cur += size; // Next token
} while(size && !done);
// If this is not 0 we don't have a balance of open and close parentheses.
if(parens) throw ExprError("Syntax error"," Miss placed parentheses");
// Now move the what's left on the opers stack to the post stack
while(!opers.empty()) {
post.push_back(opers.back());
opers.pop_back();
}
if (debug_flag) {
cout << "--------post->token-----------" << endl;
vector<ExprElement*>::iterator pit = post.begin();
while(pit != post.end() ) {
ExprElement* tmp = *pit;
printTok(tmp->token);
++pit;
}
}
if(last_oper == OPERATOR && post.size() < 2) throw ExprError("Syntax error"," There must be a operand");
return evalPostfix(post);
} // End of solve()
///////////////////////////////////////////////////////////////
double AlgebraicExpr::evalPostfix(vector<ExprElement*>& post)
{
vector<ExprElement*> stack;
ExprElement* oper;
TOKEN_TYPE last_oper = OPERATOR;
if (debug_flag) cout << "-----calculate-----" << endl;
vector<ExprElement*>::iterator cur_post = post.begin();
if(post.end() != cur_post) do {
ExprElement* operand;
long long tmp_int = 0;
long long tmp_int1 = 0;
oper = *cur_post;
if(oper->storage_class == OPERATOR_CLASS) {
if (debug_flag) printTok(oper->token);
switch((TOKEN)oper->token) {
case POST_INC_TOK:
if(stack.back()->storage_class == VARIABLE_CLASS) (*var_list)[stack.back()->token_str] += 1;
else throw ExprError("Syntax error","++ Must follow a variable");
break;
case POST_DEC_TOK:
if(stack.back()->storage_class == VARIABLE_CLASS) (*var_list)[stack.back()->token_str] -= 1;
else throw ExprError("Syntax error","-- Must follow a variable");
break;
case PRE_INC_TOK :
if(stack.back()->storage_class == VARIABLE_CLASS) (*var_list)[stack.back()->token_str] += 1;
else throw ExprError("Syntax error","++ Must precede a variable");
stack.back()->value += 1;
break;
case PRE_DEC_TOK :
if(stack.back()->storage_class == VARIABLE_CLASS) (*var_list)[stack.back()->token_str] -= 1;
else throw ExprError("Syntax error","-- Must precede a variable");
stack.back()->value -= 1;
break;
case COMPLEMENT_TOK :
tmp_int = (long long)stack.back()->value;
stack.back()->value = (double)(~tmp_int);
break;
case UNARY_MINUS_TOK:
stack.back()->value = -(stack.back()->value);
break;
case UNARY_PLUS_TOK:
stack.back()->value = +(stack.back()->value);
break;
case ADD_TOK :
operand = stack.back();
stack.pop_back();
stack.back()->value += operand->value;
break;
case SUBTRACT_TOK :
operand = stack.back();
stack.pop_back();
stack.back()->value -= operand->value;
break;
case MULTIPLY_TOK :
operand = stack.back();
stack.pop_back();
stack.back()->value *= operand->value;
break;
case DIVIDE_TOK :
operand = stack.back();
stack.pop_back();
if(operand->value == 0) throw ExprError("Syntax error"," Dividing by zero");
stack.back()->value /= operand->value;
break;
case MODULO_TOK :
operand = stack.back();
stack.pop_back();
if(operand->value == 0) throw ExprError("Syntax error"," Dividing by zero");
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
stack.back()->value = (double)(tmp_int1 % tmp_int);
break;
case SHIFT_LEFT_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
stack.back()->value = (double)(tmp_int1 << tmp_int);
break;
case SHIFT_RIGHT_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
stack.back()->value = (double)(tmp_int1 >> tmp_int);
break;
case BIT_AND_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
stack.back()->value = (double)(tmp_int & tmp_int1);
break;
case BIT_XOR_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
stack.back()->value = (double)(tmp_int ^ tmp_int1);
break;
case BIT_OR_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
stack.back()->value = (double)(tmp_int | tmp_int1);
break;
case AND_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value && operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case OR_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value || operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case LESS_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value < operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case LESS_EQUAL_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value <= operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case GREATER_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value > operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case GREATER_EQUAL_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value >= operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case EQUAL_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value == operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case NOT_EQUAL_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->value != operand->value) stack.back()->value = 1;
else stack.back()->value = 0;
break;
case NOT_TOK :
if(stack.back()->value) stack.back()->value = 0;
else stack.back()->value = 1;
break;
case ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = operand->value;
else throw ExprError("Syntax error"," Only variables can be asigned values (=)");
stack.back()->value = operand->value;
break;
case ADD_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = operand->value + var_list->at(stack.back()->token_str);
else throw ExprError("Syntax error"," Only variables can be asigned values (+=)");
stack.back()->value += operand->value;
break;
case SUB_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = operand->value - var_list->at(stack.back()->token_str);
else throw ExprError("Syntax error"," Only variables can be asigned values (-=)");
stack.back()->value -= operand->value;
break;
case MUL_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = operand->value * var_list->at(stack.back()->token_str);
else throw ExprError("Syntax error"," Only variables can be asigned values (*=)");
stack.back()->value *= operand->value;
break;
case DIV_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
if(operand->value == 0) throw ExprError("Syntax error"," Dividing by zero");
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = operand->value / var_list->at(stack.back()->token_str);
else throw ExprError("Syntax error"," Only variables can be asigned values (/=)");
stack.back()->value /= operand->value;
break;
case MOD_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
if(operand->value == 0) throw ExprError("Syntax error"," Dividing by zero");
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = (double)(tmp_int1 % tmp_int);
else throw ExprError("Syntax error"," Only variables ca be asigned values (%=)");
stack.back()->value = (double)(tmp_int1 % tmp_int);
break;
case SH_LEFT_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = (double)(tmp_int1 << tmp_int);
else throw ExprError("Syntax error"," Only variables ca be asigned values (<<=)");
stack.back()->value = (double)(tmp_int1 << tmp_int);
break;
case SH_RIGHT_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = (double)(tmp_int1 >> tmp_int);
else throw ExprError("Syntax error"," Only variables ca be asigned values (>>=)");
stack.back()->value = (double)(tmp_int1 >> tmp_int);
break;
case AND_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = (double)(tmp_int1 & tmp_int);
else throw ExprError("Syntax error"," Only variables ca be asigned values (&=)");
stack.back()->value = (double)(tmp_int1 & tmp_int);
break;
case OR_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = (double)(tmp_int1 | tmp_int);
else throw ExprError("Syntax error"," Only variables ca be asigned values (|=)");
stack.back()->value = (double)(tmp_int1 | tmp_int);
break;
case XOR_ASSIGN_TOK :
operand = stack.back();
stack.pop_back();
tmp_int = (long long)operand->value;
tmp_int1 = (long long)stack.back()->value;
if(stack.back()->storage_class == VARIABLE_CLASS)
(*var_list)[stack.back()->token_str] = (double)(tmp_int1 ^ tmp_int);
else throw ExprError("Syntax error"," Only variables ca be asigned values (^=)");
stack.back()->value = (double)(tmp_int1 ^ tmp_int);
break;
case COLON_TOK :
++cur_post;
oper = *cur_post;
if(oper->token == QUESTION_TOK) {
ExprElement* false_op = stack.back();
stack.pop_back();
ExprElement* true_op = stack.back();
stack.pop_back();
operand = stack.back();
stack.pop_back();
if(operand->value) {
stack.push_back(true_op);
} else {
stack.push_back(false_op);
}
} else throw ExprError("Syntax error"," Miss placed colon");
break;
case QUESTION_TOK :
throw ExprError("Syntax error"," Miss placed question mark");
break;
case FUNCTION_TOK :
exeFun(oper->token_str,stack);
break;
default:
throw ExprError("Syntax error"," Unknown operator");
break;
}
last_oper = OPERATOR;
} else {
if (debug_flag) {
cout << oper->value;
if(oper->storage_class == VARIABLE_CLASS) cout << " [" << oper->token_str << "]" << endl;
else cout << endl;
}
last_oper = OPERAND;
stack.push_back(oper);
}
} while(++cur_post != post.end());
if (debug_flag) {
cout << "---------var_list----------" << endl;
for (auto const& x : *var_list) {
std::cout << x.first // variable name
<< ": \t"
<< x.second // variable value
<< endl ;
}
}
double return_value = stack.back()->value;
// Clean up the the postfix list
vector<ExprElement*>::iterator pit = post.begin();
while(pit != post.end() ) {
ExprElement* tmp = *pit;
delete tmp;
++pit;
}
if ( return_value == HUGE_VAL) throw ExprError("Value too large"," Final value");
return return_value;
} // End of evalPostfix()
///////////////////////////////////////////////////////////////
TOKEN AlgebraicExpr::processToken(const string& expr,string& token_str,int& size)
{
TOKEN token;
if(expr.size() == 0) return(EOF_TOK);
if(!(token = (TOKEN)lexer.lex(expr))) {
throw ExprError("Syntax error (2)",expr);
}
token_str = lexer.text();
if(token == SHELL_VARIABLE_TOK) {
token_str.erase(0,1); // Remove the '$'
}
size = lexer.size();
return(token);
} // End of processToken()
///////////////////////////////////////////////////////////////
char AlgebraicExpr::string2Byte(string s)
{
string p;
char byte = 0;
switch(s[0]) {
case 0 :
byte = 0;
break;
case '\'' :
if(s[1] != '\\' ) {
byte = s[1];
} else switch(s[2]) {
case 'a' : byte = '\a'; break;
case 'b' : byte = '\b'; break;
case 'f' : byte = '\f'; break;
case 'n' : byte = '\n'; break;
case 'r' : byte = '\r'; break;
case 't' : byte = '\t'; break;
case 'v' : byte = '\v'; break;
case '\'' : byte = '\''; break;
case '\"' : byte = '\"'; break;
case '\\' : byte = '\\'; break;
break;
default:
s[1] = '0';
string::size_type sz = 0;
long n;
n = stol(&s[1],&sz,0);
if(n > 0xff) throw ExprError("Too big for a Byte",s);
byte = (char)n;
}
break;
default:
string::size_type sz = 0;
long n;
n = stol(s,&sz,0);
if(n > 0xff) throw ExprError("Too big for a Byte",s);
byte = (char)n;
}
return(byte);
} // End of string2Byte()
///////////////////////////////////////////////////////////////
///////////////////// END OF FILE /////////////////////////////
///////////////////////////////////////////////////////////////
Header
/////////////////////////////////////////////////////////////////////////////////////
///
/// algebraicfun.h
///
/// Acme Software Works, Inc.
///
/// Created January 27, 2004 by Don Dugger
///
/// <PRE>
/// Copyright 2019 Acme Software Works, Inc.
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
/// this list of conditions and the following disclaimer.
///
/// 2. 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.
///
/// 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 HOLDER 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.
/// </PRE>
/////////////////////////////////////////////////////////////////////////////////////
#ifndef _algebraicfun_h_
#define _algebraicfun_h_
/////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include "algebraicexpr.h"
/////////////////////////////////////////////////////////////////////////////////////
/// Classes
class AlgebraicFun : public AlgebraicExpr {
/// Function enumerators
enum MATH_FUNCTION {
ABS,
ACOS,
ACOSH,
ASIN,
ASINH,
ATAN,
ATAN2,
ATANH,
CBRT,
CEIL,
COPYSIGN,
COS,
COSH,
ERF,
ERFC,
EXP,
EXP2,
EXPM1,
FDIM,
FLOOR,
FMA,
FMAX,
FMIN,
FMOD,
HYPOT,
LDEXP,
LGAMMA,
LOG,
LOG10,
LOG1P,
LOG2,
LOGB,
POW,
REMAINDER,
RINT,
ROUND,
SIGNBIT,
SIN,
SINH,
SQRT,
TAN,
TANH,
TGAMMA,
TRUNC,
XRT
};
/// Argument Element
/// A class for keeping track of parentheses and arguments
class ArgElement {
public:
ArgElement(int pl,int fa) { paren = pl; fun_argc = fa; argc = 0; };
int paren; // Parentheses depth
int argc; // Current argument count
int fun_argc; // This function's required number of arguments
};
/// The argument element stack
vector<ArgElement*> arg_stack;
/// For translating function tokens to enumerator
map<string,MATH_FUNCTION> fun_list {
{ "abs(", ABS },
{ "acos(", ACOS },
{ "acosh(", ACOSH },
{ "asin(", ASIN },
{ "asinh(", ASINH },
{ "atan(", ATAN },
{ "atan2(", ATAN2 },
{ "atanh(", ATANH },
{ "cbrt(", CBRT },
{ "ceil(", CEIL },
{ "copysign(", COPYSIGN },
{ "cos(", COS },
{ "cosh(", COSH },
{ "erf(", ERF },
{ "erfc(", ERFC },
{ "exp(", EXP },
{ "exp2(", EXP2 },
{ "expm1(", EXPM1 },
{ "fdim(", FDIM },
{ "floor(", FLOOR },
{ "fma(", FMA },
{ "fmax(", FMAX },
{ "fmin(", FMIN },
{ "fmod(", FMOD },
{ "hypot(", HYPOT },
{ "ldexp(", LDEXP },
{ "lgamma(", LGAMMA },
{ "log(", LOG },
{ "log10(", LOG10 },
{ "log1p(", LOG1P },
{ "log2(", LOG2 },
{ "logb(", LOGB },
{ "pow(", POW },
{ "remainder(", REMAINDER },
{ "rint(", RINT },
{ "round(", ROUND },
{ "signbit(", SIGNBIT },
{ "sin(", SIN },
{ "sinh(", SINH },
{ "sqrt(", SQRT },
{ "tan(", TAN },
{ "tanh(", TANH },
{ "tgamma(", TGAMMA },
{ "trunc(", TRUNC },
{ "xrt(", XRT }
};
/// The function's number of required arguments list
map<string,int> argc_list {
{ "abs(", 1 },
{ "acos(", 1 },
{ "acosh(", 1 },
{ "asin(", 1 },
{ "asinh(", 1 },
{ "atan(", 1 },
{ "atan2(", 2 },
{ "atanh(", 1 },
{ "cbrt(", 1 },
{ "ceil(", 1 },
{ "copysign(", 2 },
{ "cos(", 1 },
{ "cosh(", 1 },
{ "dgr(", 1 },
{ "erf(", 1 },
{ "erfc(", 1 },
{ "exp(", 1 },
{ "exp2(", 1 },
{ "expm1(", 1 },
{ "fdim(", 2 },
{ "floor(", 1 },
{ "fma(", 3 },
{ "fmax(", 2 },
{ "fmin(", 2 },
{ "fmod(", 2 },
{ "hypot(", 2 },
{ "ldexp(", 2 },
{ "lgamma(", 1 },
{ "log(", 1 },
{ "log10(", 1 },
{ "log1p(", 1 },
{ "log2(", 1 },
{ "logb(", 1 },
{ "pow(", 2 },
{ "rad(", 1 },
{ "remainder(", 2 },
{ "rint(", 1 },
{ "round(", 1 },
{ "signbit(", 1 },
{ "sin(", 1 },
{ "sinh(", 1 },
{ "sqrt(", 1 },
{ "tan(", 1 },
{ "tanh(", 1 },
{ "tgamma(", 1 },
{ "trunc(", 1 },
{ "xrt(", 2 }
};
public:
/// @note The constructors simply invoke the base class constructors
///
/// This constructor is used to create an AlgebraicFun object
/// that when used in the null form will not allow assignments
/// and will allocate an internal variable list. It can optionally
/// allow assignments when it is invoked with argument "true".
/// @param as The optional assignments flag.
/// @brief The null constructor.
AlgebraicFun(bool as = false) : AlgebraicExpr(as) {};
/// This constructor is used to create an AlgebraicFun object
/// that will use the variable list which it is given as an
/// argument and by default will allow assignments. However
/// if given a second argument of "false" will not allow assignments.
/// @param var The external variable list.
/// @param as The optional assignments flag.
/// @brief The constructor with the external variable list.
AlgebraicFun(map<string,double>& var,bool allow = true) : AlgebraicExpr(var,allow) {};
/// The destructor that must, if allocated, free the internal variable list.
~AlgebraicFun() {};
/// Create an ArgElemet with parentheses depth and the function's number
/// of required arguments. And place it in the argument list.
/// @param n Text of the function name.
/// @param paren The current parentheses depth
/// @return The functions required number of arguments or on error zero.
/// @brief Create an ArgElemet and place it on the argument list.
int parseFun(string n,int paren);
/// Check that the commas are correctly placed.
/// @param paren The parentheses depth.
/// @brief Check commas.
void commaFun(int paren);
/// Handle the closing parentheses. Check that there are the correct number
/// of arguments.
/// @paren The parentheses depth.
/// @return true if there are no errors and false otherwise.
/// @brief Handle the closing parentheses.
bool closeParenFun(int paren);
/// Execute the function and enter the return value in the expression
/// element on the top of the stack. If there are more then one argument
/// remove them from the stack.
/// @param n Text of the function name.
/// @param stack The expression element stack.
/// @brief Execute the function.
void exeFun(string n,vector<ExprElement*>& stack);
};
///////////////////////////////////////////////////////////////////////
#endif // _algebraicfun_h_
Implementation
/////////////////////////////////////////////////////////////////////////////////////
///
/// algebraicfun.cpp
///
/// Acme Software Works, Inc.
///
/// Created January 27, 2004 by Don Dugger
///
/// <PRE>
/// Copyright 2019 Acme Software Works, Inc.
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
/// this list of conditions and the following disclaimer.
///
/// 2. 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.
///
/// 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 HOLDER 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.
/// </PRE>
/////////////////////////////////////////////////////////////////////////////////////
#include "algebraicfun.h"
///////////////////////////////////////////////////////////////
int AlgebraicFun::parseFun(string n,int paren)
{
try {
if ( debug_flag ) cout << "argc = " << argc_list[n] << " [" << n << "]" << endl;
arg_stack.push_back(new ArgElement(paren,argc_list[n]));
return(arg_stack.back()->fun_argc);
} catch(ExprError) {
return(0);
}
} // End of ParseFun()
///////////////////////////////////////////////////////////////
void AlgebraicFun::commaFun(int paren)
{
if(arg_stack.back()->fun_argc <= ++(arg_stack.back()->argc)) {
throw ExprError("Too many arguments",&expr[cur]);
return;
} else if(arg_stack.back()->paren != paren) {
throw ExprError("Miss placed parentheses or comma",&expr[cur]);
return;
}
} // End of CommaFun()
///////////////////////////////////////////////////////////////
bool AlgebraicFun::closeParenFun(int paren)
{
if(arg_stack.empty()) {
return(false);
}
if(arg_stack.back()->paren != paren) {
return(false);
}
if(arg_stack.back()->fun_argc < ++(arg_stack.back()->argc)) {
throw ExprError("Too many arguments",&expr[cur]);
return(true);
}
delete arg_stack.back();
arg_stack.pop_back();
return(true);
} // End of CloseParenFun()
///////////////////////////////////////////////////////////////
void AlgebraicFun::exeFun(string n,vector<ExprElement*>& stack)
{
ExprElement* operand;
ExprElement* operand2;
switch(fun_list[n]) {
case ABS:
stack.back()->value = abs(stack.back()->value);
break;
case ACOS:
stack.back()->value = acos(stack.back()->value);
break;
case ACOSH:
stack.back()->value = acosh(stack.back()->value);
break;
case ASIN:
stack.back()->value = asin(stack.back()->value);
break;
case ASINH:
stack.back()->value = asinh(stack.back()->value);
break;
case ATAN:
stack.back()->value = atan(stack.back()->value);
break;
case ATAN2:
operand = stack.back();
stack.pop_back();
stack.back()->value = atan2(stack.back()->value,operand->value);
break;
case ATANH:
stack.back()->value = atanh(stack.back()->value);
break;
case CBRT:
stack.back()->value = cbrt(stack.back()->value);
break;
case CEIL:
stack.back()->value = ceil(stack.back()->value);
break;
case COPYSIGN:
operand = stack.back();
stack.pop_back();
stack.back()->value = copysign(stack.back()->value,operand->value);
break;
case COS:
stack.back()->value = cos(stack.back()->value);
break;
case COSH:
stack.back()->value = cosh(stack.back()->value);
break;
case ERF:
stack.back()->value = erf(stack.back()->value);
break;
case ERFC:
stack.back()->value = erfc(stack.back()->value);
break;
case EXP:
stack.back()->value = exp(stack.back()->value);
break;
case EXP2:
stack.back()->value = exp2(stack.back()->value);
break;
case EXPM1:
stack.back()->value = expm1(stack.back()->value);
break;
case FDIM:
operand = stack.back();
stack.pop_back();
stack.back()->value = fdim(stack.back()->value,operand->value);
break;
case FLOOR:
stack.back()->value = floor(stack.back()->value);
break;
case FMA:
operand2 = stack.back();
stack.pop_back();
operand = stack.back();
stack.pop_back();
stack.back()->value = fma(stack.back()->value,operand->value,operand2->value);
break;
case FMAX:
operand = stack.back();
stack.pop_back();
stack.back()->value = fmax(stack.back()->value,operand->value);
break;
case FMIN:
operand = stack.back();
stack.pop_back();
stack.back()->value = fmin(stack.back()->value,operand->value);
break;
case FMOD:
operand = stack.back();
stack.pop_back();
stack.back()->value = fmod(stack.back()->value,operand->value);
break;
case HYPOT:
operand = stack.back();
stack.pop_back();
stack.back()->value = hypot(stack.back()->value,operand->value);
break;
case LDEXP:
operand = stack.back();
stack.pop_back();
stack.back()->value = ldexp(stack.back()->value,operand->value);
break;
case LGAMMA:
stack.back()->value = lgamma(stack.back()->value);
break;
case LOG:
stack.back()->value = log(stack.back()->value);
break;
case LOG10:
stack.back()->value = log10(stack.back()->value);
break;
case LOG1P:
stack.back()->value = log1p(stack.back()->value);
break;
case LOG2:
stack.back()->value = log2(stack.back()->value);
break;
case LOGB:
stack.back()->value = logb(stack.back()->value);
break;
case POW:
operand = stack.back();
stack.pop_back();
stack.back()->value = pow(stack.back()->value,operand->value);
break;
case REMAINDER:
operand = stack.back();
stack.pop_back();
stack.back()->value = remainder(stack.back()->value,operand->value);
break;
case RINT:
stack.back()->value = rint(stack.back()->value);
break;
case ROUND:
stack.back()->value = round(stack.back()->value);
break;
case SIGNBIT:
stack.back()->value = signbit(stack.back()->value);
break;
case SIN:
stack.back()->value = sin(stack.back()->value);
break;
case SINH:
stack.back()->value = sinh(stack.back()->value);
break;
case SQRT:
stack.back()->value = sqrt(stack.back()->value);
break;
case TAN:
stack.back()->value = tan(stack.back()->value);
break;
case TANH:
stack.back()->value = tanh(stack.back()->value);
break;
case TGAMMA:
stack.back()->value = tgamma(stack.back()->value);
break;
case TRUNC:
stack.back()->value = trunc(stack.back()->value);
break;
case XRT:
operand = stack.back();
stack.pop_back();
stack.back()->value = pow(stack.back()->value,1/operand->value);
break;
}
} // End of ExeFun()
///////////////////////////////////////////////////////////////
///////////////////// END OF FILE /////////////////////////////
///////////////////////////////////////////////////////////////
Header
// token.h generated by reflex 3.3.5 from token.l
#ifndef REFLEX_TOKEN_H
#define REFLEX_TOKEN_H
#define yyIN_HEADER 1
#define REFLEX_VERSION "3.3.5"
////////////////////////////////////////////////////////////////////////////////
// //
// OPTIONS USED //
// //
////////////////////////////////////////////////////////////////////////////////
#define REFLEX_OPTION_flex false
#define REFLEX_OPTION_header_file "token.h"
#define REFLEX_OPTION_lex lex
#define REFLEX_OPTION_lexer Lexer
#define REFLEX_OPTION_never_interactive true
#define REFLEX_OPTION_noyywrap true
#define REFLEX_OPTION_outfile "token.cpp"
#define REFLEX_OPTION_prefix yy
////////////////////////////////////////////////////////////////////////////////
// //
// SECTION 1: %top user code //
// //
////////////////////////////////////////////////////////////////////////////////
#line 1 "token.l"
#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
////////////////////////////////////////////////////////////////////////////////
// //
// REGEX MATCHER //
// //
////////////////////////////////////////////////////////////////////////////////
#include <reflex/matcher.h>
////////////////////////////////////////////////////////////////////////////////
// //
// FLEX-COMPATIBLE ABSTRACT LEXER CLASS //
// //
////////////////////////////////////////////////////////////////////////////////
#include <reflex/flexlexer.h>
typedef reflex::FlexLexer<reflex::Matcher> FlexLexer;
////////////////////////////////////////////////////////////////////////////////
// //
// LEXER CLASS //
// //
////////////////////////////////////////////////////////////////////////////////
class Lexer : public FlexLexer {
public:
Lexer(
// a persistent source of input, empty by default
const reflex::Input& input = reflex::Input(),
// optional output stream, NULL means std::cout by default
std::ostream *os = NULL)
:
FlexLexer(input, os)
{
}
virtual int yylex(void)
{
LexerError("Lexer::yylex invoked but %option lex=lex is used");
yyterminate();
}
// the flex lexer function defined by SECTION 2
virtual int lex(void);
// lexer functions accepting new input to scan
int lex(const reflex::Input& input)
{
in(input);
return lex();
}
int lex(const reflex::Input& input, std::ostream *os)
{
in(input);
if (os)
out(*os);
return lex();
}
};
#endif
Implementation
// token.cpp generated by reflex 3.3.5 from token.l
#define REFLEX_VERSION "3.3.5"
////////////////////////////////////////////////////////////////////////////////
// //
// OPTIONS USED //
// //
////////////////////////////////////////////////////////////////////////////////
#define REFLEX_OPTION_flex false
#define REFLEX_OPTION_header_file "token.h"
#define REFLEX_OPTION_lex lex
#define REFLEX_OPTION_lexer Lexer
#define REFLEX_OPTION_never_interactive true
#define REFLEX_OPTION_noyywrap true
#define REFLEX_OPTION_outfile "token.cpp"
#define REFLEX_OPTION_prefix yy
////////////////////////////////////////////////////////////////////////////////
// //
// SECTION 1: %top user code //
// //
////////////////////////////////////////////////////////////////////////////////
#line 1 "token.l"
#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
////////////////////////////////////////////////////////////////////////////////
// //
// FLEX-COMPATIBLE DEFINITIONS //
// //
////////////////////////////////////////////////////////////////////////////////
#define INITIAL (0)
#define YY_NUM_RULES (64)
////////////////////////////////////////////////////////////////////////////////
// //
// LEXER CLASS INCLUDE //
// //
////////////////////////////////////////////////////////////////////////////////
#include "token.h"
////////////////////////////////////////////////////////////////////////////////
// //
// SECTION 1: %{ user code %} //
// //
////////////////////////////////////////////////////////////////////////////////
#line 8 "token.l"
#include "token_enum.h"
////////////////////////////////////////////////////////////////////////////////
// //
// SECTION 2: rules //
// //
////////////////////////////////////////////////////////////////////////////////
int Lexer::lex(void)
{
static const char *REGEX_INITIAL = "(?m)(0[0-7]+)|([0-9]+)|([0-9]*\\.[0-9]+)|([0-9]*\\.[0-9]+[Ee][\\x2b\\x2d][0-9]*)|(0[Xx][0-9A-Fa-f]+)|(0[Bb][01]+)|('[^\\x5c]+')|('\\\\[\"'0abfnrtv]')|('\\\\[0-7]+')|('\\\\x[0-9A-Fa-f]+')|(\"(?:[^\"\\x5c]|\\\\.)*\")|(\\$[A-Z_a-z][0-9A-Z_a-z]*)|([A-Z_a-z][0-9A-Z_a-z]*)|([A-Z_a-z][0-9A-Z_a-z]*[\\x09\\x20]*\\()|(//)|(<<=)|(>>=)|(\\+\\+)|(--)|(<<)|(>>)|(<=)|(>=)|(==)|(!=)|(&&)|(\\|\\|)|(\\*=)|(/=)|(%=)|(\\+=)|(-=)|(&=)|(\\|=)|(\\^=)|(-)|(\\+)|(\\*)|(/)|(%)|(<)|(>)|(\\^)|(\\|)|(@)|(&)|(=)|(~)|(!)|(\\?)|(:)|(\\()|(\\))|(\\[)|(\\])|(\\{)|(\\})|(;)|(,)|(/\\*)|(\\*/)|(#)|(\\s)|(.)";
static const reflex::Pattern PATTERN_INITIAL(REGEX_INITIAL);
if (!has_matcher())
{
matcher(new Matcher(PATTERN_INITIAL, stdinit(), this));
YY_USER_INIT
}
while (true)
{
switch (matcher().scan())
{
case 0:
if (matcher().at_end())
{
yyterminate();
}
else
{
output(matcher().input());
}
YY_BREAK
case 1: // rule token.l:15: 0[0-7]+ :
YY_USER_ACTION
#line 15 "token.l"
{ return(OCTAL_TOK); }
YY_BREAK
case 2: // rule token.l:16: [0-9]+ :
YY_USER_ACTION
#line 16 "token.l"
{ return(INTEGER_TOK); }
YY_BREAK
case 3: // rule token.l:17: [0-9]*\.[0-9]+ :
YY_USER_ACTION
#line 17 "token.l"
{ return(FLOAT_TOK); }
YY_BREAK
case 4: // rule token.l:18: [0-9]*\.[0-9]+[Ee][+-][0-9]* :
YY_USER_ACTION
#line 18 "token.l"
{ return(FLOAT_TOK); }
YY_BREAK
case 5: // rule token.l:19: 0[xX][0-9a-fA-F]+ :
YY_USER_ACTION
#line 19 "token.l"
{ return(HEX_TOK); }
YY_BREAK
case 6: // rule token.l:20: 0[bB][01]+ :
YY_USER_ACTION
#line 20 "token.l"
{ return(BIN_TOK); }
YY_BREAK
case 7: // rule token.l:21: '[^\\]+' :
YY_USER_ACTION
#line 21 "token.l"
{ return(CHAR_TOK); }
YY_BREAK
case 8: // rule token.l:22: '\\[0abfnrtv'\"]' :
YY_USER_ACTION
#line 22 "token.l"
{ return(CTRL_CHAR_TOK); }
YY_BREAK
case 9: // rule token.l:23: '\\[0-7]+' :
YY_USER_ACTION
#line 23 "token.l"
{ return(OCT_CHAR_TOK); }
YY_BREAK
case 10: // rule token.l:24: '\\x[0-9a-fA-F]+' :
YY_USER_ACTION
#line 24 "token.l"
{ return(HEX_CHAR_TOK); }
YY_BREAK
case 11: // rule token.l:25: \"([^\"\\]|\\.)*\" :
YY_USER_ACTION
#line 25 "token.l"
{ return(STRING_CONST_TOK); }
YY_BREAK
case 12: // rule token.l:26: \$[a-zA-Z_][a-zA-Z0-9_]* :
YY_USER_ACTION
#line 26 "token.l"
{ return(SHELL_VARIABLE_TOK); }
YY_BREAK
case 13: // rule token.l:27: [a-zA-Z_][a-zA-Z0-9_]* :
YY_USER_ACTION
#line 27 "token.l"
{ return(VARIABLE_TOK); }
YY_BREAK
case 14: // rule token.l:28: [a-zA-Z_][a-zA-Z0-9_]*[ \t]*\( :
YY_USER_ACTION
#line 28 "token.l"
{ return(FUNCTION_TOK); }
YY_BREAK
case 15: // rule token.l:29: \/\/ :
YY_USER_ACTION
#line 29 "token.l"
{ return(COMMENT_TOK); }
YY_BREAK
case 16: // rule token.l:30: <<= :
YY_USER_ACTION
#line 30 "token.l"
{ return(SH_LEFT_ASSIGN_TOK); }
YY_BREAK
case 17: // rule token.l:31: >>= :
YY_USER_ACTION
#line 31 "token.l"
{ return(SH_RIGHT_ASSIGN_TOK); }
YY_BREAK
case 18: // rule token.l:32: \+\+ :
YY_USER_ACTION
#line 32 "token.l"
{ return(POST_INC_TOK); }
YY_BREAK
case 19: // rule token.l:33: -- :
YY_USER_ACTION
#line 33 "token.l"
{ return(POST_DEC_TOK); }
YY_BREAK
case 20: // rule token.l:34: << :
YY_USER_ACTION
#line 34 "token.l"
{ return(SHIFT_LEFT_TOK); }
YY_BREAK
case 21: // rule token.l:35: >> :
YY_USER_ACTION
#line 35 "token.l"
{ return(SHIFT_RIGHT_TOK); }
YY_BREAK
case 22: // rule token.l:36: <= :
YY_USER_ACTION
#line 36 "token.l"
{ return(LESS_EQUAL_TOK); }
YY_BREAK
case 23: // rule token.l:37: >= :
YY_USER_ACTION
#line 37 "token.l"
{ return(GREATER_EQUAL_TOK); }
YY_BREAK
case 24: // rule token.l:38: == :
YY_USER_ACTION
#line 38 "token.l"
{ return(EQUAL_TOK); }
YY_BREAK
case 25: // rule token.l:39: != :
YY_USER_ACTION
#line 39 "token.l"
{ return(NOT_EQUAL_TOK); }
YY_BREAK
case 26: // rule token.l:40: && :
YY_USER_ACTION
#line 40 "token.l"
{ return(AND_TOK); }
YY_BREAK
case 27: // rule token.l:41: \|\| :
YY_USER_ACTION
#line 41 "token.l"
{ return(OR_TOK); }
YY_BREAK
case 28: // rule token.l:42: \*= :
YY_USER_ACTION
#line 42 "token.l"
{ return(MUL_ASSIGN_TOK); }
YY_BREAK
case 29: // rule token.l:43: \/= :
YY_USER_ACTION
#line 43 "token.l"
{ return(DIV_ASSIGN_TOK); }
YY_BREAK
case 30: // rule token.l:44: %= :
YY_USER_ACTION
#line 44 "token.l"
{ return(MOD_ASSIGN_TOK); }
YY_BREAK
case 31: // rule token.l:45: \+= :
YY_USER_ACTION
#line 45 "token.l"
{ return(ADD_ASSIGN_TOK); }
YY_BREAK
case 32: // rule token.l:46: \-= :
YY_USER_ACTION
#line 46 "token.l"
{ return(SUB_ASSIGN_TOK); }
YY_BREAK
case 33: // rule token.l:47: &= :
YY_USER_ACTION
#line 47 "token.l"
{ return(AND_ASSIGN_TOK); }
YY_BREAK
case 34: // rule token.l:48: \|= :
YY_USER_ACTION
#line 48 "token.l"
{ return(OR_ASSIGN_TOK); }
YY_BREAK
case 35: // rule token.l:49: \^= :
YY_USER_ACTION
#line 49 "token.l"
{ return(XOR_ASSIGN_TOK); }
YY_BREAK
case 36: // rule token.l:50: - :
YY_USER_ACTION
#line 50 "token.l"
{ return(UNARY_MINUS_TOK); }
YY_BREAK
case 37: // rule token.l:51: \+ :
YY_USER_ACTION
#line 51 "token.l"
{ return(UNARY_PLUS_TOK); }
YY_BREAK
case 38: // rule token.l:52: \* :
YY_USER_ACTION
#line 52 "token.l"
{ return(MULTIPLY_TOK); }
YY_BREAK
case 39: // rule token.l:53: \/ :
YY_USER_ACTION
#line 53 "token.l"
{ return(DIVIDE_TOK); }
YY_BREAK
case 40: // rule token.l:54: % :
YY_USER_ACTION
#line 54 "token.l"
{ return(MODULO_TOK); }
YY_BREAK
case 41: // rule token.l:55: < :
YY_USER_ACTION
#line 55 "token.l"
{ return(LESS_TOK); }
YY_BREAK
case 42: // rule token.l:56: > :
YY_USER_ACTION
#line 56 "token.l"
{ return(GREATER_TOK); }
YY_BREAK
case 43: // rule token.l:57: \^ :
YY_USER_ACTION
#line 57 "token.l"
{ return(BIT_XOR_TOK); }
YY_BREAK
case 44: // rule token.l:58: \| :
YY_USER_ACTION
#line 58 "token.l"
{ return(BIT_OR_TOK); }
YY_BREAK
case 45: // rule token.l:59: @ :
YY_USER_ACTION
#line 59 "token.l"
{ return(ATSIGN_TOK); }
YY_BREAK
case 46: // rule token.l:60: & :
YY_USER_ACTION
#line 60 "token.l"
{ return(BIT_AND_TOK); }
YY_BREAK
case 47: // rule token.l:61: = :
YY_USER_ACTION
#line 61 "token.l"
{ return(ASSIGN_TOK); }
YY_BREAK
case 48: // rule token.l:62: ~ :
YY_USER_ACTION
#line 62 "token.l"
{ return(COMPLEMENT_TOK); }
YY_BREAK
case 49: // rule token.l:63: ! :
YY_USER_ACTION
#line 63 "token.l"
{ return(NOT_TOK); }
YY_BREAK
case 50: // rule token.l:64: \? :
YY_USER_ACTION
#line 64 "token.l"
{ return(QUESTION_TOK); }
YY_BREAK
case 51: // rule token.l:65: : :
YY_USER_ACTION
#line 65 "token.l"
{ return(COLON_TOK); }
YY_BREAK
case 52: // rule token.l:66: \( :
YY_USER_ACTION
#line 66 "token.l"
{ return(OPEN_PAREN_TOK); }
YY_BREAK
case 53: // rule token.l:67: \) :
YY_USER_ACTION
#line 67 "token.l"
{ return(CLOSE_PAREN_TOK); }
YY_BREAK
case 54: // rule token.l:68: \[ :
YY_USER_ACTION
#line 68 "token.l"
{ return(OPEN_BRACE_TOK); }
YY_BREAK
case 55: // rule token.l:69: \] :
YY_USER_ACTION
#line 69 "token.l"
{ return(CLOSE_BRACE_TOK); }
YY_BREAK
case 56: // rule token.l:70: \{ :
YY_USER_ACTION
#line 70 "token.l"
{ return(OPEN_BRACKET_TOK); }
YY_BREAK
case 57: // rule token.l:71: \} :
YY_USER_ACTION
#line 71 "token.l"
{ return(CLOSE_BRACKET_TOK); }
YY_BREAK
case 58: // rule token.l:72: ; :
YY_USER_ACTION
#line 72 "token.l"
{ return(SEMICOLON_TOK); }
YY_BREAK
case 59: // rule token.l:73: , :
YY_USER_ACTION
#line 73 "token.l"
{ return(COMMA_TOK); }
YY_BREAK
case 60: // rule token.l:74: \/\* :
YY_USER_ACTION
#line 74 "token.l"
{ return(OPEN_COMMENT_TOK); }
YY_BREAK
case 61: // rule token.l:75: \*\/ :
YY_USER_ACTION
#line 75 "token.l"
{ return(CLOSE_COMMENT_TOK); }
YY_BREAK
case 62: // rule token.l:76: # :
YY_USER_ACTION
#line 76 "token.l"
{ return(COMMENT_TOK); }
YY_BREAK
case 63: // rule token.l:77: \s :
YY_USER_ACTION
#line 77 "token.l"
{ return(WHITE_SPACE_TOK); }
YY_BREAK
case 64: // rule token.l:78: . :
YY_USER_ACTION
#line 78 "token.l"
{ return(ERROR_TOK); }
YY_BREAK
}
}
}
Lexer Input
%top{
#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include <map>
%}
%{
#include "token_enum.h"
%}
%option flex=false
%option noyywrap
%option never-interactive
%%
0[0-7]+ { return(OCTAL_TOK); }
[0-9]+ { return(INTEGER_TOK); }
[0-9]*\.[0-9]+ { return(FLOAT_TOK); }
[0-9]*\.[0-9]+[Ee][+-][0-9]* { return(FLOAT_TOK); }
0[xX][0-9a-fA-F]+ { return(HEX_TOK); }
0[bB][01]+ { return(BIN_TOK); }
'[^\\]+' { return(CHAR_TOK); }
'\\[0abfnrtv'\"]' { return(CTRL_CHAR_TOK); }
'\\[0-7]+' { return(OCT_CHAR_TOK); }
'\\x[0-9a-fA-F]+' { return(HEX_CHAR_TOK); }
\"([^\"\\]|\\.)*\" { return(STRING_CONST_TOK); }
\$[a-zA-Z_][a-zA-Z0-9_]* { return(SHELL_VARIABLE_TOK); }
[a-zA-Z_][a-zA-Z0-9_]* { return(VARIABLE_TOK); }
[a-zA-Z_][a-zA-Z0-9_]*[ \t]*\( { return(FUNCTION_TOK); }
\/\/ { return(COMMENT_TOK); }
<<= { return(SH_LEFT_ASSIGN_TOK); }
>>= { return(SH_RIGHT_ASSIGN_TOK); }
\+\+ { return(POST_INC_TOK); }
-- { return(POST_DEC_TOK); }
<< { return(SHIFT_LEFT_TOK); }
>> { return(SHIFT_RIGHT_TOK); }
<= { return(LESS_EQUAL_TOK); }
>= { return(GREATER_EQUAL_TOK); }
== { return(EQUAL_TOK); }
!= { return(NOT_EQUAL_TOK); }
&& { return(AND_TOK); }
\|\| { return(OR_TOK); }
\*= { return(MUL_ASSIGN_TOK); }
\/= { return(DIV_ASSIGN_TOK); }
%= { return(MOD_ASSIGN_TOK); }
\+= { return(ADD_ASSIGN_TOK); }
\-= { return(SUB_ASSIGN_TOK); }
&= { return(AND_ASSIGN_TOK); }
\|= { return(OR_ASSIGN_TOK); }
\^= { return(XOR_ASSIGN_TOK); }
- { return(UNARY_MINUS_TOK); }
\+ { return(UNARY_PLUS_TOK); }
\* { return(MULTIPLY_TOK); }
\/ { return(DIVIDE_TOK); }
% { return(MODULO_TOK); }
< { return(LESS_TOK); }
> { return(GREATER_TOK); }
\^ { return(BIT_XOR_TOK); }
\| { return(BIT_OR_TOK); }
@ { return(ATSIGN_TOK); }
& { return(BIT_AND_TOK); }
= { return(ASSIGN_TOK); }
~ { return(COMPLEMENT_TOK); }
! { return(NOT_TOK); }
\? { return(QUESTION_TOK); }
: { return(COLON_TOK); }
\( { return(OPEN_PAREN_TOK); }
\) { return(CLOSE_PAREN_TOK); }
\[ { return(OPEN_BRACE_TOK); }
\] { return(CLOSE_BRACE_TOK); }
\{ { return(OPEN_BRACKET_TOK); }
\} { return(CLOSE_BRACKET_TOK); }
; { return(SEMICOLON_TOK); }
, { return(COMMA_TOK); }
\/\* { return(OPEN_COMMENT_TOK); }
\*\/ { return(CLOSE_COMMENT_TOK); }
# { return(COMMENT_TOK); }
\s { return(WHITE_SPACE_TOK); }
. { return(ERROR_TOK); }
%%
Lexer Table
#/////////////////////////////////////////////////////////////////////////////////////
#///
#/// Acme Software Works, Inc.
#///
#/// Created January 27, 2004 by Don Dugger
#///
#/// <PRE>
#/// Copyright 2019 Acme Software Works, Inc.
#///
#/// Redistribution and use in source and binary forms, with or without
#/// modification, are permitted provided that the following conditions are met:
#///
#/// 1. Redistributions of source code must retain the above copyright notice,
#/// this list of conditions and the following disclaimer.
#///
#/// 2. 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.
#///
#/// 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 HOLDER 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.
#/// </PRE>
#/////////////////////////////////////////////////////////////////////////////////////
#
# class AlgebraicExpr
# Token Prec Category
INTEGER 0 OPERAND_CAT
FLOAT 0 OPERAND_CAT
OCTAL 0 OPERAND_CAT
HEX 0 OPERAND_CAT
BIN 0 OPERAND_CAT
CHAR 0 OPERAND_CAT
CTRL_CHAR 0 OPERAND_CAT
OCT_CHAR 0 OPERAND_CAT
HEX_CHAR 0 OPERAND_CAT
STRING_CONST 0 OPERAND_CAT
VARIABLE 0 OPERAND_CAT
SHELL_VARIABLE 0 OPERAND_CAT
FUNCTION 1 FUNCTION_CAT
OPEN_PAREN -3 PAREN_CAT
CLOSE_PAREN 0 PAREN_CAT
POST_INC 12 SPECUNARY_CAT
PRE_INC 12 UNARY_CAT
POST_DEC 12 SPECUNARY_CAT
PRE_DEC 12 UNARY_CAT
COMPLEMENT 12 BIT_CAT
NOT 12 SPECUNARY_CAT
UNARY_MINUS 12 UNARY_CAT
UNARY_PLUS 12 UNARY_CAT
MULTIPLY 11 MATH_CAT
DIVIDE 11 MATH_CAT
MODULO 11 BIT_CAT
ADD 10 MATH_CAT
SUBTRACT 10 MATH_CAT
SHIFT_LEFT 9 BIT_CAT
SHIFT_RIGHT 9 BIT_CAT
LESS 8 COMPARE_CAT
LESS_EQUAL 8 COMPARE_CAT
GREATER 8 COMPARE_CAT
GREATER_EQUAL 8 COMPARE_CAT
EQUAL 7 COMPARE_CAT
NOT_EQUAL 7 COMPARE_CAT
ATSIGN 0 PUCH_CAT
BIT_AND 6 BIT_CAT
BIT_XOR 5 BIT_CAT
BIT_OR 4 BIT_CAT
AND 3 BIT_CAT
OR 2 BIT_CAT
QUESTION -2 PUCH_CAT
COLON -2 PUCH_CAT
ASSIGN -1 ASSIGN_CAT
MUL_ASSIGN -1 ASSIGN_CAT
DIV_ASSIGN -1 ASSIGN_CAT
MOD_ASSIGN -1 ASSIGN_CAT
ADD_ASSIGN -1 ASSIGN_CAT
SUB_ASSIGN -1 ASSIGN_CAT
SH_LEFT_ASSIGN -1 ASSIGN_CAT
SH_RIGHT_ASSIGN -1 ASSIGN_CAT
AND_ASSIGN -1 ASSIGN_CAT
OR_ASSIGN -1 ASSIGN_CAT
XOR_ASSIGN -1 ASSIGN_CAT
OPEN_BRACE 0 PAREN_CAT
CLOSE_BRACE 0 PAREN_CAT
OPEN_BRACKET 0 PAREN_CAT
CLOSE_BRACKET 0 PAREN_CAT
SEMICOLON 0 PUCH_CAT
COMMA -3 PUCH_CAT
COMMENT 0 COMMENT_CAT
OPEN_COMMENT 0 COMMENT_CAT
CLOSE_COMMENT 0 COMMENT_CAT
VALUE 0 OPERAND_CAT
FILE 0 FILE_CAT
OUTFILE 0 FILE_CAT
INFILE 0 FILE_CAT
BUILTIN_FUNCTION 99 FUNCTION_CAT
CONTROL_FUNCTION 99 FUNCTION_CAT
BUILTIN_TYPE 0 TYPE_CAT
MAIN_FUNCTION 0 FUNCTION_CAT
WHITE_SPACE 0 PUCH_CAT
NOP 0 PUCH_CAT
EOF 0 PUCH_CAT
#
Make Lexer Files
This script produces four files uses to aid the lexer. It takes the file lex.tab as an input.
- token_prec.cpp An array of the token precedence.
- token_cat.cpp An array of the token categories.
- token_enum.cpp The token enumerators.
- token_print.cpp A switch statement for outputting token names used for debuging.
#! /bin/sh
#/////////////////////////////////////////////////////////////////////////////////////
#///
#/// Acme Software Works, Inc.
#///
#/// Created January 27, 2004 by Don Dugger
#///
#/// <PRE>
#/// Copyright 2019 Acme Software Works, Inc.
#///
#/// Redistribution and use in source and binary forms, with or without
#/// modification, are permitted provided that the following conditions are met:
#///
#/// 1. Redistributions of source code must retain the above copyright notice,
#/// this list of conditions and the following disclaimer.
#///
#/// 2. 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.
#///
#/// 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 HOLDER 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.
#/// </PRE>
#/////////////////////////////////////////////////////////////////////////////////////
if [ "$AWK" = "" ] ; then
AWK="awk"
fi
#####################################################
cat $@ | $AWK '#
BEGIN {
start = "NO"
}
$1 ~ /^#class/ {
class = $2;
next
}
$1 ~ /^#/ {
next
}
$1 ~ /^[A-Z]+/ {
if ( start == "NO") {
start = "YES"
# printf("int %s::token_prec[] = {\n 0,\n %d",class,$2);
printf(" 0,\n %d",$2);
} else {
printf(",\n %d",$2);
}
}
END {
# printf("\n};\n");
printf("\n");
}' - > ../src/token_prec.cpp
#####################################################
cat $@ | $AWK '#
BEGIN {
start = "NO"
}
$1 ~ /^#class/ {
class = $2;
next
}
$1 ~ /^#/ {
next
}
$1 ~ /^[A-Z]+/ {
if ( start == "NO") {
start = "YES"
# printf("TOKEN_CATEGORY %s::token_cat[] = {\n PUCH_CAT,\n %s",class,$3);
printf(" PUCH_CAT,\n %s",$3);
} else {
printf(",\n %s",$3);
}
}
END {
# printf("\n};\n");
printf("\n");
}' - > ../src/token_cat.cpp
#####################################################
cat $@ | $AWK '#
BEGIN {
start = "NO"
}
$1 ~ /^#/ {
next
}
{
if ( start == "NO") {
start = "YES"
num = 1
tok = sprintf("%s_TOK",$1);
printf("#ifndef _token_enum_h_\n#define _token_enum_h_\n\n");
printf("enum TOKEN {\n %-20s = %d,\n %-20s = %d","ERROR_TOK",0,tok,num);
++num
} else {
tok = sprintf("%s_TOK",$1);
printf(",\n %-20s = %d",tok,num);
++num
}
}
END {
printf("\n};\n");
printf("\n#endif /* _token_enum_h_ */\n");
}' - > ../src/token_enum.h
#####################################################
cat $@ | $AWK '#
BEGIN {
start = "NO"
}
$1 ~ /^#/ {
next
}
{
if ( start == "NO") {
start = "YES"
tok = sprintf("%s_TOK",$1);
printf(" switch(token) {\n case %s :\n printf(\"%s\\n\");\n break;\n",tok,tok);
} else {
tok = sprintf("%s_TOK",$1);
printf(" case %s :\n printf(\"%s\\n\");\n break;\n",tok,tok);
}
}
END {
printf(" default:\n printf(\"UNKNOWN TOKEN %%d\\n\",token);\n }\n");
}' - > ../src/token_print.cpp
#####################################################
exit 0
Test Program math.cpp
/////////////////////////////////////////////////////////////////////////////////////
///
/// Acme Software Works, Inc.
///
/// Created January 27, 2004 by Don Dugger
///
/// <PRE>
/// Copyright 2019 Acme Software Works, Inc.
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions are met:
///
/// 1. Redistributions of source code must retain the above copyright notice,
/// this list of conditions and the following disclaimer.
///
/// 2. 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.
///
/// 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 HOLDER 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.
/// </PRE>
/////////////////////////////////////////////////////////////////////////////////////
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
/////////////////////////////////////////////////////////////*/
//
#include "algebraicfun.h"
//
using namespace std;
//
/////////////////////////////////////////////////////////////*/
//
// Change the defualt termination to exit
//
void terminator() {
exit(-1);
}
void (*old_terminate)() = set_terminate(terminator);
//////////////////////////////////////////////////////////////
//
// Split a string at the delimiter
//
vector<string> split(vector<string>& list, string str, string delimiter)
{
size_t start = 0;
size_t end;
size_t delimiter_len = delimiter.length();
string segment;
while ((end = str.find(delimiter, start)) != string::npos) {
segment = str.substr(start, end - start);
list.push_back(segment);
start = end + delimiter_len;
}
list.push_back(str.substr(start));
return list;
} // End of split ()
//////////////////////////////////////////////////////////////
//
// main
//
//
int main(int argc,char* argv[])
{
string input_str;
double x = 0;
map<string,double> var_list;
vector<string> expressions;
enum COMMANDS {
DEFAULT, // Use up 0
QUIT,
HELP,
VARS,
DEBUG
};
map<string,COMMANDS> cmd_list {
{ ".", QUIT },
{ "?", HELP },
{ "=", VARS },
{ "!0", DEBUG0 },
{ "!1", DEBUG1 },
{ "!2", DEBUG2 }
};
{
AlgebraicFun math(var_list);
math.solve("PI=3.141592653589793");
math.solve("E=2.718281828459045");
math.solve("PHI=1.61803398875");
for (;;) {
getline(cin,input_str);
expressions = split(expressions,input_str,";");
for ( auto &expression : expressions ) {
if ( expression.size() == 0 ) exit(0);
cout << "[" << expression << "]" << endl;
switch(cmd_list[expression]) {
case QUIT:
exit(0);
case HELP:
cout << "Enter the character followed by Return" << endl;
cout << ". Quit" << endl;
cout << "? Help (This)" << endl;
cout << "= Variable List" << endl;
cout << "!0 Cancel debug mode" << endl;
cout << "!1 Debug mode 1" << endl;
cout << "!2 Debug mode 2" << endl;
break;
case VARS:
cout << "---------Variable_List----------" << endl;
for (auto const& x : var_list) {
cout << x.first // string (key)
<< ": \t"
<< x.second // string's value
<< endl ;
}
break;
case DEBUG0:
math.debug(0);
break;
case DEBUG1 :
math.debug(1);
break;
case DEBUG2 :
math.debug(2);
break;
default:
x = math.solve(expression);
cout << "RESULT> " << x << endl;
}
}
}
}
exit(0);
} // End of main()
//////////////////////////////////////////////////////////////
///////////////////// END OF FILE ////////////////////////////
//////////////////////////////////////////////////////////////
Test Program Build Script build
#!/bin/sh
CC=""
g++ --version >/dev/null 2>&1
if [ $? -eq 0 ] ; then
CC="g++"
fi
clang++ --version >/dev/null 2>&1
if [ $? -eq 0 ] ; then
CC="clang++"
fi
if [ -z "$CC" ] ; then
echo "Unknown or no C++ compiler installed on this system"
exit 1
fi
if [ $1 != "" ] ; then
CC=$1
fi
$CC -std=c++11 -I../src -c math.cpp -o math.o
$CC -std=c++11 -I../src -o math math.o -L ../ -lalgebraicexpr -lm -L/usr/local/lib/ -lreflex
exit 0
For more information: info@acmesoftwareworks.com