Вопрос по parsing, boost-spirit, boost, boost-spirit-qi, c++ – Разобрать процитированные строки с boost :: spirit

5

Я хотел бы разобрать предложение, в котором некоторые строки могут быть без кавычек, "цитировано"; или "процитировано". Код ниже почти работает - но он не соответствует закрывающим кавычкам. Я предполагаю, что это из-за ссылки на qq. Модификация прокомментирована в коде, модификация повторяется в «цитируемом». или "цитируется" Также анализ и помогает показать оригинальную проблему с заключительной цитатой. Код также описывает точную грамматику.

Чтобы было совершенно ясно: строки без кавычек разбираются. Строка в кавычках, как'hello' будет разбирать открытую цитату'все персонажиhello, но потом не удалось разобрать окончательную цитату'.

Я сделал еще одну попытку, похожую на начало / конец тега вповысить учебники, но безуспешно.

<code>template <typename Iterator>
struct test_parser : qi::grammar<Iterator, dectest::Test(), ascii::space_type>
{
    test_parser()
        :
    test_parser::base_type(test, "test")
    {
        using qi::fail;
        using qi::on_error;
        using qi::lit;
        using qi::lexeme;
        using ascii::char_;
        using qi::repeat;
        using namespace qi::labels;
        using boost::phoenix::construct;
        using boost::phoenix::at_c;
        using boost::phoenix::push_back;
        using boost::phoenix::val;
        using boost::phoenix::ref;
        using qi::space;

        char qq;          

        arrow = lit("->");

        open_quote = (char_('\'') | char_('"')) [ref(qq) = _1];  // Remember what the opening quote was
        close_quote = lit(val(qq));  // Close must match the open
        // close_quote = (char_('\'') | char_('"')); // Enable this line to get code 'almost' working

        quoted_string = 
            open_quote
            >> +ascii::alnum        
            >> close_quote; 

        unquoted_string %= +ascii::alnum;
        any_string %= (quoted_string | unquoted_string);

        test = 
            unquoted_string             [at_c<0>(_val) = _1] 
            > unquoted_string           [at_c<1>(_val) = _1]   
            > repeat(1,3)[any_string]   [at_c<2>(_val) = _1]
            > arrow
            > any_string                [at_c<3>(_val) = _1] 
            ;

        // .. <snip>set rule names
        on_error<fail>(/* <snip> */);
        // debug rules
    }

    qi::rule<Iterator> arrow;
    qi::rule<Iterator> open_quote;
    qi::rule<Iterator> close_quote;

    qi::rule<Iterator, std::string()> quoted_string;
    qi::rule<Iterator, std::string()> unquoted_string;
    qi::rule<Iterator, std::string()> any_string;     // A quoted or unquoted string

    qi::rule<Iterator, dectest::Test(), ascii::space_type> test;

};


// main()
// This example should fail at the very end 
// (ie not parse "str3' because of the mismatched quote
// However, it fails to parse the closing quote of str1
typedef boost::tuple<string, string, vector<string>, string> DataT;
DataT data;
std::string str("addx001 add 'str1'   \"str2\"       ->  \"str3'");
std::string::const_iterator iter = str.begin();
const std::string::const_iterator end = str.end();
bool r = phrase_parse(iter, end, grammar, boost::spirit::ascii::space, data);
</code>

Для бонусного кредита: решение, которое позволяет избежать локального члена данных (например,char qq в приведенном выше примере) было бы предпочтительным, но с практической точки зрения я буду использовать все, что работает!

Для записи, делаяchar qq переменная-членstruct test_parser терпит неудачу точно так же. Zero
Сбой в том, что "таким же образом?" Вы не сказали нам, как это не работает (хотя я могу представить, что это из-заqq ссылка). Nicol Bolas
@NicolBolas Это был комментарий в коде - я уточнил вопрос, спасибо за указание. Я также подозреваю, что ссылка (qq), но оборотная сторона лямбда-усилителя заключается в том, что их сложно отладить, поскольку вы не можете пройти через это в традиционном смысле! Zero

Ваш Ответ

1   ответ
12

qq после выхода из конструктора зависает, так что это действительно проблема.

qi::locals этоcanonical способ сохранить локальное состояние внутри выражений парсера. Другим вариантом будет продление срока службыqq (делая его членом класса грамматики, например). Наконец, вы можете быть заинтересованы вinherited attributes также. Этот механизм дает вам возможность вызывать правило / грамматику с «параметрами»; (прохождение местного штата вокруг).

NOTE There are caveats with the use of the kleene operator +: it is greedy, and parsing fails if the string is not terminated with the expected quote.

See another answer I wrote for more complete examples of treating arbitrary contents in (optionally/partially) quoted strings, that allow escaping of quotes inside quoted strings and more things like that:

How to make my split work only on one real line and be capable to skip quoted parts of string?

Я уменьшил грамматику до соответствующего бита и включил несколько тестовых случаев:

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

namespace qi = boost::spirit::qi;

template <typename Iterator>
struct test_parser : qi::grammar<Iterator, std::string(), qi::space_type, qi::locals<char> >
{
    test_parser() : test_parser::base_type(any_string, "test")
    {
        using namespace qi;

        quoted_string = 
               omit    [ char_("'\"") [_a =_1] ]             
            >> no_skip [ *(char_ - char_(_a))  ]
            >> lit(_a)
        ; 

        any_string = quoted_string | +qi::alnum;
    }

    qi::rule<Iterator, std::string(), qi::space_type, qi::locals<char> > quoted_string, any_string;
};

int main()
{
    test_parser<std::string::const_iterator> grammar;
    const char* strs[] = { "\"str1\"", 
                           "'str2'",
                           "'str3' trailing ok",
                           "'st\"r4' embedded also ok",
                           "str5",
                           "str6'",
                           NULL };

    for (const char** it = strs; *it; ++it)
    {
        const std::string str(*it);
        std::string::const_iterator iter = str.begin();
        std::string::const_iterator end  = str.end();

        std::string data;
        bool r = phrase_parse(iter, end, grammar, qi::space, data);

        if (r)
            std::cout << "Parsed:    " << str << " --> " << data << "\n";
        if (iter!=end)
            std::cout << "Remaining: " << std::string(iter,end) << "\n";
    }
}

Выход:

Parsed:    "str1" --> str1
Parsed:    'str2' --> str2
Parsed:    'str3' trailing ok --> str3
Remaining: trailing ok
Parsed:    'st"r4' embedded also ok --> st"r4
Remaining: embedded also ok
Parsed:    str5 --> str5
Parsed:    str6' --> str6
Remaining: '
@Zero. Для хорошего примера я буду ссылаться на страницу, на которую вы ссылаетесь в своем вопросе, особенно здесь:One More Take
@ Ноль, спасибо! И, эмqi::locals была гиперссылка в моем ответе :) -click it for documentation
Немного улучшен синтаксический анализ строкового литерала (прием любого текста в кавычках). Теперь также с фиксированным тестом
Ага, понял - внизуOne More Take они говорят о «местных жителях»; параметр шаблона. Еще раз спасибо. Zero
Спасибо, это именно то, что я был после. Можете ли вы опубликовать ссылку на любую документацию / примеры о местных жителях, мне потребовалось некоторое время, чтобы заметитьqi::local<char> в подписи к правилу, и это было бы хорошим ориентиром для меня и всех, кто рассматривает этот вопрос. Zero

Похожие вопросы