LoginSignup
0
0

More than 5 years have passed since last update.

オフラインリアルタイムどう書く E29 の実装例( C++ )

Last updated at Posted at 2018-12-09

問題の名前 : アンエスケープ
問題 : http://nabetani.sakura.ne.jp/hena/orde29unes/
実装リンク集 : https://qiita.com/Nabetani/items/f2db9b916c0a301b744f

次回のイベントは 2/2
see https://yhpg.doorkeeper.jp/events/84247

で。

ruby に racc があるのなら、C++ には boost::spirit::qi がある。

というわけで、qi で書いてみた。

非常に苦しんだけど、なんとか書くことができた。

c++17
// clang++ -std=c++17 -O2 -Wall e29.cpp
#include <iostream>
#include <vector>
#include <string>
#include <optional>
#include <algorithm>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_action.hpp>
#include <boost/algorithm/string/join.hpp>

namespace qi = boost::spirit::qi;
namespace sw = qi::standard_wide;

template <typename Iterator>
std::optional<std::vector<std::string>> parse(Iterator first, Iterator last)
{
  std::vector<std::string> o{""};
  using rule_type = qi::rule<Iterator, std::string()>;
  rule_type alnum_unit = (sw::char_ - '/' - '\'' - '"');
  rule_type slash_pair = qi::lit("//");
  auto raw_proc = [&o](auto const &v) {
    o.back() += v;
  };
  auto slash_pair_proc = [&o](auto const &v) {
    o.back() += "/";
  };
  rule_type sq = qi::lit('\'');
  rule_type dq = qi::lit('"');
  rule_type slash = qi::lit('/');
  rule_type dq_unit = "\"" >> *(alnum_unit | sq | slash) >> "\"";
  rule_type sq_unit = "'" >> *(alnum_unit | dq | slash) >> "'";
  rule_type entry = +(
      alnum_unit[raw_proc] | slash_pair[slash_pair_proc] | dq_unit[raw_proc] | sq_unit[raw_proc]);
  auto entry_proc = [&o](auto const &v) {
    o.emplace_back("");
  };
  rule_type entries = entry[entry_proc] % '/';
  bool r = qi::parse(first, last, entries);
  auto rest = std::string(first, last);
  if (!r || first != last)
  {
    return std::nullopt;
  }
  o.pop_back(); // なぜこれが必要なのかわからない。
  bool empty_exist = std::any_of(
      o.cbegin(), o.cend(),
      [](auto const &v) -> bool { return v.empty(); });
  if (empty_exist)
  {
    return std::nullopt;
  }
  return o;
}

std::string solve(std::string const &src)
{
  auto result = parse(src.cbegin(), src.cend());
  if (!result)
  {
    return "-";
  }
  return boost::algorithm::join(*result, ",");
}

struct result_t
{
  int win, lose;
  int total() const { return win + lose; }
};

void run_test(result_t &r, char const *src, char const *expected)
{
  std::string actual = solve(src);
  bool okay = actual == expected;
  (okay ? r.win : r.lose) += 1;
  std::cout
      << (okay ? "ok " : "**NG** ")
      << src << "->" << actual << " / " << expected
      << std::endl;
}

int main()
{
#define test(x, y) run_test(result, x, y)
  result_t result = {0};
  /*0*/ test("foo/bar/baz", "foo,bar,baz");
  /*1*/ test("/foo/bar/baz'/", "-");
  /*2*/ test("\"", "-");
  /*3*/ test("'", "-");
  // 中略
  /*63*/ test("Foo/Bar/\"Hoge'/'Fuga\"", "Foo,Bar,Hoge'/'Fuga");
  std::cout << result.win << " / " << result.total() << std::endl;
  return 0;
}

テストデータの大半は省略。

o.pop_back(); が必要な理由がよくわからない。

というかそもそも、[&o] とキャプチャするのは美しくない気がするんだけど、他に方法がわからなかった。

初めて boost::spirit::qi を使ったんだけど:

  • 噂通り、コンパイル時間が boost した。
  • 噂通り、演算子のオーバーロード濫用していて面白くも気持ち悪いライブラリだと思った。

という感じ。流石です。+(expr1|expr2) とかがなんかすごい。

生まれて初めてといえば、 std::optional を使ったのも初めて。boost::optional なら使ったことあったけど。std::none じゃなくて std::nullopt なんだね。

行数という点では、solve の最後までで 63行。racc 版とほぼ同じ。もっと長くなると思っていたのでびっくりした。

まあ、レキサが別関数になっていないからか。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0