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