ASW

Algebraic Expression Library

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.

Detailed Functional Description and Interface Specification


Doxygen Document

Installation Instructions

  1. First you will need to install RE/Flex. You will find installation instructions there.
  2. Then download the tar file.
  3. Then un-tar it in your home directory.
  4. Change directory to algebraicexpr.
  5. Then execute the configure script.
  6. 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

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