Генератор парсеров Generi c в boost :: spirit :: x3 - PullRequest
1 голос
/ 10 февраля 2020

Я пытаюсь написать универсальный c генератор парсера в духе буста. Я придумал следующий код:

auto attr_to_val = [](auto& ctx) { _val(ctx) = boost::fusion::at_c<2>(_attr(ctx)); };

auto parser_gen = [](const std::string a, auto&& p) {
    return((boost::spirit::x3::string(a) >> boost::spirit::x3::blank >> p)[attr_to_val]);
};

и попытался использовать его следующим образом:

int a;
auto action = [&a](auto& ctx) { a = _val(ctx); };
auto parser = (parser_gen("aaa", boost::spirit::x3::uint_))[action];
parse(bar.begin(), bar.end(), parser);

, но он дает много ошибок из-за невозможности конвертировать boost::fusion::deque в int. С другой стороны, когда я немного его изменяю, это ИМХО эквивалентно расширению кода шаблона выше:

auto pars = (
    boost::spirit::x3::string("aaa") >>
    boost::spirit::x3::blank >> boost::spirit::x3::uint_)[attr_to_val];

int a;
auto action = [&a](auto& ctx) { a = _val(ctx); };
parse(bar.begin(), bar.end(), pars);

Все нормально. Что я делаю не так и как я могу заставить parser_gen работать?

1 Ответ

2 голосов
/ 11 февраля 2020
  1. Вам не нужно открывать все атрибуты, значительно упрощая типы атрибутов.

  2. Для сопоставления строкового литерала без предоставления его в качестве ключа (который по-видимому, вы все равно не хотите, потому что вы игнорируете это в действии semanti c), используйте x3::lit("aaa") вместо x3::string("aaa"). В выражении x3 голое "aaa" будет автоматически интерпретироваться как x3::lit("aaa") (из-за x3::as_parser).

  3. Более того, вы обращаетесь к at_c<2>, подразумевая, что вы не x3::blank тоже не подвергается. Почему не просто x3::omit[x3::blank]? Еще лучше рассмотреть возможность использования шкипера и иметь это неявное значение.

  4. В action вы используете x3::_val, что зависит от атрибута объявленного правила (нет x3 :: правило в поле зрения?) ИЛИ фактическая привязанная ссылка (вы ничего не передаете x3::parse).

    Поскольку ваше действие связывается с аргументом синтаксического анализатора, кажется, вы хотели его атрибут, который можно запросить с помощью x3::_attr().

    Кажется, что вы можете вообще обойтись без semanti c действий, см. ниже

Исправление идей:

Это сочетает в себе все вышеперечисленное:

Посмотреть Live On Coliru

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

namespace x3 = boost::spirit::x3;

int main() {
    auto parser_gen = [=](std::string const a, auto&& p) {
        return x3::skip(x3::blank)[ x3::lit(a) >> p ];
    };

    for (std::string const bar : { "aaa 42", "aaa99", }) {
        int a;
        if (parse(begin(bar), end(bar), parser_gen("aaa", x3::uint_), a)) {
            std::cout << "Parsed " << a << "\n";
        } else {
            std::cout << "Failed\n";
        }
    }
}

Печать

Parsed 42
Parsed 99

Посмотреть Live On Coliru

namespace {
    template <typename P>
    auto label_gen(P p) {
        return x3::omit[ x3::lexeme[ x3::as_parser(p) >> (&x3::punct | !x3::graph) ] ];
    }

    template <typename L, typename P> auto parser_gen(L l, P p) {
        return x3::skip(x3::blank)[ label_gen(l) >> p ];
    }
}

Теперь печатается на один матч меньше:

Parsed 42
Failed

БОНУС: Выполнение Полезный материал

Итак, я думаю, вы хотели объединить несколько из этих пар метка / значение полезным способом, возможно, объясняя действия. Теперь вы можете взять страницу из этого ответа: Boost Spirit x3: разобрать в структуры .

На самом деле, я воздержусь от воспроизведения кода из этого примера здесь, но я думаю, что это может примените к вашему случаю использования очень хорошо.

...