Skip to main content
Tweeted twitter.com/StackCodeReview/status/1378180632854081537
Became Hot Network Question
added 322 characters in body
Source Link
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <string_view>
#include <string>
#include <vector>
#include <utility>
#include <stack>
#include <numbers>
#include <regex>
#include <variant>
#include <chrono>
#include <cmath>

constexpr int LEFT_ASSOC = 0;
constexpr int RIGHT_ASSOC = 1;

//pair is <prec, assoc_id>
static const std::unordered_map<std::string_view, std::pair<int, int>> assoc_prec{ {"^", std::make_pair(4, RIGHT_ASSOC)},
                                                                                   {"*", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"/", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"+", std::make_pair(2, LEFT_ASSOC)},
                                                                                   {"-", std::make_pair(2, LEFT_ASSOC)} };

static const std::unordered_map<std::string_view, double(*)(double)> unary_func_tbl{ {"sin", &sin},
                                                                                     {"cos", &cos},
                                                                                     {"sqrt", &sqrt},
                                                                                     {"abs", &abs},
                                                                                     {"tan", &tan},
                                                                                     {"acos", &acos},
                                                                                     {"asin", &asin},
                                                                                     {"atan", &atan},
                                                                                     {"log", &log},
                                                                                     {"log10", &log10},
                                                                                     {"cosh", &cosh},
                                                                                     {"sinh", &sinh},
                                                                                     {"tanh", &tanh},
                                                                                     {"exp", &exp},
                                                                                     {"cbrt", &cbrt},
                                                                                     {"tgamma", &tgamma},
                                                                                     {"lgamma", &lgamma},
                                                                                     {"ceil", &ceil},
                                                                                     {"floor", &floor},
                                                                                     {"acosh", &acosh},
                                                                                     {"asinh", &asinh},
                                                                                     {"trunc", &trunc},
                                                                                     {"atanh", &atanh} };

static const std::unordered_set<std::string_view> funcs{ "sin", "tan", "acos", "asin", "asin", "abs",
                                                         "atan", "cosh", "sinh", "cos", "tanh",
                                                         "acosh", "asinh", "atanh", "exp", "ldexp",
                                                         "log", "log10", "sqrt", "cbrt", "tgamma",
                                                         "lgamma", "ceil", "floor", "trunc" };

bool is_left_assoc(std::string_view str)
{
    int id = assoc_prec.at(str).second;
    if (id == 0) return true;
    else return false;
}

bool is_binary_op(std::string_view str)
{
    if (str == "/" || str == "*" || str == "+" || str == "-" || str == "^")
    {
        return true;
    }
    else return false;
}

bool is_func(std::string_view str)
{
    if (funcs.count(str) > 0) return true;
    else return false;
}


//handls decimal numbers
bool is_num(std::string_view str)
{
    int num_found_periods = 0;
    for (const auto c : str)
    {
        if (c == '.')
        {
            num_found_periods++;
            if (num_found_periods > 1)
            {
                return false;
            }
        }
        if (!isdigit(c) && c != '.')
        {
            return false;
        }
    }
    return true;
}

int get_prec(std::string_view str)
{
    if (is_func(str))
    {
        return 1; //TODO: check it this is the correct value
    }
    else if (is_binary_op(str))
    {
        return assoc_prec.at(str).first;
    }
    else
    {
        return 0;
    }
}


//from https://stackoverflow.com/a/56204256
//modified regex expr
std::vector<std::string> tokenize(const std::string& str)
{
    std::vector<std::string> res;
    const std::regex words_regex("(sin|tan|acos|asin|abs|atan|cosh|sinh|cos|"
                                 "tanh|acosh|asinh|atanh|exp|ldexp|log|log10|"
                                 "sqrt|cbrt|tgamma|lgamma|ceil|floor|x|e)|^-|[0-9]?"
                                 "([0-9]*[.])?[0-9]+|[\\-\\+\\\\\(\\)\\/\\*\\^\\]",
                                  std::regex_constants::egrep);

    auto words_begin = std::sregex_iterator(str.begin(), str.end(), words_regex);
    auto words_end = std::sregex_iterator();
    for (std::sregex_iterator i = words_begin; i != words_end; ++i) 
    {
        res.push_back((*i).str());
    }
    return res;
}

//params: 
//str - string to be converted
//var_name - any occurances of this as a seperate token will be treated as a varable
//convert string in infix notation to a string in Reverse Polish Notation
//using dijkstra's shunting yard algorithm 
std::vector<std::variant<double, std::string>> s_yard(const std::string& str, std::string var_name)
{
    std::vector<std::variant<double, std::string>> output_queue;
    std::stack<std::string> op_stack;

    for (const auto& tok : tokenize(str))
    {
        if (tok == "pi")
        {
            output_queue.push_back(std::numbers::pi_v<double>);
        }
        else if (tok == "e")
        {
            output_queue.push_back(std::numbers::e_v<double>);
        }
        else if (tok == var_name)
        {
            output_queue.push_back(tok);
        }
        else if (is_num(tok))
        {
            output_queue.push_back(strtod(tok.c_str(), NULL));
        }
        else if (is_func(tok))
        {
            op_stack.push(tok);
        }
        else if (is_binary_op(tok))
        {   
            while (!op_stack.empty() && \
                  (is_binary_op(op_stack.top()) && get_prec(op_stack.top()) > (get_prec(tok)) || \
                  (get_prec(op_stack.top()) == get_prec(tok) && is_left_assoc(tok))) && \
                  (op_stack.top() != "("))
            {
                //pop operators from stack to queue
                while (!op_stack.empty())
                {
                    output_queue.push_back(op_stack.top());
                    op_stack.pop();
                }
            }
            op_stack.push(tok);
        }
        else if (tok == "(")
        {
            op_stack.push(tok);
        }
        else if (tok == ")")
        {
            while (op_stack.top() != "(")
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
            if (op_stack.top() == "(")
            {
                op_stack.pop();
            }
            if (is_func(op_stack.top()))
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
        }
    }
    //all tokens read
    while (!op_stack.empty())
    {
        //there are mismatched parentheses
        if (op_stack.top() == "(" || op_stack.top() == ")")
        {
            std::cout << "mismatched parentheses\n";
        }
        output_queue.push_back(op_stack.top());
        op_stack.pop();
    }
    return output_queue;
}

double compute_binary_ops(double d1, double d2, std::string_view op)
{
    if (op == "*") return d1 * d2;
    else if (op == "+") return d1 + d2;
    else if (op == "-") return d1 - d2;
    else if (op == "/") return d1 / d2;
    else if (op == "^") return pow(d1, d2);
    else
    {
        std::cout << R"(invalid operator: ")" << op << R"("  passed to func "compute_binary_ops")" << '\n';
        exit(-1);
    }
}

double eval_rpn(const std::vector<std::variant<double, std::string>>& tokens, std::string var_name, double var_value)
{   
    double d2 = 0.0;
    double res = 0.0;
    std::stack<std::variant<double, std::string>> stack;
    for (const auto& tok : tokens)
    {
        if (const double *number = std::get_if<double>(&tok))
        {
            stack.push(*number);
        }
        else if (std::get<std::string>(tok) == var_name)
        {
            stack.push(var_value);
        }
        //handle binary operaters
        else if(is_binary_op(std::get<std::string>(tok)))
        {
            d2 = std::get<double>(stack.top());
            stack.pop();
            if (!stack.empty())
            {       
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = compute_binary_ops(d1, d2, std::get<std::string>(tok));
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
        //handle funcs(unary ops)
        else if (is_func(std::get<std::string>(tok)))
        {
            if (!stack.empty())
            {
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = (*unary_func_tbl.at(std::get<std::string>(tok)))(d1);
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
    }
    return std::get<double>(stack.top());
}
constexpr int LEFT_ASSOC = 0;
constexpr int RIGHT_ASSOC = 1;

//pair is <prec, assoc_id>
static const std::unordered_map<std::string_view, std::pair<int, int>> assoc_prec{ {"^", std::make_pair(4, RIGHT_ASSOC)},
                                                                                   {"*", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"/", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"+", std::make_pair(2, LEFT_ASSOC)},
                                                                                   {"-", std::make_pair(2, LEFT_ASSOC)} };

static const std::unordered_map<std::string_view, double(*)(double)> unary_func_tbl{ {"sin", &sin},
                                                                                     {"cos", &cos},
                                                                                     {"sqrt", &sqrt},
                                                                                     {"abs", &abs},
                                                                                     {"tan", &tan},
                                                                                     {"acos", &acos},
                                                                                     {"asin", &asin},
                                                                                     {"atan", &atan},
                                                                                     {"log", &log},
                                                                                     {"log10", &log10},
                                                                                     {"cosh", &cosh},
                                                                                     {"sinh", &sinh},
                                                                                     {"tanh", &tanh},
                                                                                     {"exp", &exp},
                                                                                     {"cbrt", &cbrt},
                                                                                     {"tgamma", &tgamma},
                                                                                     {"lgamma", &lgamma},
                                                                                     {"ceil", &ceil},
                                                                                     {"floor", &floor},
                                                                                     {"acosh", &acosh},
                                                                                     {"asinh", &asinh},
                                                                                     {"trunc", &trunc},
                                                                                     {"atanh", &atanh} };

static const std::unordered_set<std::string_view> funcs{ "sin", "tan", "acos", "asin", "asin", "abs",
                                                         "atan", "cosh", "sinh", "cos", "tanh",
                                                         "acosh", "asinh", "atanh", "exp", "ldexp",
                                                         "log", "log10", "sqrt", "cbrt", "tgamma",
                                                         "lgamma", "ceil", "floor", "trunc" };

bool is_left_assoc(std::string_view str)
{
    int id = assoc_prec.at(str).second;
    if (id == 0) return true;
    else return false;
}

bool is_binary_op(std::string_view str)
{
    if (str == "/" || str == "*" || str == "+" || str == "-" || str == "^")
    {
        return true;
    }
    else return false;
}

bool is_func(std::string_view str)
{
    if (funcs.count(str) > 0) return true;
    else return false;
}


//handls decimal numbers
bool is_num(std::string_view str)
{
    int num_found_periods = 0;
    for (const auto c : str)
    {
        if (c == '.')
        {
            num_found_periods++;
            if (num_found_periods > 1)
            {
                return false;
            }
        }
        if (!isdigit(c) && c != '.')
        {
            return false;
        }
    }
    return true;
}

int get_prec(std::string_view str)
{
    if (is_func(str))
    {
        return 1; //TODO: check it this is the correct value
    }
    else if (is_binary_op(str))
    {
        return assoc_prec.at(str).first;
    }
    else
    {
        return 0;
    }
}


//from https://stackoverflow.com/a/56204256
//modified regex expr
std::vector<std::string> tokenize(const std::string& str)
{
    std::vector<std::string> res;
    const std::regex words_regex("(sin|tan|acos|asin|abs|atan|cosh|sinh|cos|"
                                 "tanh|acosh|asinh|atanh|exp|ldexp|log|log10|"
                                 "sqrt|cbrt|tgamma|lgamma|ceil|floor|x|e)|^-|[0-9]?"
                                 "([0-9]*[.])?[0-9]+|[\\-\\+\\\\\(\\)\\/\\*\\^\\]",
                                  std::regex_constants::egrep);

    auto words_begin = std::sregex_iterator(str.begin(), str.end(), words_regex);
    auto words_end = std::sregex_iterator();
    for (std::sregex_iterator i = words_begin; i != words_end; ++i) 
    {
        res.push_back((*i).str());
    }
    return res;
}

//params: 
//str - string to be converted
//var_name - any occurances of this as a seperate token will be treated as a varable
//convert string in infix notation to a string in Reverse Polish Notation
//using dijkstra's shunting yard algorithm 
std::vector<std::variant<double, std::string>> s_yard(const std::string& str, std::string var_name)
{
    std::vector<std::variant<double, std::string>> output_queue;
    std::stack<std::string> op_stack;

    for (const auto& tok : tokenize(str))
    {
        if (tok == "pi")
        {
            output_queue.push_back(std::numbers::pi_v<double>);
        }
        else if (tok == "e")
        {
            output_queue.push_back(std::numbers::e_v<double>);
        }
        else if (tok == var_name)
        {
            output_queue.push_back(tok);
        }
        else if (is_num(tok))
        {
            output_queue.push_back(strtod(tok.c_str(), NULL));
        }
        else if (is_func(tok))
        {
            op_stack.push(tok);
        }
        else if (is_binary_op(tok))
        {   
            while (!op_stack.empty() && \
                  (is_binary_op(op_stack.top()) && get_prec(op_stack.top()) > (get_prec(tok)) || \
                  (get_prec(op_stack.top()) == get_prec(tok) && is_left_assoc(tok))) && \
                  (op_stack.top() != "("))
            {
                //pop operators from stack to queue
                while (!op_stack.empty())
                {
                    output_queue.push_back(op_stack.top());
                    op_stack.pop();
                }
            }
            op_stack.push(tok);
        }
        else if (tok == "(")
        {
            op_stack.push(tok);
        }
        else if (tok == ")")
        {
            while (op_stack.top() != "(")
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
            if (op_stack.top() == "(")
            {
                op_stack.pop();
            }
            if (is_func(op_stack.top()))
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
        }
    }
    //all tokens read
    while (!op_stack.empty())
    {
        //there are mismatched parentheses
        if (op_stack.top() == "(" || op_stack.top() == ")")
        {
            std::cout << "mismatched parentheses\n";
        }
        output_queue.push_back(op_stack.top());
        op_stack.pop();
    }
    return output_queue;
}

double compute_binary_ops(double d1, double d2, std::string_view op)
{
    if (op == "*") return d1 * d2;
    else if (op == "+") return d1 + d2;
    else if (op == "-") return d1 - d2;
    else if (op == "/") return d1 / d2;
    else if (op == "^") return pow(d1, d2);
    else
    {
        std::cout << R"(invalid operator: ")" << op << R"("  passed to func "compute_binary_ops")" << '\n';
        exit(-1);
    }
}

double eval_rpn(const std::vector<std::variant<double, std::string>>& tokens, std::string var_name, double var_value)
{   
    double d2 = 0.0;
    double res = 0.0;
    std::stack<std::variant<double, std::string>> stack;
    for (const auto& tok : tokens)
    {
        if (const double *number = std::get_if<double>(&tok))
        {
            stack.push(*number);
        }
        else if (std::get<std::string>(tok) == var_name)
        {
            stack.push(var_value);
        }
        //handle binary operaters
        else if(is_binary_op(std::get<std::string>(tok)))
        {
            d2 = std::get<double>(stack.top());
            stack.pop();
            if (!stack.empty())
            {       
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = compute_binary_ops(d1, d2, std::get<std::string>(tok));
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
        //handle funcs(unary ops)
        else if (is_func(std::get<std::string>(tok)))
        {
            if (!stack.empty())
            {
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = (*unary_func_tbl.at(std::get<std::string>(tok)))(d1);
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
    }
    return std::get<double>(stack.top());
}
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <string_view>
#include <string>
#include <vector>
#include <utility>
#include <stack>
#include <numbers>
#include <regex>
#include <variant>
#include <chrono>
#include <cmath>

constexpr int LEFT_ASSOC = 0;
constexpr int RIGHT_ASSOC = 1;

//pair is <prec, assoc_id>
static const std::unordered_map<std::string_view, std::pair<int, int>> assoc_prec{ {"^", std::make_pair(4, RIGHT_ASSOC)},
                                                                                   {"*", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"/", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"+", std::make_pair(2, LEFT_ASSOC)},
                                                                                   {"-", std::make_pair(2, LEFT_ASSOC)} };

static const std::unordered_map<std::string_view, double(*)(double)> unary_func_tbl{ {"sin", &sin},
                                                                                     {"cos", &cos},
                                                                                     {"sqrt", &sqrt},
                                                                                     {"abs", &abs},
                                                                                     {"tan", &tan},
                                                                                     {"acos", &acos},
                                                                                     {"asin", &asin},
                                                                                     {"atan", &atan},
                                                                                     {"log", &log},
                                                                                     {"log10", &log10},
                                                                                     {"cosh", &cosh},
                                                                                     {"sinh", &sinh},
                                                                                     {"tanh", &tanh},
                                                                                     {"exp", &exp},
                                                                                     {"cbrt", &cbrt},
                                                                                     {"tgamma", &tgamma},
                                                                                     {"lgamma", &lgamma},
                                                                                     {"ceil", &ceil},
                                                                                     {"floor", &floor},
                                                                                     {"acosh", &acosh},
                                                                                     {"asinh", &asinh},
                                                                                     {"trunc", &trunc},
                                                                                     {"atanh", &atanh} };

static const std::unordered_set<std::string_view> funcs{ "sin", "tan", "acos", "asin", "asin", "abs",
                                                         "atan", "cosh", "sinh", "cos", "tanh",
                                                         "acosh", "asinh", "atanh", "exp", "ldexp",
                                                         "log", "log10", "sqrt", "cbrt", "tgamma",
                                                         "lgamma", "ceil", "floor", "trunc" };

bool is_left_assoc(std::string_view str)
{
    int id = assoc_prec.at(str).second;
    if (id == 0) return true;
    else return false;
}

bool is_binary_op(std::string_view str)
{
    if (str == "/" || str == "*" || str == "+" || str == "-" || str == "^")
    {
        return true;
    }
    else return false;
}

bool is_func(std::string_view str)
{
    if (funcs.count(str) > 0) return true;
    else return false;
}


//handls decimal numbers
bool is_num(std::string_view str)
{
    int num_found_periods = 0;
    for (const auto c : str)
    {
        if (c == '.')
        {
            num_found_periods++;
            if (num_found_periods > 1)
            {
                return false;
            }
        }
        if (!isdigit(c) && c != '.')
        {
            return false;
        }
    }
    return true;
}

int get_prec(std::string_view str)
{
    if (is_func(str))
    {
        return 1; //TODO: check it this is the correct value
    }
    else if (is_binary_op(str))
    {
        return assoc_prec.at(str).first;
    }
    else
    {
        return 0;
    }
}


//from https://stackoverflow.com/a/56204256
//modified regex expr
std::vector<std::string> tokenize(const std::string& str)
{
    std::vector<std::string> res;
    const std::regex words_regex("(sin|tan|acos|asin|abs|atan|cosh|sinh|cos|"
                                 "tanh|acosh|asinh|atanh|exp|ldexp|log|log10|"
                                 "sqrt|cbrt|tgamma|lgamma|ceil|floor|x|e)|^-|[0-9]?"
                                 "([0-9]*[.])?[0-9]+|[\\-\\+\\\\\(\\)\\/\\*\\^\\]",
                                  std::regex_constants::egrep);

    auto words_begin = std::sregex_iterator(str.begin(), str.end(), words_regex);
    auto words_end = std::sregex_iterator();
    for (std::sregex_iterator i = words_begin; i != words_end; ++i) 
    {
        res.push_back((*i).str());
    }
    return res;
}

//params: 
//str - string to be converted
//var_name - any occurances of this as a seperate token will be treated as a varable
//convert string in infix notation to a string in Reverse Polish Notation
//using dijkstra's shunting yard algorithm 
std::vector<std::variant<double, std::string>> s_yard(const std::string& str, std::string var_name)
{
    std::vector<std::variant<double, std::string>> output_queue;
    std::stack<std::string> op_stack;

    for (const auto& tok : tokenize(str))
    {
        if (tok == "pi")
        {
            output_queue.push_back(std::numbers::pi_v<double>);
        }
        else if (tok == "e")
        {
            output_queue.push_back(std::numbers::e_v<double>);
        }
        else if (tok == var_name)
        {
            output_queue.push_back(tok);
        }
        else if (is_num(tok))
        {
            output_queue.push_back(strtod(tok.c_str(), NULL));
        }
        else if (is_func(tok))
        {
            op_stack.push(tok);
        }
        else if (is_binary_op(tok))
        {   
            while (!op_stack.empty() && \
                  (is_binary_op(op_stack.top()) && get_prec(op_stack.top()) > (get_prec(tok)) || \
                  (get_prec(op_stack.top()) == get_prec(tok) && is_left_assoc(tok))) && \
                  (op_stack.top() != "("))
            {
                //pop operators from stack to queue
                while (!op_stack.empty())
                {
                    output_queue.push_back(op_stack.top());
                    op_stack.pop();
                }
            }
            op_stack.push(tok);
        }
        else if (tok == "(")
        {
            op_stack.push(tok);
        }
        else if (tok == ")")
        {
            while (op_stack.top() != "(")
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
            if (op_stack.top() == "(")
            {
                op_stack.pop();
            }
            if (is_func(op_stack.top()))
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
        }
    }
    //all tokens read
    while (!op_stack.empty())
    {
        //there are mismatched parentheses
        if (op_stack.top() == "(" || op_stack.top() == ")")
        {
            std::cout << "mismatched parentheses\n";
        }
        output_queue.push_back(op_stack.top());
        op_stack.pop();
    }
    return output_queue;
}

double compute_binary_ops(double d1, double d2, std::string_view op)
{
    if (op == "*") return d1 * d2;
    else if (op == "+") return d1 + d2;
    else if (op == "-") return d1 - d2;
    else if (op == "/") return d1 / d2;
    else if (op == "^") return pow(d1, d2);
    else
    {
        std::cout << R"(invalid operator: ")" << op << R"("  passed to func "compute_binary_ops")" << '\n';
        exit(-1);
    }
}

double eval_rpn(const std::vector<std::variant<double, std::string>>& tokens, std::string var_name, double var_value)
{   
    double d2 = 0.0;
    double res = 0.0;
    std::stack<std::variant<double, std::string>> stack;
    for (const auto& tok : tokens)
    {
        if (const double *number = std::get_if<double>(&tok))
        {
            stack.push(*number);
        }
        else if (std::get<std::string>(tok) == var_name)
        {
            stack.push(var_value);
        }
        //handle binary operaters
        else if(is_binary_op(std::get<std::string>(tok)))
        {
            d2 = std::get<double>(stack.top());
            stack.pop();
            if (!stack.empty())
            {       
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = compute_binary_ops(d1, d2, std::get<std::string>(tok));
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
        //handle funcs(unary ops)
        else if (is_func(std::get<std::string>(tok)))
        {
            if (!stack.empty())
            {
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = (*unary_func_tbl.at(std::get<std::string>(tok)))(d1);
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
    }
    return std::get<double>(stack.top());
}
added 6774 characters in body
Source Link
constexpr int LEFT_ASSOC = 0;
constexpr int RIGHT_ASSOC = 1;

//pair is <prec, assoc_id>
static const std::vector<stdunordered_map<std::variant<doublestring_view, std::string>>pair<int, s_yardint>> assoc_prec{ {"^", std::make_pair(4, RIGHT_ASSOC)},
                                                                                   {"*", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"/", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"+", std::make_pair(2, LEFT_ASSOC)},
                                                                                   {"-", std::make_pair(2, LEFT_ASSOC)} };

static const std::string&unordered_map<std::string_view, strdouble(*)(double)> unary_func_tbl{ {"sin", &sin},
                                                                                     {"cos", &cos},
                                                                                     {"sqrt", &sqrt},
                                                                                     {"abs", &abs},
                                                                                     {"tan", &tan},
                                                                                     {"acos", &acos},
                                                                                     {"asin", &asin},
                                                                                     {"atan", &atan},
                                                                                     {"log", &log},
                                                                                     {"log10", &log10},
                                                                                     {"cosh", &cosh},
                                                                                     {"sinh", &sinh},
                                                                                     {"tanh", &tanh},
                                                                                     {"exp", &exp},
                                                                                     {"cbrt", &cbrt},
                                                                                     {"tgamma", &tgamma},
                                                                                     {"lgamma", &lgamma},
                                                                                     {"ceil", &ceil},
                                                                                     {"floor", &floor},
                                                                                     {"acosh", &acosh},
                                                                                     {"asinh", &asinh},
                                                                                     {"trunc", &trunc},
                                                                                     {"atanh", &atanh} };

static const std::stringunordered_set<std::string_view> var_namefuncs{ "sin", "tan", "acos", "asin", "asin", "abs",
                                                         "atan", "cosh", "sinh", "cos", "tanh",
                                                         "acosh", "asinh", "atanh", "exp", "ldexp",
                                                         "log", "log10", "sqrt", "cbrt", "tgamma",
                                                         "lgamma", "ceil", "floor", "trunc" };

bool is_left_assoc(std::string_view str)
{
    int id = assoc_prec.at(str).second;
    if (id == 0) return true;
    else return false;
}

bool is_binary_op(std::string_view str)
{
    if (str == "/" || str == "*" || str == "+" || str == "-" || str == "^")
    {
        return true;
    }
    else return false;
}

bool is_func(std::string_view str)
{
    if (funcs.count(str) > 0) return true;
    else return false;
}


//handls decimal numbers
bool is_num(std::string_view str)
{
    int num_found_periods = 0;
    for (const auto c : str)
    {
        if (c == '.')
        {
            num_found_periods++;
            if (num_found_periods > 1)
            {
                return false;
            }
        }
        if (!isdigit(c) && c != '.')
        {
            return false;
        }
    }
    return true;
}

int get_prec(std::string_view str)
{
    if (is_func(str))
    {
        return 1; //TODO: check it this is the correct value
    }
    else if (is_binary_op(str))
    {
        return assoc_prec.at(str).first;
    }
    else
    {
        return 0;
    }
}


//from https://stackoverflow.com/a/56204256
//modified regex expr
std::vector<std::string> tokenize(const std::string& str)
{
    std::vector<std::string> res;
    const std::regex words_regex("(sin|tan|acos|asin|abs|atan|cosh|sinh|cos|"
                                 "tanh|acosh|asinh|atanh|exp|ldexp|log|log10|"
                                 "sqrt|cbrt|tgamma|lgamma|ceil|floor|x|e)|^-|[0-9]?"
                                 "([0-9]*[.])?[0-9]+|[\\-\\+\\\\\(\\)\\/\\*\\^\\]",
                                  std::regex_constants::egrep);

    auto words_begin = std::sregex_iterator(str.begin(), str.end(), words_regex);
    auto words_end = std::sregex_iterator();
    for (std::sregex_iterator i = words_begin; i != words_end; ++i) 
    {
        res.push_back((*i).str());
    }
    return res;
}

//params: 
//str - string to be converted
//var_name - any occurances of this as a seperate token will be treated as a varable
//convert string in infix notation to a string in Reverse Polish Notation
//using dijkstra's shunting yard algorithm 
std::vector<std::variant<double, std::string>> s_yard(const std::string& str, std::string var_name)
{
    std::vector<std::variant<double, std::string>> output_queue;
    std::stack<std::string> op_stack;

    for (const auto& tok : tokenize(str))
    {
        if (tok == "pi")
        {
            output_queue.push_back(std::numbers::pi_v<double>);
        }
        else if (tok == "e")
        {
            output_queue.push_back(std::numbers::e_v<double>);
        }
        else if (tok == var_name)
        {
            output_queue.push_back(tok);
        }
        else if (is_num(tok))
        {
            output_queue.push_back(strtod(tok.c_str(), NULL));
        }
        else if (is_func(tok))
        {
            op_stack.push(tok);
        }
        else if (is_binary_op(tok))
        {   
            while (!op_stack.empty() && \
                  (is_binary_op(op_stack.top()) && get_prec(op_stack.top()) > (get_prec(tok)) || \
                  (get_prec(op_stack.top()) == get_prec(tok) && is_left_assoc(tok))) && \
                  (op_stack.top() != "("))
            {
                //pop operators from stack to queue
                while (!op_stack.empty())
                {
                    output_queue.push_back(op_stack.top());
                    op_stack.pop();
                }
            }
            op_stack.push(tok);
        }
        else if (tok == "(")
        {
            op_stack.push(tok);
        }
        else if (tok == ")")
        {
            while (op_stack.top() != "(")
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
            if (op_stack.top() == "(")
            {
                op_stack.pop();
            }
            if (is_func(op_stack.top()))
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
        }
    }
    //all tokens read
    while (!op_stack.empty())
    {
        //there are mismatched parentheses
        if (op_stack.top() == "(" || op_stack.top() == ")")
        {
            std::cout << "mismatched parentheses\n";
        }
        output_queue.push_back(op_stack.top());
        op_stack.pop();
    }
    return output_queue;
}

double compute_binary_ops(double d1, double d2, std::string_view op)
{
    if (op == "*") return d1 * d2;
    else if (op == "+") return d1 + d2;
    else if (op == "-") return d1 - d2;
    else if (op == "/") return d1 / d2;
    else if (op == "^") return pow(d1, d2);
    else
    {
        std::cout << R"(invalid operator: ")" << op << R"("  passed to func "compute_binary_ops")" << '\n';
        exit(-1);
    }
}

double eval_rpn(const std::vector<std::variant<double, std::string>>& tokens, std::string var_name, double var_value)
{   
    double d2 = 0.0;
    double res = 0.0;
    std::stack<std::variant<double, std::string>> stack;
    for (const auto& tok : tokens)
    {
        if (const double *number = std::get_if<double>(&tok))
        {
            stack.push(*number);
        }
        else if (std::get<std::string>(tok) == var_name)
        {
            stack.push(var_value);
        }
        //handle binary operaters
        else if(is_binary_op(std::get<std::string>(tok)))
        {
            d2 = std::get<double>(stack.top());
            stack.pop();
            if (!stack.empty())
            {       
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = compute_binary_ops(d1, d2, std::get<std::string>(tok));
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
        //handle funcs(unary ops)
        else if (is_func(std::get<std::string>(tok)))
        {
            if (!stack.empty())
            {
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = (*unary_func_tbl.at(std::get<std::string>(tok)))(d1);
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
    }
    return std::get<double>(stack.top());
}
std::vector<std::variant<double, std::string>> s_yard(const std::string& str, std::string var_name)
{
    std::vector<std::variant<double, std::string>> output_queue;
    std::stack<std::string> op_stack;

    for (const auto& tok : tokenize(str))
    {
        if (tok == "pi")
        {
            output_queue.push_back(std::numbers::pi_v<double>);
        }
        else if (tok == "e")
        {
            output_queue.push_back(std::numbers::e_v<double>);
        }
        else if (tok == var_name)
        {
            output_queue.push_back(tok);
        }
        else if (is_num(tok))
        {
            output_queue.push_back(strtod(tok.c_str(), NULL));
        }
        else if (is_func(tok))
        {
            op_stack.push(tok);
        }
        else if (is_binary_op(tok))
        {   
            while (!op_stack.empty() && \
                  (is_binary_op(op_stack.top()) && get_prec(op_stack.top()) > (get_prec(tok)) || \
                  (get_prec(op_stack.top()) == get_prec(tok) && is_left_assoc(tok))) && \
                  (op_stack.top() != "("))
            {
                //pop operators from stack to queue
                while (!op_stack.empty())
                {
                    output_queue.push_back(op_stack.top());
                    op_stack.pop();
                }
            }
            op_stack.push(tok);
        }
        else if (tok == "(")
        {
            op_stack.push(tok);
        }
        else if (tok == ")")
        {
            while (op_stack.top() != "(")
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
            if (op_stack.top() == "(")
            {
                op_stack.pop();
            }
            if (is_func(op_stack.top()))
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
        }
    }
    //all tokens read
    while (!op_stack.empty())
    {
        //there are mismatched parentheses
        if (op_stack.top() == "(" || op_stack.top() == ")")
        {
            std::cout << "mismatched parentheses\n";
        }
        output_queue.push_back(op_stack.top());
        op_stack.pop();
    }
    return output_queue;
}

double eval_rpn(const std::vector<std::variant<double, std::string>>& tokens, std::string var_name, double var_value)
{   
    double d2 = 0.0;
    double res = 0.0;
    std::stack<std::variant<double, std::string>> stack;
    for (const auto& tok : tokens)
    {
        if (const double *number = std::get_if<double>(&tok))
        {
            stack.push(*number);
        }
        else if (std::get<std::string>(tok) == var_name)
        {
            stack.push(var_value);
        }
        //handle binary operaters
        else if(is_binary_op(std::get<std::string>(tok)))
        {
            d2 = std::get<double>(stack.top());
            stack.pop();
            if (!stack.empty())
            {       
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = compute_binary_ops(d1, d2, std::get<std::string>(tok));
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
        //handle funcs(unary ops)
        else if (is_func(std::get<std::string>(tok)))
        {
            if (!stack.empty())
            {
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = (*unary_func_tbl.at(std::get<std::string>(tok)))(d1);
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
    }
    return std::get<double>(stack.top());
}
constexpr int LEFT_ASSOC = 0;
constexpr int RIGHT_ASSOC = 1;

//pair is <prec, assoc_id>
static const std::unordered_map<std::string_view, std::pair<int, int>> assoc_prec{ {"^", std::make_pair(4, RIGHT_ASSOC)},
                                                                                   {"*", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"/", std::make_pair(3, LEFT_ASSOC)},
                                                                                   {"+", std::make_pair(2, LEFT_ASSOC)},
                                                                                   {"-", std::make_pair(2, LEFT_ASSOC)} };

static const std::unordered_map<std::string_view, double(*)(double)> unary_func_tbl{ {"sin", &sin},
                                                                                     {"cos", &cos},
                                                                                     {"sqrt", &sqrt},
                                                                                     {"abs", &abs},
                                                                                     {"tan", &tan},
                                                                                     {"acos", &acos},
                                                                                     {"asin", &asin},
                                                                                     {"atan", &atan},
                                                                                     {"log", &log},
                                                                                     {"log10", &log10},
                                                                                     {"cosh", &cosh},
                                                                                     {"sinh", &sinh},
                                                                                     {"tanh", &tanh},
                                                                                     {"exp", &exp},
                                                                                     {"cbrt", &cbrt},
                                                                                     {"tgamma", &tgamma},
                                                                                     {"lgamma", &lgamma},
                                                                                     {"ceil", &ceil},
                                                                                     {"floor", &floor},
                                                                                     {"acosh", &acosh},
                                                                                     {"asinh", &asinh},
                                                                                     {"trunc", &trunc},
                                                                                     {"atanh", &atanh} };

static const std::unordered_set<std::string_view> funcs{ "sin", "tan", "acos", "asin", "asin", "abs",
                                                         "atan", "cosh", "sinh", "cos", "tanh",
                                                         "acosh", "asinh", "atanh", "exp", "ldexp",
                                                         "log", "log10", "sqrt", "cbrt", "tgamma",
                                                         "lgamma", "ceil", "floor", "trunc" };

bool is_left_assoc(std::string_view str)
{
    int id = assoc_prec.at(str).second;
    if (id == 0) return true;
    else return false;
}

bool is_binary_op(std::string_view str)
{
    if (str == "/" || str == "*" || str == "+" || str == "-" || str == "^")
    {
        return true;
    }
    else return false;
}

bool is_func(std::string_view str)
{
    if (funcs.count(str) > 0) return true;
    else return false;
}


//handls decimal numbers
bool is_num(std::string_view str)
{
    int num_found_periods = 0;
    for (const auto c : str)
    {
        if (c == '.')
        {
            num_found_periods++;
            if (num_found_periods > 1)
            {
                return false;
            }
        }
        if (!isdigit(c) && c != '.')
        {
            return false;
        }
    }
    return true;
}

int get_prec(std::string_view str)
{
    if (is_func(str))
    {
        return 1; //TODO: check it this is the correct value
    }
    else if (is_binary_op(str))
    {
        return assoc_prec.at(str).first;
    }
    else
    {
        return 0;
    }
}


//from https://stackoverflow.com/a/56204256
//modified regex expr
std::vector<std::string> tokenize(const std::string& str)
{
    std::vector<std::string> res;
    const std::regex words_regex("(sin|tan|acos|asin|abs|atan|cosh|sinh|cos|"
                                 "tanh|acosh|asinh|atanh|exp|ldexp|log|log10|"
                                 "sqrt|cbrt|tgamma|lgamma|ceil|floor|x|e)|^-|[0-9]?"
                                 "([0-9]*[.])?[0-9]+|[\\-\\+\\\\\(\\)\\/\\*\\^\\]",
                                  std::regex_constants::egrep);

    auto words_begin = std::sregex_iterator(str.begin(), str.end(), words_regex);
    auto words_end = std::sregex_iterator();
    for (std::sregex_iterator i = words_begin; i != words_end; ++i) 
    {
        res.push_back((*i).str());
    }
    return res;
}

//params: 
//str - string to be converted
//var_name - any occurances of this as a seperate token will be treated as a varable
//convert string in infix notation to a string in Reverse Polish Notation
//using dijkstra's shunting yard algorithm 
std::vector<std::variant<double, std::string>> s_yard(const std::string& str, std::string var_name)
{
    std::vector<std::variant<double, std::string>> output_queue;
    std::stack<std::string> op_stack;

    for (const auto& tok : tokenize(str))
    {
        if (tok == "pi")
        {
            output_queue.push_back(std::numbers::pi_v<double>);
        }
        else if (tok == "e")
        {
            output_queue.push_back(std::numbers::e_v<double>);
        }
        else if (tok == var_name)
        {
            output_queue.push_back(tok);
        }
        else if (is_num(tok))
        {
            output_queue.push_back(strtod(tok.c_str(), NULL));
        }
        else if (is_func(tok))
        {
            op_stack.push(tok);
        }
        else if (is_binary_op(tok))
        {   
            while (!op_stack.empty() && \
                  (is_binary_op(op_stack.top()) && get_prec(op_stack.top()) > (get_prec(tok)) || \
                  (get_prec(op_stack.top()) == get_prec(tok) && is_left_assoc(tok))) && \
                  (op_stack.top() != "("))
            {
                //pop operators from stack to queue
                while (!op_stack.empty())
                {
                    output_queue.push_back(op_stack.top());
                    op_stack.pop();
                }
            }
            op_stack.push(tok);
        }
        else if (tok == "(")
        {
            op_stack.push(tok);
        }
        else if (tok == ")")
        {
            while (op_stack.top() != "(")
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
            if (op_stack.top() == "(")
            {
                op_stack.pop();
            }
            if (is_func(op_stack.top()))
            {
                output_queue.push_back(op_stack.top());
                op_stack.pop();
            }
        }
    }
    //all tokens read
    while (!op_stack.empty())
    {
        //there are mismatched parentheses
        if (op_stack.top() == "(" || op_stack.top() == ")")
        {
            std::cout << "mismatched parentheses\n";
        }
        output_queue.push_back(op_stack.top());
        op_stack.pop();
    }
    return output_queue;
}

double compute_binary_ops(double d1, double d2, std::string_view op)
{
    if (op == "*") return d1 * d2;
    else if (op == "+") return d1 + d2;
    else if (op == "-") return d1 - d2;
    else if (op == "/") return d1 / d2;
    else if (op == "^") return pow(d1, d2);
    else
    {
        std::cout << R"(invalid operator: ")" << op << R"("  passed to func "compute_binary_ops")" << '\n';
        exit(-1);
    }
}

double eval_rpn(const std::vector<std::variant<double, std::string>>& tokens, std::string var_name, double var_value)
{   
    double d2 = 0.0;
    double res = 0.0;
    std::stack<std::variant<double, std::string>> stack;
    for (const auto& tok : tokens)
    {
        if (const double *number = std::get_if<double>(&tok))
        {
            stack.push(*number);
        }
        else if (std::get<std::string>(tok) == var_name)
        {
            stack.push(var_value);
        }
        //handle binary operaters
        else if(is_binary_op(std::get<std::string>(tok)))
        {
            d2 = std::get<double>(stack.top());
            stack.pop();
            if (!stack.empty())
            {       
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = compute_binary_ops(d1, d2, std::get<std::string>(tok));
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
        //handle funcs(unary ops)
        else if (is_func(std::get<std::string>(tok)))
        {
            if (!stack.empty())
            {
                const double d1 = std::get<double>(stack.top());
                stack.pop();
                res = (*unary_func_tbl.at(std::get<std::string>(tok)))(d1);
                stack.push(res);
            }
            else
            {
                if (std::get<std::string>(tok) == "-") res = -(d2);
                else res = d2;
                stack.push(res);
            }
        }
    }
    return std::get<double>(stack.top());
}
added 82 characters in body
Source Link

I will post the parser that creates a rpn representation of an input string and evala function that evaluates the rpn here(the rest is on the github):

I will post the parser and eval here(the rest is on the github):

I will post the parser that creates a rpn representation of an input string and a function that evaluates the rpn here(the rest is on the github):

Source Link
Loading