Humanity

Edit the world by your favorite way

逆ポーランド記法計算機書いてみた


結構前からちょくちょくいじってて、
昨日はこればっかやってた。


というわけで貼りつけたいんだけど、
どうやらGistは1つのGistに複数ファイルがあると
アルファベット順的に先頭のファイルしか貼りつけができないらしい。
これのために新しいGistを作るのもアレなので
直接はてダに貼りつける。
(っていってもGistは気軽に作れるのが良いところだと思ってるけど)


boost使ったりしてみた(というか途中使わざるを得なかったり・・・orz)
あとでパーサの部分をBoost.Spiritを使って書いてみるかも。

/* vim:ts=4:sw=4:sts=0:tw=0:set et: */

#include <iostream>
#include <cstdlib>
#include <string>
#include <map>
#include <stack>
#include <cassert>
#include <functional>
#include <boost/function.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;
using namespace boost;

template <class Digit>
class RPN {
    typedef string::iterator
        InputIterator;
    typedef map< string, function<Digit (Digit, Digit)> >
        FuncMap;
    typedef stack<string>
        Stack;

public:
    RPN() {
        funcmap["+"] = plus<Digit>();
        funcmap["-"] = minus<Digit>();
        funcmap["*"] = multiplies<Digit>();
        funcmap["/"] = divides<Digit>();
    }


public:
    Digit
    eval(string& line) {
        while (! stk.empty())
            stk.pop();
        first = line.begin();
        last = line.end();
        return parse();
    }

    Digit
    parse();

private:
    string
    get_word();

    void
    call_stack() {
        string op, n, m;

        cerr << "* pop " << stk.top() << endl;
        op = stk.top(); stk.pop();

        if (is_op(op)) {
            cerr << "* pop " << stk.top() << endl;
            m = stk.top(); stk.pop();
            cerr << "* pop " << stk.top() << endl;
            n = stk.top(); stk.pop();
            stk.push(lexical_cast<string>(
                funcmap[op](
                    lexical_cast<Digit>(n),
                    lexical_cast<Digit>(m)
                )
            ));
        } else {
            die(op + ": No such operator");
        }
    }

    bool
    is_op(const char c) {
        string s(1, c);
        return is_op(s);
    }
    bool
    is_op(string& s) {
        return funcmap.find(s) != funcmap.end();
    }

    void
    die(const string& errmsg) {
        cerr << "error:"
             << errmsg
             << endl;
        exit(EXIT_FAILURE);
    }

private:
    InputIterator first;
    InputIterator last;
    FuncMap       funcmap;
    Stack         stk;
};

/* parser
 * TODO Boost.Spirit
 */
template <class Digit>
string
RPN<Digit>::get_word() {
    string word;
    while (first != last) {
        if (isspace(*first)) {
            ++first;
            continue;
        }

        if (isdigit(*first)) {
            while (first != last && isdigit(*first)) {
                word += *first;
                ++first;
            }
            break;
        }

        if (isalpha(*first)) {
            while (first != last && isalpha(*first)) {
                word += *first;
                ++first;
            }
            break;
        }

        if (is_op(*first)) {
            while (first != last && is_op(*first)) {
                word += *first;
                ++first;
            }
            break;
        }

        ++first;
    }
    cerr << "get_word() returns: " << word << endl;
    return word;
}

template <class Digit>
Digit
RPN<Digit>::parse() {
    while (1) {
        string word = get_word();
        if (word.empty()) {
            if (stk.empty()) {
                return 0.0;
            } else {
                return lexical_cast<Digit>(stk.top());
            }
        }
        cerr << "* push " << word << endl;
        stk.push(word);
        if (is_op(word))
            call_stack();
    }

    die("never reach this block");
}



int
main(int argc, char *argv[]) {

    RPN<double> rpn;
    string buf;
    while (getline(cin, buf)) {
        try {
            cout << "result: " << rpn.eval(buf) << endl;
        } catch (const bad_lexical_cast& e) {
            cerr << e.what() << endl;
        }
    }

    return EXIT_SUCCESS;
}