Sprout::Weed の lim を短くする

特定の文字列をパースする場合に Sprout::Weed の lim を使用して

 *w::lim<4>(w::char_ - ':') >> ":" >> *w::lim<2>(w::char_)


の様なパーサを定義するんですが、これだと若干長くなってしまいます。
これはこれで汎用性があっていいのですが、簡単なパーサを定義する場合はちょっとめんどくさいよねー、ってことどうにかして短く出来ないかとやってみました。

[ソース]

#include <sprout/weed.hpp>


template<typename Parser, typename Category,  std::size_t Size>
struct limiter_impl : sprout::weed::parser_base{

    template<typename Context, typename Iterator>
    struct attribute{
        typedef decltype(
            sprout::weed::parse(
                std::declval<Iterator>(),
                std::declval<Iterator>(),
                sprout::weed::lim<Size>(std::declval<Parser>())
            ).attr()
        ) type;
    };
    template<typename Context, typename Iterator>
    struct result{
        typedef sprout::weed::parser_result<Iterator, typename attribute<Context, Iterator>::type> type;
    };
    constexpr limiter_impl() = default;
    explicit constexpr limiter_impl(Parser const& parser, Category const& category)
        : parser_(parser), category_(category){}

    template<
        typename Context,
        typename Iterator,
        typename Result = typename result<Context, Iterator>::type
    >
    constexpr Result operator()(
        Iterator first,
        Iterator last,
        Context const&
    ) const{
        return eval<Result>(sprout::weed::parse(first, last, sprout::weed::lim<Size>(parser_, category_)));
    }

    template<typename Result, typename Parsed>
    constexpr Result
    eval(Parsed&& parsed) const{
        return Result{ parsed.success(), parsed.current(), parsed.attr() };
    }
    

    constexpr sprout::weed::limited::category
    category() const{
        return category_;
    }
    
    constexpr Parser
    parser() const{
        return parser_;
    }

private:
    Parser parser_;
    Category category_;
};

//
// operator*
//
template<typename Parser, typename Category, std::size_t Size>
constexpr auto
operator *(::limiter_impl<Parser, Category, Size> const& lim)
->decltype(*sprout::weed::lim<Size>(lim.parser(), lim.category())) const{
    return *sprout::weed::lim<Size>(lim.parser(), lim.category());
}

template<typename Parser, typename Category, std::size_t Size>
constexpr auto
operator *(::limiter_impl<Parser, Category, Size>&& lim)
->decltype(*sprout::weed::lim<Size>(lim.parser(), lim.category())) const{
    return *sprout::weed::lim<Size>(lim.parser(), lim.category());
}

//
// operator -
//
template<typename Parser, typename Category, std::size_t Size, typename T>
constexpr auto
operator -(::limiter_impl<Parser, Category, Size> const& lim, T&& t)
->decltype(sprout::weed::lim<Size>(lim.parser() - t, lim.category())) const{
    return sprout::weed::lim<Size>(lim.parser() - t, lim.category());
}

template<typename Parser, typename Category, std::size_t Size, typename T>
constexpr auto
operator -(::limiter_impl<Parser, Category, Size>&& lim, T&& t)
->decltype(sprout::weed::lim<Size>(lim.parser() - t, lim.category())) const{
    return sprout::weed::lim<Size>(lim.parser() - t, lim.category());
}

//
// operator >>
//
template<typename Parser, typename Category, std::size_t Size, typename T>
constexpr auto
operator >>(::limiter_impl<Parser, Category, Size> const& lim, T&& t)
->decltype(sprout::weed::lim<Size>(lim.parser() >> t, lim.category())) const{
    return sprout::weed::lim<Size>(lim.parser() >> t, lim.category());
}

template<typename Parser, typename Category, std::size_t Size, typename T>
constexpr auto
operator >>(::limiter_impl<Parser, Category, Size>&& lim, T&& t)
->decltype(sprout::weed::lim<Size>(lim.parser() >> t, lim.category())) const{
    return sprout::weed::lim<Size>(lim.parser() >> t, lim.category());
}


template<std::size_t N, typename Parser, typename Category,
    typename Result = limiter_impl<typename std::decay<Parser>::type, typename std::decay<Category>::type, N>
>
constexpr Result
limiter(Parser&& parser, Category&& category){
    return Result{ sprout::forward<Parser>(parser), sprout::forward<Category>(category) };
}

template<std::size_t Size>
constexpr auto
chars()
->decltype(limiter<Size>(sprout::weed::char_, sprout::weed::limited::discard)){
    return limiter<Size>(sprout::weed::char_, sprout::weed::limited::discard);
}

template<std::size_t Size, typename Category>
constexpr auto
chars(Category&& category)
->decltype(limiter<Size>(sprout::weed::char_, sprout::forward<Category>(category))){
    return limiter<Size>(sprout::weed::char_, sprout::forward<Category>(category));
}

static constexpr auto char1  = chars<1>();
static constexpr auto char2  = chars<2>();
static constexpr auto char3  = chars<3>();
static constexpr auto char4  = chars<4>();
static constexpr auto char5  = chars<5>();
static constexpr auto char6  = chars<6>();
static constexpr auto char7  = chars<7>();
static constexpr auto char8  = chars<8>();
static constexpr auto char9  = chars<9>();
static constexpr auto char10 = chars<10>();


template<typename Parsed>
struct parsed_holder{
    constexpr
    operator bool() const{
        return parsed.success();
    }
    
    constexpr decltype(std::declval<Parsed>().attr())
    attr() const{
        return parsed.attr();
    }

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

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

    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)) };
}


int
main(){
    namespace w = sprout::weed;
    
    // w::lim を使用する場合長くなってしまう
    static_assert(parse("homu:an", *w::lim<4>(w::char_ - ':') >> ":" >> *w::lim<2>(w::char_)) == "homuan", "");
    
    // 短い!
    static_assert(parse("homu:an", *(char4 - ':') >> ":" >> *char2) == "homuan", "");
    
    {
        static constexpr char src[] = "homumado";

        static_assert(parse(src, chars<4>()), "");
        static_assert(parse(src, chars<4>()) == 'h', "");
        static_assert(parse(src, chars<4>()) == parse(src, w::lim<4>(w::char_)), "");

        static_assert(parse(src, *chars<4>()), "");
        static_assert(parse(src, *chars<4>()) == "homu", "");
        static_assert(parse(src, *w::lim<4>(w::char_)), "");
        static_assert(parse(src, *chars<4>()) == parse(src, *w::lim<4>(w::char_)), "");

        static_assert(parse(src, *chars<4>(w::limited::circular)) == "mado", "");
        static_assert(
               parse(src, *chars<4>(w::limited::circular))
            == parse(src, *w::lim<4>(w::char_, w::limited::circular)),
        "");

        static_assert(parse(src, chars<4>() - 'm') == parse(src, w::lim<4>(w::char_ - 'm')), "");
        static_assert(parse(src, *(chars<4>() - 'm')) == parse(src, *w::lim<4>(w::char_ - 'm')), "");
        static_assert(
               parse(src, "homu" >> *chars<4>())
            == parse(src, "homu" >> *w::lim<4>(w::char_))
        , "");
    }

    return 0;
}


ちょっとコードは長いですが、要は

 *(char4 - ':') == *lim<4>(char_ - ':')


が等価になるようなパーサを定義しているだけですね。
まだ operator << と operator - しか定義していませんが、そこそこ使えるんじゃないかと。
個人的にはパーサがだいぶすっきりしていて気に入っているんですがどうでしょう。


あとどうでもいいですが、T&& が厄介過ぎて辛い。
多重定義と相性が悪すぎる…。

[コンパイラ]

  • g++ (GCC) 4.8.0 20120415 (experimental)