Ускоренный анализатор пропусков по крайней мере с одним пробелом - PullRequest
0 голосов
/ 01 ноября 2018

В грамматике, которую я реализую, есть элементы, разделенные пробелом. С парсером пропуска пробелы между элементами пропускаются автоматически, но это также позволяет нет пробела, что не то, что я хочу. Конечно, я мог бы явно написать грамматику, включающую эти пробелы, но мне кажется (со сложностью и гибкостью, предлагаемой духом), что есть лучший способ сделать это. Есть? Вот пример:

#include <cstdlib>
#include <iostream>
#include <string>

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

namespace qi = boost::spirit::qi;

int main(int argc, char** argv)
{
    if(argc != 2)
    {
        std::exit(1);
    }
    std::string str = argv[1];
    auto iter = str.begin();
    bool r = qi::phrase_parse(iter, str.end(), qi::char_ >> qi::char_, qi::blank);

    if (r && iter == str.end())
    {
        std::cout << "parse succeeded\n";
    }
    else
    {
        std::cout << "parse failed. Remaining unparsed: " << std::string(iter, str.end()) << '\n';
    }
}

Это позволяет ab, а также a b. Я хочу, чтобы только последний был разрешен.

В связи с этим: Как именно работают пропускающие парсеры? Один предоставляет что-то вроде qi :: blank, тогда применяется ли звезда клины для формирования пропускающего парсера? Я хотел бы получить немного просветления здесь, может быть, это также поможет в решении этой проблемы.

Дополнительная информация: мой настоящий парсер выглядит примерно так:

one   = char_("X") >> repeat(2)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;
two   = char_("Y") >> repeat(3)[omit[+blank] >> +alnum];
three = char_("Z") >> repeat(4)[omit[+blank] >> +alnum] >> qi::omit[+qi::blank] >> +alnum;

main = one | two | three;

, что делает грамматику довольно шумной, чего я хотел бы избежать.

1 Ответ

0 голосов
/ 01 ноября 2018

Прежде всего, грамматические спецификации, в которых я обычно вижу подобные требования, являются (всегда?) RFC. В 99% случаев проблем нет, рассмотрим, например ::100100

 myrule = skip(space) [ uint_ >> uint_ ];

Это уже неявно требует как минимум 1 пробел между цифрами, по той простой причине, что в противном случае было бы 1 число. Такое же упрощение происходит в удивительно многих случаях (см., Например, упрощения, сделанные в отношении повсеместного производства ПОБВ в этом ответе на прошлой неделе Вектор последовательности значений Ци Boost.Spirit ).


Учитывая это, шкиперы применяют ноль или более раз по определению, так что нет никакого способа получить то, что вы хотите, с помощью существующей директивы с состоянием, такой как skip(). См. Также http://stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965 или документы - под lexeme, [no_]skip и skip_flag::dont_postskip).


Глядя на вашу конкретную грамматику, я бы сделал это:

bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);

Здесь вы можете добавить отрицательное предположение в лексеме, чтобы утверждать, что «достигнут конец токена» - что в вашем синтаксическом анализаторе будет обозначено как !qi::graph:

    auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);

См. Демонстрацию:

Live On Coliru

#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

int main() {
    for (std::string const str : { "ab", " ab ", " a b ", "a b" }) {
        auto iter = str.begin(), end = str.end();

        auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);

        bool r = qi::phrase_parse(iter, end, token >> token, qi::blank);

        std::cout << " --- " << std::quoted(str) << " --- ";
        if (r) {
            std::cout << "parse succeeded.";
        } else {
            std::cout << "parse failed.";
        }

        if (iter != end) {
            std::cout << " Remaining unparsed: " << std::string(iter, str.end());
        }

        std::cout << std::endl;
    }
}

Печать

 --- "ab" --- parse failed. Remaining unparsed: ab
 --- " ab " --- parse failed. Remaining unparsed:  ab 
 --- " a b " --- parse succeeded.
 --- "a b" --- parse succeeded.

БОНУС Обзор заметок

Мои рекомендации:

  1. ваш шкипер должен отвечать за грамматику. Грустно, что все образцы ци заставляют людей поверить, что вам нужно разрешить звонящему решить, что
  2. проверка конца итератора делает не равной проверкой ошибок. Очень возможно правильно разобрать вещи без использования всего ввода . Вот почему сообщение об «оставшихся входных данных» должно происходить не только в случае неудачного анализа.
  3. Если конечный неразобранный ввод является ошибкой, расшифрует его :

Live On Coliru

#include <iostream>
#include <iomanip>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

int main() {
    for (std::string const str : { "ab", " ab ", " a b ", "a b happy trees are trailing" }) {
        auto iter = str.begin(), end = str.end();

        auto token = qi::copy(qi::lexeme [ qi::char_ >> !qi::graph ]);

        bool r = qi::parse(iter, end, qi::skip(qi::space) [ token >> token >> qi::eoi ]);

        std::cout << " --- " << std::quoted(str) << " --- ";
        if (r) {
            std::cout << "parse succeeded.";
        } else {
            std::cout << "parse failed.";
        }

        if (iter != end) {
            std::cout << " Remaining unparsed: " << std::quoted(std::string(iter, str.end()));
        }

        std::cout << std::endl;
    }
}

печать

 --- "ab" --- parse failed. Remaining unparsed: "ab"
 --- " ab " --- parse failed. Remaining unparsed: " ab "
 --- " a b " --- parse succeeded.
 --- "a b happy trees are trailing" --- parse failed. Remaining unparsed: "a b happy trees are trailing"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...