3

キーワードではない英数字文字列を解析する識別子パーサーを書くのに苦労しています。キーワードはすべて表にあります。

struct keywords_t : x3::symbols<x3::unused_type> {
    keywords_t() {
        add("for", x3::unused)
                ("in", x3::unused)
                ("while", x3::unused);
    }
} const keywords;

識別子のパーサーは次のようになります。

auto const identifier_def =       
            x3::lexeme[
                (x3::alpha | '_') >> *(x3::alnum | '_')
            ];

これらを組み合わせて、識別子パーサーがキーワードの解析に失敗するようにしました。私はこのように試しました:

auto const identifier_def =       
                x3::lexeme[
                    (x3::alpha | '_') >> *(x3::alnum | '_')
                ]-keywords;

この:

auto const identifier_def =       
                x3::lexeme[
                    (x3::alpha | '_') >> *(x3::alnum | '_') - keywords
                ];

ほとんどの入力で機能しますが、文字列が次のようなキーワードで始まる場合int, whilefoo, forbar、パーサーはこの文字列の解析に失敗します。このパーサーを正しくするにはどうすればよいですか?

4

2 に答える 2

5

あなたの問題は、Spirit の差分演算子のセマンティクスが原因です。あなたがa - bスピリットを持っているとき、次のことを行います:

  • b一致 するかどうかを確認します。
    • 存在する場合はa - b失敗し、何も解析されません。
    • b失敗した場合は、a一致 するかどうかを確認します。
      • a失敗した場合は失敗a - bし、何も解析されません。
      • 成功した場合aa - b成功し、解析するものは何でもa解析します。

あなたの場合 ( unchecked_identifier - keyword) 識別子がキーワードで始まる限り、keyword一致し、パーサーは失敗します。keywordしたがって、個別のキーワードが渡されるたびに一致するものと交換する必要がありますが、キーワードの後に​​何か他のものが続くと失敗します。not predicate( !) はそれを助けることができます。

auto const distinct_keyword = x3::lexeme[ keyword >> !(x3::alnum | '_') ];

完全なサンプル ( Coliru で実行):

//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/spirit/home/x3.hpp>

namespace parser {
    namespace x3 = boost::spirit::x3;

    struct keywords_t : x3::symbols<x3::unused_type> {
        keywords_t() {
            add("for", x3::unused)
                    ("in", x3::unused)
                    ("while", x3::unused);
        }
    } const keywords;

    x3::rule<struct identifier_tag,std::string>  const identifier ("identifier");

    auto const distinct_keyword = x3::lexeme[ keywords >> !(x3::alnum | '_') ];
    auto const unchecked_identifier = x3::lexeme[(x3::alpha | x3::char_('_')) >> *(x3::alnum | x3::char_('_'))];


    auto const identifier_def = unchecked_identifier - distinct_keyword;

    //This should also work:
    //auto const identifier_def = !distinct_keyword >> unchecked_identifier


    BOOST_SPIRIT_DEFINE(identifier);

    bool is_identifier(const std::string& input)
    {
        auto iter = std::begin(input), end= std::end(input);

        bool result = x3::phrase_parse(iter,end,identifier,x3::space);

        return result && iter==end;
    }
}



int main() {

    std::cout << parser::is_identifier("fortran") << std::endl;
    std::cout << parser::is_identifier("for") << std::endl;
    std::cout << parser::is_identifier("integer") << std::endl;
    std::cout << parser::is_identifier("in") << std::endl;
    std::cout << parser::is_identifier("whileechoyote") << std::endl;
    std::cout << parser::is_identifier("while") << std::endl;
}
于 2016-06-26T16:34:30.723 に答える
2

問題は、これがレクサーなしで実行されることです。つまり、

keyword >> *char_

そして、それに入れると、としておよびとしてwhilefoo解析されます。whilekeywordfoo*char_

これは 2 つの方法で防ぐことができます。キーワードの後に​​スペースを入れる必要があります。

auto keyword_rule = (keyword >> x3::space);
//or if you use phrase_parse
auto keyword_rule = x3::lexeme[keyword >> x3::space];

あなたが説明した他の方法も可能です。つまり、文字列からキーワードを明示的に削除します(私はそのようにします):

auto string = x3::lexeme[!keyword >> (x3::alpha | '_') >> *(x3::alnum | '_')];

あなたの定義の問題は、最初の文字セットをキーワードとして解釈するため、まったく解析しないことを選択することです。「xy」演算子は、x を解析するが y を解析しないことを意味します。ただし、「whilefoo」を渡すと、「while」がキーワードとして解釈されるため、まったく解析されません。

于 2016-06-26T16:05:01.060 に答える