Sprout.Weed でセマンティックアクション

的ななにか。
Boost.Spirit.Qi とはちと挙動が違うと思いますが、あったらあったで便利そうな感じがします。

[ソース]

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


template<typename T>
using decay = typename std::decay<T>::type;


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

    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 Parser, typename Action>
struct semantic_action : sprout::weed::parser_base{

    template<typename Context, typename Iterator>
    struct attribute{
        typedef decltype(std::declval<Action>()(
            sprout::weed::parse(
                std::declval<Iterator>(),
                std::declval<Iterator>(),
                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 semantic_action(Parser const& parser, Action const& action)
        : parser(parser), action(action){}

    template<
        typename Context,
        typename Iterator,
        typename Result = typename result<Context, Iterator>::type
    >
    constexpr Result operator()(
        Iterator first,
        Iterator last,
        Context const&
    ) const{
        typedef typename result<Context, Iterator>::type result_type;
        typedef typename attribute<Context, Iterator>::type attribute_type;
        return eval<Result>(sprout::weed::parse(first, last, parser));
    }

private:
    template<typename Result, typename Parsed>
    constexpr Result
    eval(Parsed&& parsed){
        return parsed.success()
             ? Result{ parsed.success(), parsed.current(), action(parsed.attr()) }
             : Result{ parsed.success(), parsed.current(), decltype(action(parsed.attr())){} };
    }
    
    Parser parser;
    Action action;
};


template<typename Parser, typename Action>
constexpr semantic_action<decay<Parser>, decay<Action>>
action(Parser&& parser, Action&& action){
    return { sprout::forward<Parser>(parser), sprout::forward<Action>(action) };
}

template<typename Parser>
struct action_operator{
    
    template<typename Action>
    constexpr semantic_action<Parser, decay<Action>>
    operator [](Action&& action) const{
        return { parser, sprout::forward<Action>(action) };
    }
    
    Parser parser;
};

template<typename Parser>
constexpr action_operator<decay<Parser>>
_(Parser&& parser){
    return { parser };
}


constexpr int
twice(int n){
    return n + n;
}

constexpr long long int
plus(sprout::array<long long int, 2> const& v){
    return v[0] + v[1];
}

constexpr long long int
minus(sprout::array<long long int, 2> const& v){
    return v[0] - v[1];
}

constexpr long long int
multiple(sprout::array<long long int, 2> const& v){
    return v[0] * v[1];
}

constexpr long long int
division(sprout::array<long long int, 2> const& v){
    return v[0] / v[1];
}


struct person{
    sprout::string<16> name;
    std::size_t age;
    
    constexpr bool
    operator ==(person const& rhs) const{
        return name == rhs.name
            && age  == rhs.age;
    }
};

struct to_person{
    template<typename T>
    constexpr person
    operator ()(T&& t) const{
        return {
            sprout::get<0>(sprout::forward<T>(t)),
            sprout::get<1>(sprout::forward<T>(t))
        };
    }
};


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

    static_assert(parse("1234",  action(w::int_, &twice)) == 2468, "");
    static_assert(parse("12+34", action(w::int_ >> "+" >> w::int_, &plus)) == 46, "");
    static_assert(parse("12-34", _(w::int_ >> "-" >> w::int_)[ &minus ]) == -22, "");


    static constexpr auto calc
        = _(w::int_ >> "+" >> w::int_)[ &plus ]
        | _(w::int_ >> "-" >> w::int_)[ &minus ]
        | _(w::int_ >> "*" >> w::int_)[ &multiple ]
        | _(w::int_ >> "/" >> w::int_)[ &division ];
    
    static_assert( parse("23+14", calc) == 37, "");
    static_assert( parse("23-14", calc) == 9, "");
    static_assert( parse("23*14", calc) == 322, "");
    static_assert( parse("23/14", calc) == 1, "");
    static_assert(!parse("23%14", calc), "");


    using sprout::to_string;
    static constexpr auto str = +w::lim<16>(w::alpha);
    static constexpr auto parser = "name=" >> str >> ":" >> "age=" >> w::uint32;

    static constexpr auto result_check = person{ to_string("mado"), 13 };
    static_assert( parse("name=mado:age=13", _(parser)[ to_person() ]) == result_check, "");
    static_assert(!parse("name=mado;age=13", _(parser)[ to_person() ]), "");

    return 0;
}

まぁパーサの評価後に指定した関数に渡しているだけですね。
attribute 型とか結構無理やり取得していますが。
今回は attribute をそのまま関数に渡していますが、array や tuple の場合は展開して評価したいですねぇ。
あとパースに失敗した場合の attribute の値はどうしようかしら。

[コンパイラ]

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