5

Boost::Spirit を使用した非常に単純なパーサーがあります。

rule<std::string::iterator, std::string()> zeroTo255 = (string("25") >> char_('0', '5'))
    | (char_('2') >> char_('0', '4') >> digit)
    | (char_('1') >> repeat[2](digit))
    | (char_('1', '9') >> digit) | digit;

解析しようとすると

std::string o{"1"};
std::string s;
parse(o.begin(), o.end(), zeroTo255, s);
std::cout << o << ": " << s << std::endl;

私は出力として持っています

1: 111

私は明らかに何か間違ったことをしていますが、何ですか?

4

2 に答える 2

8

qi::hold@Andrzejによって正しく言及されているように、それについての1つの方法です

役立つかもしれないいくつかの観察と、より良い解決策があると思います。


ポイントは、Spirit は設計上、属性の「一時」ストレージを必要としないということです。実際、そもそも属性がコピー可能であると想定することはできません。これがここでの理由です (すべてを単一の std::vector<> に解析し、パーサー ステップごとにコピーすることを想像してみてください)。

より本質的なレベルでは、ここで逆になっているのは属性の処理ではなく、パーサー式自体のように見えます。意図を述べることができず、数値表現を扱うあらゆる種類の複雑さを招きます...本当にそうすべきではありません。

それに対する私の見解は

rule<std::string::iterator, std::string()> zeroTo255, alternatively;

alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];

ご覧のとおり、Spirit に数値を解析させ、実際には範囲を確認するだけです。これは、最初にやりたかったことです。

典型的ではない 2 番目の点は、ルールがstd::string属性を公開しているという事実unsigned charです。

これが意識的な設計上の決定であると仮定すると、

  • 否定先読み ( !parser) - 属性には影響しません
  • 正先読み ( &parser) - 属性には影響しません
  • qi::as_stringqi::rawqi::lexemeおよび_qi::no_skip
  • セマンティック アクション (自動ルールに依存しないでください)

元のルールを最小限に変更すると、次のようになります。

zeroTo255 = raw [ 
          ("25" >> char_("0-5"))
        | ('2' >> char_("0-4") >> digit)
        | ('1' >> digit >> digit)
        | (char_("1-9") >> digit) 
        | digit
    ];

qi::holdこれは、_hold_ing 属性値のパフォーマンス上の欠点ではなく、使用するコードとほぼ同じ効果があります。

お役に立てれば。

完全なサンプル: http://liveworkspace.org/code/4v4CQW$0で公開:

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

namespace qi = boost::spirit::qi;

int main()
{
    using namespace qi;
    rule<std::string::iterator, std::string()> zeroTo255, alternatively;

    zeroTo255 = raw [ 
              ("25" >> char_("0-5"))
            | ('2' >> char_("0-4") >> digit)
            | ('1' >> digit >> digit)
            | (char_("1-9") >> digit) 
            | digit
        ];

    alternatively %= raw [ uint_ [ _pass = (_1 <= 255) ] ];

    for (auto& input : std::vector<std::string> { "255", "249", "178", "30", "4" })
    {
        std::string output;
        std::cout << "zeroTo255:\t" << std::boolalpha 
                  << parse(std::begin(input), std::end(input), zeroTo255, output) 
                  << ": " << output << std::endl;

        output.clear();
        std::cout << "alternatively:\t" << std::boolalpha 
                  << parse(std::begin(input), std::end(input), alternatively, output) 
                  << ": " << output << std::endl;
    }

}

出力

zeroTo255:      true: 255
alternatively:  true: 255
zeroTo255:      true: 249
alternatively:  true: 249
zeroTo255:      true: 178
alternatively:  true: 178
zeroTo255:      true: 30
alternatively:  true: 30
zeroTo255:      true: 4
alternatively:  true: 4
于 2012-12-14T08:40:42.800 に答える
5

私はかつて同様の問題に直面しました。これは、Spirit の代替演算子が機能する特定の方法です。追加のディレクティブ「hold」を使用すると、例が機能するはずです。

rule<std::string::iterator, std::string()> zeroTo255 
= hold[string("25") >> char_('0', '5')]
| hold[char_('2') >> char_('0', '4') >> digit]
| hold[char_('1') >> repeat[2](digit)]
| hold[char_('1', '9') >> digit] | digit;

この動作の詳細については、このスレッドを参照してください。

于 2012-12-13T23:05:25.747 に答える