[项目分享]c++代码计算器

2023-7-4 11:30:02 [显示全部楼层]
4202浏览
查看: 4202|回复: 8

[项目分享] c++代码计算器

[复制链接]
template <char...> struct char_vector { using vec_type = char_vector; };
template<typename t, t... cs>constexpr char_vector<cs...> operator ""_str() { return {}; }
然后我们需要两个栈来保存操作数和操作符。第一个栈是 int 类型的,第二个栈是 char 类型的。我们把栈写好。
template <char...> struct char_vector { using vec_type = char_vector; };template <typename> struct pop_char {};template <char c, char... cs>struct pop_char<char_vector<c, cs...>> : std::integral_constant<char, c>, char_vector<cs...> {};template <char, typename> struct push_char {};template <char c, char... cs>struct push_char<c, char_vector<cs...>> : char_vector<c, cs...> {};template <int...> struct int_vector { using vec_type = int_vector; };template <typename> struct pop_int {};template <int i, int... is>struct pop_int<int_vector<i, is...>> : std::integral_constant<int, i>, int_vector<is...> {};template <int, typename> struct push_int {};template <int i, int... is>struct push_int<i, int_vector<is...>> : int_vector<i, is...> {};
好了。接下来我们考虑做一个状态机。每一个状态都是 { 操作数栈,操作符栈,输入流 } 的集合。template <typename ns, typename os, typename s>struct state{    using num_stack = ns;    using op_stack = os;    using stream = s;};
然后我们就可以 parse char 啦。这部分参照网上,很多资料都会讲如何用两个栈求中序表达式的值。我就不赘述啦。直接看代码吧。template <int... is, char... cs1, char c, char... cs2>constexpr auto eval_once(state<int_vector<is...>, char_vector<cs1...>,    char_vector<c, cs2...>>){    if constexpr (c == ' ')    {        return state<int_vector<is...>, char_vector<cs1...>,            char_vector<cs2...>>{};    }    else if constexpr (c >= '0' && c <= '9')    {        using merge_type = decltype(merge(char_vector<c, cs2...>{}));        using ops = typename push_int<merge_type::value, int_vector<is...>>::vec_type;        return state<ops, char_vector<cs1...>, typename merge_type::type>{};    }    else if constexpr (c == '(')    {        return state<int_vector<is...>, char_vector<c, cs1...>,            char_vector<cs2...>>{};    }    else if constexpr (c == ')')    {        return calc_brk(state<int_vector<is...>, char_vector<cs1...>,            char_vector<c, cs2...>>{});    }    else if constexpr (c == '+' || c == '-' || c == '*' || c == '/')    {        if constexpr (sizeof...(cs1) == 0)            return state<int_vector<is...>, char_vector<c, cs1...>,            char_vector<cs2...>>{};        else if constexpr (((pop_char<char_vector<cs1...>>::value == '+' ||            pop_char<char_vector<cs1...>>::value == '-') &&            (c == '*' || c == '/')) ||            pop_char<char_vector<cs1...>>::value == '(')            return state<int_vector<is...>, char_vector<c, cs1...>,            char_vector<cs2...>>{};        else            return calc_op(state<int_vector<is...>, char_vector<cs1...>,                char_vector<c, cs2...>>{});    }}
这个 eval once 来 parse 当前的字符,并且进行相应的操作。其中 merge 操作是合并字符变成一个数字的。template <int ret = 0, char c, char... cs>constexpr auto merge(char_vector<c, cs...>){    if constexpr (c >= '0' && c <= '9')        return merge<10 * ret + (c - '0')>(char_vector<cs...>{});    else        return merge_pair<ret, char_vector<c, cs...>>{};}template <int ret = 0>constexpr auto merge(char_vector<>){    return merge_pair<ret, char_vector<>>{};}
其中 merge pair 就是 { ret, stream } 的集合。这里的 stream 是合并完之后剩的流。template <int n, typename t>struct merge_pair{    static const int value = n;    using type = t;};
我们的 eval once 还用到了两个 calc 方法。其实就是遇到 ) 和优先级低的操作符的时候要进行的计算。
template <int... is, char... cs1, char c, char... cs2>constexpr auto calc_brk(state<int_vector<is...>, char_vector<cs1...>,    char_vector<c, cs2...>>){    if constexpr (pop_char<char_vector<cs1...>>::value == '(')        return state<int_vector<is...>,        typename pop_char<char_vector<cs1...>>::vec_type,        char_vector<cs2...>>{};    else        return calc_brk(calc_once<pop_char<char_vector<cs1...>>::value>(            state<int_vector<is...>, typename pop_char<char_vector<cs1...>>::vec_type,            char_vector<c, cs2...>>{}));}template <int... is, char... cs1, char c, char... cs2>constexpr auto calc_op(state<int_vector<is...>, char_vector<cs1...>,    char_vector<c, cs2...>>){    return calc_once<pop_char<char_vector<cs1...>>::value>(        state<int_vector<is...>,        typename push_char<c, typename pop_char<char_vector<cs1...>>::vec_type>::vec_type,        char_vector<cs2...>>{});}
其中 calc once 就是根据传入的操作符,从操作数栈里弹出两个操作数计算,然后把结果放入操作数栈里。
template <char op, int i1, int i2, int... is, char... cs1, char... cs2>constexpr auto calc_once(state<int_vector<i1, i2, is...>, char_vector<cs1...>,    char_vector<cs2...>>){    if constexpr (op == '+')        return state<int_vector<i2 + i1, is...>, char_vector<cs1...>,        char_vector<cs2...>>{};    else if constexpr (op == '-')        return state<int_vector<i2 - i1, is...>, char_vector<cs1...>,        char_vector<cs2...>>{};    else if constexpr (op == '*')        return state<int_vector<i2 * i1, is...>, char_vector<cs1...>,        char_vector<cs2...>>{};    else if constexpr (op == '/')        return state<int_vector<i2 / i1, is...>, char_vector<cs1...>,        char_vector<cs2...>>{};}
嗯。我们的 eval once 就解释完了。接下来我们需要一个递归函数启动 eval once。template <int... is, char... cs1, char... cs2>constexpr auto eval_all(state<int_vector<is...>, char_vector<cs1...>,    char_vector<cs2...>>){    if constexpr (sizeof...(cs2) == 0)        return state<int_vector<is...>, char_vector<cs1...>,        char_vector<cs2...>>{};    else        return eval_all(eval_once(state<int_vector<is...>, char_vector<cs1...>,            char_vector<cs2...>>{}));}
这个 eval all 函数计算完之后,流就空了。只剩下操作数和操作符两个栈了。我们最后写一个 calc last 函数计算这两个栈的最后结果。
template <int... is, char... cs>constexpr int calc_last(state<int_vector<is...>, char_vector<cs...>,    char_vector<>>){    if constexpr (sizeof...(cs) == 0)        return pop_int<int_vector<is...>>::value;    else        return calc_last(calc_once<pop_char<char_vector<cs...>>::value>(            state<int_vector<is...>,            typename pop_char<char_vector<cs...>>::vec_type,            char_vector<>>{}));}
好啦。最后我们写一个 eval 函数把它们组合起来。
template <typename t>constexpr int eval(t){    if constexpr (std::is_same_v<t, char_vector<>>)        return 0;    else        return calc_last(eval_all(state<int_vector<>, char_vector<>, t>{}));}
现在我们可以写这样的代码了。







#include <boost/spirit/home/x3.hpp>#include <boost/fusion/include/adapt_struct.hpp>#include <boost/fusion/include/io.hpp>namespace x3 = boost::spirit::x3;using x3::double_;using x3::_attr;using x3::space;using x3::phrase_parse;using x3::_val;
然后,定义赋值、加减乘除的action:
auto assign_ = [](auto& ctx) {_val(ctx) = _attr(ctx);};auto add_ = [](auto& ctx) { _val(ctx) += _attr(ctx);};auto sub_ = [](auto& ctx) { _val(ctx) -= _attr(ctx);};auto mul_ = [](auto& ctx) { _val(ctx) *= _attr(ctx);};auto div_ = [](auto& ctx) { _val(ctx) /= _attr(ctx);};
很自然对不对,然后根据ebnf定义,做个猴子都会的填空题:
auto const expression_def =    term[assign_]    >> *(   ('+' >> term            [add_])        |   ('-' >> term            [sub_])        )    ;auto const term_def =    factor[assign_]    >> *(   ('*' >> factor          [mul_])        |   ('/' >> factor          [div_])        )    ;auto const factor_def =    double_    |   '(' >> expression >> ')'    ;BOOST_SPIRIT_DEFINE(    expression  , term  , factor);
最后补充个主函数就OK了
int main(){    std::string line;    while (true) {        std::getline(std::cin, line);        if (line == "q") {            break;        }        auto it = line.begin();        auto const end = line.end();        double result = 0.0;        bool ret = phrase_parse(it, end, expression, space, result);        if (ret && it == end) {            std::cout << result << std::endl;        } else {            std::cout <<std::string("failed to parse at: \"").append(std::string(it, end)).append("\"") << std::endl;;        }    }    return 0;}






#include<cctype>#include<iostream>#include<map>#include<string>using namespace std;enum Token_value{    NAME,NUMBER,END,PLUS='+',MINUS='-',MUL='*',DIV='/',PRINT=';',ASSIGN='=',LP='(',RP=')'};Token_value curr_tok=PRINT;map<string,double> table;double number_value;string string_value;int no_of_errors;double expr(bool get);double term(bool get);double prim(bool get);Token_value get_token();double error(const string& s){    no_of_errors++;    cerr<<"error:"<<s<<endl;    return 1;}Token_value get_token(){    char ch=0;    cin>>ch;    switch (ch) {        case 0:            return curr_tok=END;        case ';':case '*':case '/':case '+':case '-':case '(':case ')':case '=':            return curr_tok=Token_value(ch);        case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':case '.':            cin.putback(ch);            cin>>number_value;            return curr_tok=NUMBER;        default:            if (isalpha(ch)) {                cin.putback(ch);                cin>>string_value;                return curr_tok=NAME;            }            error("bad token");            return curr_tok=PRINT;    }}double prim(bool get){    if (get) get_token();    switch (curr_tok) {        case NUMBER:        {   double v=number_value;            get_token();            return v;        }        case NAME:        {   double& v=table[string_value];            if (get_token()==ASSIGN) v=expr(true);            return v;        }        case MINUS:            return -prim(true);        case LP:        {   double e=expr(true);            if (curr_tok!=RP) return error(") expected");            get_token();            return e;        }        default:            return error("primary expected");    }}double term(bool get){    double left=prim(get);    for (;;)        switch (curr_tok) {            case MUL:                left*=prim(true);                break;            case DIV:               if (double d=prim(true)) {                   left/=d;                   break;               }               return error("divide by 0");            default:                return left;         }}double expr(bool get){    double left=term(get);    for(;;)        switch(curr_tok) {            case PLUS:                left+=term(true);                break;            case MINUS:                left-=term(true);                break;            default:                return left;        }}int main(){    table["pi"]=3.1415926535897932385;    table["e"]=2.718284590452354;    while (cin) {        get_token();        if (curr_tok==END) break;        if (curr_tok==PRINT) continue;        cout<<expr(false)<<endl;    }    return no_of_errors;}







三春牛-创客  初级技神

发表于 2023-7-7 22:59:00

这个厉害!
回复

使用道具 举报

三春牛-创客  初级技神

发表于 2023-7-7 23:00:09

挺多的,但是没学过c++,看不懂......
回复

使用道具 举报

ETO.  见习技师
 楼主|

发表于 2023-7-14 14:12:38

三春牛-创客 发表于 2023-7-7 23:00
挺多的,但是没学过c++,看不懂......

可以学一下
回复

使用道具 举报

三春牛-创客  初级技神

发表于 2023-7-14 18:36:06


我也想。
回复

使用道具 举报

三春牛-创客  初级技神

发表于 2023-7-14 18:38:15


不过我要先学Python。
回复

使用道具 举报

花生编程  中级技匠

发表于 2023-7-16 22:08:55

不错不错
回复

使用道具 举报

花生编程  中级技匠

发表于 2023-7-16 22:16:52

赞赞赞赞赞
回复

使用道具 举报

快看擎天猪  中级技师

发表于 2023-7-18 17:53:04

不错不错
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
关于楼主
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail