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 の値はどうしようかしら。