Sprout.Weed でアンダーバー区切りの2進数をパースをちょっと改良

前回はアンダーバーを取り除いただけでしたが、今回は、

  • 4桁区切りで読み込む
  • prefix を定義
  • 「_」,「.」,「,」 のどれかで区切れる

という感じに改良してみました。

[ソース]

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


template<typename Parsed>
struct parsed_holder{
    constexpr
    operator bool() const{
        return parsed.success();
    }
    template<typename T>
    constexpr bool
    operator ==(T&& t) const{
        return parsed.attr() == t;
    }
    Parsed parsed;
};

template<typename Char, std::size_t N, typename Parser>
constexpr auto
parse(Char const(&str)[N], Parser&& parser)
->parsed_holder<decltype(
    sprout::weed::parse(str, str+N-1, sprout::forward<Parser>(parser))
)>{
    return { sprout::weed::parse(str, str+N-1, sprout::forward<Parser>(parser)) };
}


template<typename Parsed>
constexpr std::size_t
binary_parsed(Parsed&& parsed){
    return parsed.success() ? parsed.attr()
         : 0;
}

template<typename String>
constexpr std::size_t
parse_binary(String const& str){
    namespace w = sprout::weed;
    return binary_parsed(w::parse(str.begin(), str.end(), w::bin32 >> w::eoi));
}

template<typename Parsed>
constexpr std::size_t
remove_under_bar_binary_parsed(Parsed&& parsed){
    return parsed.success() ? parse_binary(parsed.attr())
         : 0;
}

template<
    typename Prefix,
    typename BinTop,
    typename Bin,
    typename Delimiter
>
constexpr std::size_t
remove_under_bar_binary(char const* str, std::size_t len,
    Prefix&& prefix, BinTop&& bin_top, Bin&& bin, Delimiter&& delimiter
){
    namespace w = sprout::weed;
    return remove_under_bar_binary_parsed(w::parse(str, str+len,
        prefix >> bin_top >> w::repeat(0)[w::lim<7>(delimiter >> bin)] >> w::eoi
    ));
}


constexpr std::size_t
operator "" _bin(char const* str, std::size_t len){
    namespace w = sprout::weed;
    return remove_under_bar_binary(str, len,
        w::omit[w::string("0b")|w::string("")],
        w::repeat(0)[w::lim<4>( w::char_('0')|w::char_('1') )],
        w::repeat(4)[w::lim<4>( w::char_('0')|w::char_('1') )],
        w::omit[w::char_('_')|w::char_(',')|w::char_('.')]
    );
}


int
main(){
    namespace w = sprout::weed;

    static_assert("0b1111_1111"_bin           == 0xff, "");
    static_assert(  "1111.1111"_bin           == 0xff, "");
    static_assert("0b0111"_bin                == 0x7, "");
    static_assert("0b0111_1110"_bin           == 0x7e, "");
    static_assert("0b1111,1011,0111,1110"_bin == 0xfb7e, "");
    static_assert("0b1011"_bin                == 0xb, "");
    static_assert("0b1011__"_bin              == 0, "");
    static_assert("0b1111_1111_1111_1111_1111_1111_1111_1111"_bin == 0xffffffff, "");


    // 各パーサのテストとか
    static constexpr auto bit       = w::char_('0')|w::char_('1');
    static constexpr auto bin       = w::repeat(4)[w::lim<4>(bit)];
    static constexpr auto bin_top   = w::repeat(0)[w::lim<4>(bit)];
    static constexpr auto delimiter = w::omit[w::char_('_')];
    static constexpr auto prefix    = w::omit[w::string("0b")|w::string("")];

    static_assert( parse(  "1111"  , prefix >> bin_top >> w::eoi), "");
    static_assert( parse(  "0011"  , prefix >> bin_top >> w::eoi), "");
    static_assert( parse(   "111"  , prefix >> bin_top >> w::eoi), "");
    static_assert( parse(  "0b11"  , prefix >> bin_top >> w::eoi), "");
    static_assert( parse( "0b011"  , prefix >> bin_top >> w::eoi), "");
    static_assert( parse("0b1011"  , prefix >> bin_top >> w::eoi), "");
    static_assert( parse(  "1111"  , prefix >> bin_top >> w::eoi) == "1111" , "");
    static_assert( parse("0b1111"  , prefix >> bin_top >> w::eoi) == "1111" , "");
    static_assert(!parse(  "1111_" , prefix >> bin_top >> w::eoi), "");
    static_assert(!parse(  "3914_" , prefix >> bin_top >> w::eoi), "");
    static_assert(!parse(  "11111" , prefix >> bin_top >> w::eoi), "");
    
    static constexpr auto bin_parser = prefix >> bin_top >> w::repeat(0)[w::lim<3>(delimiter >> bin)] >> w::eoi;

    static_assert( parse("1111", bin_parser), "");
    static_assert( parse("111", bin_parser), "");
    static_assert( parse("1111_1111", bin_parser), "");
    static_assert( parse("11_1111", bin_parser), "");
    static_assert( parse("0b11_1111", bin_parser), "");
    static_assert( parse("1111_0000_1111_1111", bin_parser), "");
    static_assert( parse("0b1111_0000_1111_1111", bin_parser) == "1111000011111111", "");
    static_assert(!parse("11111", bin_parser), "");
    static_assert(!parse("_1111_0000_1111_1111", bin_parser), "");
    static_assert(!parse("1111_1111_0000_1111_1111", bin_parser), "");

    return 0;
}

ちなみに全開に比べてもれなくコンパイル時間が boooooost します。
constexpr な関数内で Sprout.Weed を使おうとすると死んでしまいますね。

[コンパイラ]

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