C++ 一个简单的解释器 - 四则运算

#include <list>
using namespace std;

/*
─────────────────────────────
│ 10 │  + │  20 │  *  |  5  |
─────────────────────────────

(run interpret)
===>>>
Token[Digits]   : 10
Token[Operator] : +
Token[Digits]   : 20
Token[Operator] : *
Token[Digits]   : 5

===>>>
10 + 20 * 5

优先级:
x÷ > +-
===>>>
10 + 100

===>>>
110

*/


//
// 配置
//
const char CounterDigits[]    = "1234567890";
const char CounterOperators[] = "*/+-";
const char CounterNils[]      = " ";

enum TokensTypes
{
    Nil,
    Operator,
    Digits,
    Eof,
    Err
};

//
// common
//
inline bool isinchars( const char* str, char s ) {
    int len = strlen( str );
    for( int i = 0; i < len; i++ )
        if( s == str[i] )
            return true;
    return false;
}
inline bool isDigit( char s ) {
    return isinchars( CounterDigits, s );
}
inline bool isOperator( char s ) {
    return isinchars( CounterOperators, s );
}
inline bool isNil( char s ) {
    return isinchars( CounterNils, s );
}

//
// 类实现
//
class Token
{
public:
    Token( TokensTypes tts ) {
        this->type = tts;
        for( char &ch : raw )
            ch = 0;
    }

    Token( TokensTypes tts, char* rawstr, int startpos, int endpos ) {
        this->type = tts;
        char tmpch = rawstr[endpos];
        rawstr[endpos] = 0;
        strcpy( this->raw, rawstr + startpos );
        rawstr[endpos] = tmpch;
    }

    TokensTypes type;
    char raw[11];
};


class CounterInterpreter
{
public:
    CounterInterpreter( char* linestr ) : rawstring( linestr ), pos( 0 ) {}
    ~CounterInterpreter() { tokens.clear(); }

    //每个Token分析
    Token readtoken() {
        int startpos = this->pos;
        while( 1 ) 
        {
            //空格时startpos和pos同时递增
            if( isNil( rawstring[this->pos] ) ) {
                startpos = ++this->pos;
                continue;
            }
            //只有startpos处就是Operator时才判断为操作符
            else if( isOperator( rawstring[startpos] ) ) {
                return Token( TokensTypes::Operator, rawstring, startpos, ++this->pos );
            }
            //前方不是数字的数字处为Digits的结束处
            else if( isDigit( rawstring[this->pos] ) ) {
                if( !isDigit( rawstring[this->pos + 1] ) )
                    return Token( TokensTypes::Digits, rawstring, startpos, ++this->pos );
            }
            //EOF
            else if( rawstring[this->pos] == 0 ) {
                return Token( TokensTypes::Eof );
            }
            //ERROR
            else {
                this->pos++;
                return Token( TokensTypes::Err );
            }

            this->pos++;
        }
    }

    //Token分解
    bool analysis() {
        while( 1 ) {
            Token t = readtoken();
            tokens.push_back( t );
            if( t.type == Eof )
                break;
            if( t.type == Err )
                return false;
        }
        return true;
    }

    //正确性验证
    bool verify() {
        TokensTypes nextstatus = TokensTypes::Digits;
        for( auto t : tokens ) {
            //d+o+d
            if( t.type != nextstatus && t.type != TokensTypes::Eof )
                return false;
            //数字可以放在最前最后  但操作符不行
            if( t.type == TokensTypes::Eof && nextstatus == TokensTypes::Digits )
                return false;

            if( nextstatus == TokensTypes::Digits )
                nextstatus = TokensTypes::Operator;
            else if( nextstatus == TokensTypes::Operator )
                nextstatus = TokensTypes::Digits;
        }
        return true;
    }

    //最终计算
    int execute() {
        //优先计算*/
        //遇到*/时,将自己+身边的2个Token进行重新计算后,
        //删除右边的2个,并将结果写入最左边Token
        for( auto t = tokens.begin(); t != tokens.end(); t++ ) {
            if( (*t).type == TokensTypes::Operator ) {
                if( (*t).raw[0] == '*' ) {
                    t--;
                    int tmp = strtol( (*t).raw, 0, 10 );
                    t++; t++;
                    tmp *= strtol( (*t).raw, 0, 10 );
                    t--; t--;
                    sprintf( (*t).raw, "%d", tmp );
                    t++;
                    tokens.erase( t++ );
                    tokens.erase( t++ );
                    t--;
                }
                else if( (*t).raw[0] == '/' ) {
                    t--;
                    int tmp = strtol( (*t).raw, 0, 10 );
                    t++; t++;
                    tmp /= strtol( (*t).raw, 0, 10 );
                    t--; t--;
                    sprintf( (*t).raw, "%d", tmp );
                    t++;
                    tokens.erase( t++ );
                    tokens.erase( t++ );
                    t--;
                }
            }
        }

        //最后得出总数:计算+-
        int total = 0;
        for( auto t = tokens.begin(); t != tokens.end(); t++ ) {
            if( (*t).type == TokensTypes::Digits ) {
                total = strtol( (*t).raw, 0, 10 );
            }
            else if( (*t).type == TokensTypes::Operator ) {
                switch( (*t).raw[0] ) {
                    case '+':
                        t++;
                        total += strtol( (*t).raw, 0, 10 );
                        break;
                    case '-':
                        t++;
                        total -= strtol( (*t).raw, 0, 10 );
                        break;
                }
            }
        }

        return total;
    }

    //将token按顺序装起来
    list tokens;
    char* rawstring;
    int pos;
};

简单测试一下:

void main() {
    string str;
    while( 1 ) {
        //cin >> str;        //bug: cin会处理空格为结束符
        getline( cin, str );

        CounterInterpreter ci( (char*)(str.data()) );
        cout << "analysis : " << ci.analysis() << endl;
        cout << "verify   : " << ci.verify() << endl;
        cout << "result   : " << ci.execute() << endl;
    }
}

093852_a9mv_2319385.png

 

转载于:https://my.oschina.net/tasker/blog/724598

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值