Sprout.Weed でコンパイル時四則演算のパース

とりあえず、自力で書いてみました。
構文解析とかあんまりやったことが無いので、もっと簡単なやり方がありそうですね。
コードがだいぶアレな感じになってしまってぐぬぬ
多分問題ない…はず。
ちなみに括弧は対応していないです。

[ソース]

#define SPROUT_CONFIG_SUPPORT_TEMPORARY_CONTAINER_ITERATION
#include <sprout/weed.hpp>

template<std::size_t StringMax, typename String>
constexpr long long int
calc(String const& str);

template<typename String>
constexpr long long int
calc(String const& str);

namespace detail{

namespace w = sprout::weed;

template<std::size_t StringMax>
constexpr auto
make_calcu_parser(char op)
->decltype(
    w::as_tuple[
        *w::lim<StringMax>(w::char_ - (op >> *w::lim<StringMax>(w::char_-'+'-'-') >> w::eoi))
    ]
    >> op
    >> *w::lim<StringMax>(w::char_)
){
    return w::as_tuple[
        *w::lim<StringMax>(w::char_ - (op >> *w::lim<StringMax>(w::char_-'+'-'-') >> w::eoi))
    ]
    >> op
    >> *w::lim<StringMax>(w::char_);
}


// template<
//     std::size_t StringMax,
//     typename Plus,
//     typename Minus,
//     typename Multiple,
//     typename Division,
//     typename Value
// >
// constexpr long long int
// calcu_parsed(
//     Plus     && plus,
//     Minus    && minus,
//     Multiple && mul,
//     Division && div,
//     Value    && value
// ){
//     struct constexpr_error_failed_parse{
//         int operator ()() const{ return 0; }
//     };
// 
//     return plus.success()  ? calc<StringMax>(sprout::get<0>(plus.attr()))  + calc<StringMax>(sprout::get<1>(plus.attr()))
//          : minus.success() ? calc<StringMax>(sprout::get<0>(minus.attr())) - calc<StringMax>(sprout::get<1>(minus.attr()))
//          : mul.success()   ? calc<StringMax>(sprout::get<0>(mul.attr()))   * calc<StringMax>(sprout::get<1>(mul.attr()))
//          : div.success()   ? calc<StringMax>(sprout::get<0>(div.attr()))   / calc<StringMax>(sprout::get<1>(div.attr()))
//          : value.success() ? value.attr()
//          : constexpr_error_failed_parse()();
// }
// 
// 
// template<std::size_t StringMax, typename String>
// constexpr long long int
// calcu_impl(String const& str){
//     namespace w = sprout::weed;
//     return calcu_parsed<StringMax>(
//         w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('+')),
//         w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('-')),
//         w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('*')),
//         w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('/')),
//         w::parse(str.begin(), str.end(), w::int_)
//     );
// }

template<
    typename String,
    typename Parsed
>
constexpr long long int
calcu_value(String const& str, Parsed&& parsed){
    struct constexpr_error_failed_parse{
        int operator ()() const{ return 0; }
    };
    return parsed.success() ? parsed.attr()
         : constexpr_error_failed_parse()();
}

template<
    std::size_t StringMax,
    typename String,
    typename Parsed
>
constexpr long long int
calcu_division(String const& str, Parsed&& parsed){
    return parsed.success()
                ? calc<StringMax>(sprout::get<0>(parsed.attr()))
                / calc<StringMax>(sprout::get<1>(parsed.attr()))
         : calcu_value(str, w::parse(str.begin(), str.end(), w::int_));
}

template<
    std::size_t StringMax,
    typename String,
    typename Parsed
>
constexpr long long int
calcu_multiple(String const& str, Parsed&& parsed){
    return parsed.success() 
                ? calc<StringMax>(sprout::get<0>(parsed.attr()))
                * calc<StringMax>(sprout::get<1>(parsed.attr()))
         : calcu_division<StringMax>(
             str, w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('/'))
        );
}

template<
    std::size_t StringMax,
    typename String,
    typename Parsed
>
constexpr long long int
calcu_minus(String const& str, Parsed&& parsed){
    return parsed.success()
                ? calc<StringMax>(sprout::get<0>(parsed.attr()))
                - calc<StringMax>(sprout::get<1>(parsed.attr()))
         : calcu_multiple<StringMax>(
             str, w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('*'))
        );
}

template<
    std::size_t StringMax,
    typename String,
    typename Parsed
>
constexpr long long int
calcu_plus(String const& str, Parsed&& parsed){
    struct constexpr_error_failed_parse{
        int operator ()() const{ return 0; }
    };
    return parsed.success()
                ? calc<StringMax>(sprout::get<0>(parsed.attr()))
                + calc<StringMax>(sprout::get<1>(parsed.attr()))
         : calcu_minus<StringMax>(
            str, w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('-'))
        );
}


template<std::size_t StringMax, typename String>
constexpr long long int
calcu_impl(String const& str){
    namespace w = sprout::weed;
    return calcu_plus<StringMax>(
        str, w::parse(str.begin(), str.end(), make_calcu_parser<StringMax>('+'))
    );
}


}  // namespace

template<std::size_t StringMax, typename String>
constexpr long long int
calc(String const& str){
    namespace w = sprout::weed;
    return detail::calcu_impl<StringMax>(
        w::parse(str.begin(), str.end(), *w::lim<StringMax>(w::char_ >> *w::omit[w::space])).attr()
    );
}

template<typename String>
constexpr long long int
calc(String const& str){
    namespace w = sprout::weed;
    return detail::calcu_impl<16>(w::parse(str.begin(), str.end(), *w::lim<16>(w::char_ >> *w::omit[w::space])).attr());
}

template<typename Char, std::size_t N>
constexpr long long int
calc(Char const(&str)[N]){
    return calc<N>(sprout::to_string(str));
}


int
main(){
    static constexpr auto str = sprout::to_string("1+2");
    static_assert(calc(str) == 3, "");

    static_assert(calc("1+2")       == 3, "");
    static_assert(calc("1+2+3")     == 6, "");
    static_assert(calc("1-3")       == -2, "");
    static_assert(calc("1+2-3-6")   == -6, "");
    static_assert(calc("2*5")       == 10, "");
    static_assert(calc("1+2*2")     == 5, "");
    static_assert(calc("1+2*2+4/2") == 7, "");
    static_assert(calc("3 + 6 / 2") == 6, "");
    static_assert(calc("1-2+3-4")   == -2, "");
    static_assert(calc("1+2-3+4")   == 4, "");
    static_assert(calc("2*6/2")     == 6, "");
    static_assert(calc("6/2*4")     == 12, "");
    static_assert(calc("1+2-3-6")   == -6, "");
    static_assert(calc("2*3+4*2")   == 14, "");

    return 0;
}

やはり constexpr だとローカル変数が使えないのがネックですね。
無駄に毎回パーサを生成しているのでそこら辺も変えたほうがよさげ。
セマンティックアクションとかが使えればもっとスマートになるんじゃろうか。

[コンパイラ]

  • g++ (GCC) 4.7.0 20111210 (experimental)