Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Boost.Spirit V2.1用Shift JISパーサ

タグ: C++ Boost

Boost.SpiritにV2が導入されてからしばらく経つし、そろそろclassicじゃないSpiritにも手を出してみよう(`・ω・´)
ただし使用するのは1.36.0?1.40.0 β1 RC1に入っているV2ではなく、trunk@55547のV2.1。
boost::spirit::qi::int_parserの実装を参考に、まずはShift JIS1文字にマッチするプリミティブパーサを作ってみる。

#include <boost/static_assert.hpp>
#include <boost/spirit/include/qi.hpp>

namespace client {

struct sjis_parser_impl : boost::spirit::qi::primitive_parser<sjis_parser_impl>
{
    template <typename Context, typename Iterator>
    struct attribute
    {
        typedef unsigned short type;
    };

    template <typename Iterator, typename Context, typename Skipper, typename Attribute>
    bool parse(Iterator& first, Iterator const& last, Context& /*context*/, Skipper const& skipper, Attribute& attr) const
    {
        // wchar_t とか渡されても(´・ω・`)知らんがな
        BOOST_STATIC_ASSERT(sizeof(typename std::iterator_traits<Iterator>::value_type) == 1);

        boost::spirit::qi::skip_over(first, last, skipper);
        if (first == last)
            return false;

        const unsigned char high = *first;
        if ((high & ~0x7f) == 0 || (high >= 0xa1 && high <= 0xdf))
        {
            // 1バイト文字(ASCIIおよび半角カナ)にマッチ
            attr = high;
            ++first;
            return true;
        }
        else if ((high >= 0x81 && high <= 0x9f) || (high >= 0xe0 && high <= 0xef))
        {
            // 2バイト文字上位1バイトにマッチ
            const Iterator save = first++;
            if (first != last)
            {
                const unsigned char low = *first;
                if (low != 0x7f && (low >= 0x40 && low <= 0xfc))
                {
                    // 2バイト文字下位1バイトにもマッチ
                    attr = ((high << 8) | low);
                    ++first;
                    return true;
                }
            }
            first = save;
        }
        return false;
    }
};

struct sjis_parser : boost::proto::terminal<sjis_parser_impl>::type {};

} // namespace client

V2とV2.1ではプリミティブパーサの実装が違っているので、上記のコードはV2(現行リリース版)では使えない。
ま、いずれV2.1になるさ。いずれ……

#include <string.h>
#include <vector>
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
// この辺でsjis_parserを実装(実装をインクルード)

namespace client {

void test()
{
    const char source[] =
#if 0
        "ヽ(・∀・)ノ";
#else
        { -127, 82, 40, -91, -127, -51, -91, 41, -125, 109, 0 };
#endif

    const char* first = source;
    const char* last = source + strlen(source);

    using boost::spirit::qi::parse;
    using boost::spirit::qi::_1;

    // セマンティックアクションで標準出力に出力
    std::cout << std::hex;
    parse(first, last, *sjis_parser()[std::cout << _1 << ',']);
    std::cout << std::endl;

    using boost::phoenix::push_back;
    using boost::phoenix::ref;

    std::vector<int> codes;
    // セマンティックアクションでcodesにpush_back
    first = source;
    parse(first, last, *sjis_parser()[push_back(ref(codes), _1)]);
    std::copy(codes.begin(), codes.end(), std::ostream_iterator<int>(std::cout, ","));
    std::cout << std::endl;

    // パーサの属性が1つ(この場合std::vector<unsigned short>)ならこうも書ける
    first = source;
    parse(first, last, *sjis_parser(), codes);
    std::copy(codes.begin(), codes.end(), std::ostream_iterator<int>(std::cout, ","));
    std::cout << std::endl;

    std::string str("low bytes -> ");
    // std::stringにpush_backすると上位バイトが欠ける
    first = source;
    parse(first, last, *sjis_parser(), str);
    std::cout << str << std::endl;

    using boost::spirit::qi::raw;

    // rawディレクティブを使うと、マッチした範囲をiterator_rangeで求められる
    first = source;
    parse(first, last, raw[*sjis_parser()], str);
    std::cout << str << std::endl;
}

} // namespace client
8152,28,a5,81cd,a5,29,836d,
8152,28,a5,81cd,a5,29,836d,
8152,28,a5,81cd,a5,29,836d,8152,28,a5,81cd,a5,29,836d,
low bytes -> R(・ヘ・)m
ヽ(・∀・)ノ
スポンサーサイト

コメント

コメントの投稿

コメントの投稿
管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://idlysphere.blog66.fc2.com/tb.php/213-48bb346b
この記事にトラックバックする(FC2ブログユーザー)

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。