Boost.Spirit.Qi: динамически создавать «разницу» парсер во время синтаксического анализа

Разностный парсер может быть создан двоичным -(минус) оператор:

rule = qi::char_ - qi::lit("}}")

или даже сложные различия:

rule = qi::char_ - qi::lit("}}") - qi::lit("]]")

Но как я мог сгенерировать весь результат анализатора различий во время разбора?
Я предполагаю, что это может быть какая-то форма, как показано ниже:

phoenix::function<difference_parser_impl> difference_parser;
rule = qi::lazy(difference_parser(qi::char_, {"}}", "]]"}));

Здесь {..., ..., ...} part на самом деле будет контейнером stl, но это не главное; Я могу справиться с этой частью.

Я нашел шаблон qi::difference<Left, Right> — но я не мог понять, как его использовать.

2

Решение

Мне кажется, вы не столько ищете динамическое выражение «различие», сколько динамическое выражение «альтернативная переменная (a | b | c …)»:

expr - a - b - c эквивалентно expr - (a|b|c)

Вы можете легко добиться разницы, используя либо:

expr - orCombine(alternatives)

или же

!orCombine(alternatives) >> expr

Теперь у этого есть много грубых краев, которые я объясню сначала. К счастью, есть более простой способ, используя qi::symbols, который я продемонстрирую сразу после этого.

Хитрый материал

Если вы хотите, вы можете «генерировать» альтернативные выражения синтаксического анализатора по требованию, с небольшим количеством волшебства. Я показал, как это сделать в этом ответе:

Но

  1. это чревато подводными камнями (поскольку прото-выражения плохо поддаются копированию)1
  2. это удобно использовать вариадики, чтобы избежать промежуточного хранения (обратите внимание на deepcopy_ для защиты от неопределенного поведения):

    template<typename ...Expr>
    void parse_one_of(Expr& ...expressions)
    {
    auto parser = boost::fusion::fold(
    boost::tie(expressions...),
    qi::eps(false),
    deepcopy_(arg2 | arg1)
    );
    

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

Итак, вместо этого я рекомендую попробовать & Истинный подход, который «злоупотребляет» существующим «динамическим» парсером:

Упростить использование qi::symbols

Эта идея безнадежно заимствует у знаменитого «Nabialek Trick». Он использует qi :: символы, и, следовательно, имеет отличные характеристики производительности во время выполнения2.

Без дальнейших церемоний, это пример того, как вы можете использовать его, начиная с вектора строковых литералов:

template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, std::string(), Skipper>
{
parser() : parser::base_type(start)
{
static const std::vector<std::string> not_accepted { "}}", "]]" };

using namespace qi;
exclude = exclusions(not_accepted);
start = *(char_ - exclude);

BOOST_SPIRIT_DEBUG_NODE(start);
}

private:
qi::rule<It, std::string(), Skipper> start;

typedef qi::symbols<char, qi::unused_type> Exclude;
Exclude exclude;

template<typename Elements>
Exclude exclusions(Elements const& elements) {
Exclude result;

for(auto& el : elements)
result.add(el);

return result;
}
};

Полный рабочий образец этого здесь: http://coliru.stacked-crooked.com/view?id=ddbb2549674bfed90e3c8df33b048574-7616891f9fd25da6391c2728423de797 и это печатает

parse success
data: 123
trailing unparsed: ']] 4'

Полный код

Для дальнейшего использования:

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

namespace qi    = boost::spirit::qi;

template <typename It, typename Skipper = qi::space_type>
struct parser : qi::grammar<It, std::string(), Skipper>
{
parser() : parser::base_type(start)
{
static const std::vector<std::string> not_accepted { "}}", "]]" };

using namespace qi;
exclude = exclusions(not_accepted);
start = *(char_ - exclude);

BOOST_SPIRIT_DEBUG_NODE(start);
}

private:
qi::rule<It, std::string(), Skipper> start;

typedef qi::symbols<char, qi::unused_type> Exclude;
Exclude exclude;

template<typename Elements>
Exclude exclusions(Elements const& elements) {
Exclude result;

for(auto& el : elements)
result.add(el);

return result;
}
};

int main()
{
const std::string input = "1 2 3]] 4";
typedef std::string::const_iterator It;
It f(begin(input)), l(end(input));

parser<It> p;
std::string data;

bool ok = qi::phrase_parse(f,l,p,qi::space,data);
if (ok)
{
std::cout << "parse success\n";
std::cout << "data: " << data << "\n";
}
else std::cerr << "parse failed: '" << std::string(f,l) << "'\n";

if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
}

1 Я полагаю, что эта проблема скоро будет устранена в новой версии Spirit (в настоящее время она называется «Spirit X3» для экспериментальной версии).

2 Оно использует Пытается искать спички

2

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector