Объединение правил во время выполнения и возврата правил

Я пытаюсь написать сложный парсер, созданный поверх Spirit-X3, поэтому мне нужно кое-что знать:

♦ Как комбинировать правила во время выполнения. (с уловкой Набиалека)

♦ Можно ли возвращать такие правила:

x3::rule<char> SomeFunction(std::string &str)
{
    x3::rule<char> foo;
    auto bar = baz;
    BOOST_SPIRIT_DEFINE(foo, bar);
    return foo;
}

PS: SomeFunction не будет иметь фиксированного возврата, поэтому я не могу использовать только x3 :: sequence


person NotMe    schedule 08.04.2018    source источник


Ответы (1)


Да, x3 значительно упрощает составление правил.

В основном потому, что выражения синтаксического анализатора не имеют тенденции сохранять ссылки на временные объекты при назначении переменных, как это было в эпоху Qi¹.

Ограничение: объявление парсеров с внешней связью намного сложнее ... в X3, требуя танца с макросами, которые вы показываете BOOST_SPIRIT_{DECLARE,DEFINE}.

Ваш образец

Это не сработает, потому что макрос предназначен для использования в области пространства имен. Хорошая новость в том, что вам это может не понадобиться, потому что нет необходимости объявлять правило отдельно от определения, если вы не имеете дело с рекурсивно требуемыми правилами.

В стороне x3::rule<char>, скорее всего, ошибка. char - это тип тега в этом объявлении, и это не лучший тип тега. Если вместо этого вам нужен тип атрибута, это должен быть второй аргумент шаблона.

auto SomeFunction(std::string &str)
{
    return x3::rule<struct _tag, std::string> {"dynamic"}
        = '[' >> x3::lit(str) >> ']';
}

На самом деле, я очень часто делаю небольшие фабрики на своем объекте объявлений:

template <typename Attr>
auto compose = [](auto p1, auto p2) {
     return rule<struct _, Attr> {"compose"}
         = nocase [ 
               lexeme [ "property:" << as_parser(p1) ]
               >> '='
               lexeme [ "value:" << as_parser(p2) ]
           ];                  
};

Это немного надумано, но должно дать вам идеи. Используйте это как compose<int>("number", x3::int_) или compose<std::string>("name", +x3::graph)

Некоторые вдохновляющие примеры

  • Описание оператора списка (%) в Boost.Spirit с изображением специального as<>[] объекта:

    namespace {
        template <typename T>
        struct as_type {
            template <typename Expr>
                auto operator[](Expr&& expr) const {
                    return x3::rule<struct _, T>{"as"} = x3::as_parser(std::forward<Expr>(expr));
                }
        };
    
        template <typename T> static const as_type<T> as = {};
    }
    
  • Избегайте выдачи expectation_failure при сбое парсера ожидания, что динамически составляет поиск символов:

    x3::symbols<char> const keyword = []{
        x3::symbols<char> kw;
        kw += "for","begin","end","function","while","break","switch";
        return kw;
    }();
    
  • Динамическое переключение таблиц символов в x3, которое является очень полный пример со многими фабриками парсеров:

    // (case insensitive) keyword handling
    static auto kw        = [](auto p) { return x3::lexeme[p >> !(x3::graph - x3::char_("/=,()"))]; };
    static auto ikw       = [](auto p) { return x3::no_case [kw(p)]; };
    static auto qualifier = [](auto p) { return x3::lexeme['/' >> ikw(p)]; };
    

    И даже показывает, как переопределить as_spirit_parser для ваших собственных типов:

    // Options and CiOptions
    namespace util {
        template <typename Tag>
        auto as_spirit_parser(Options<Tag> const& o, bool to_lower = false) {
            x3::symbols<typename Options<Tag>::type> p;
            int n = 0;
            for (std::string el : o._options) {
                if (to_lower) boost::to_lower(el);
                p.add(el, n++);
            }
            return kw(p);
        }
    
        template <typename Tag>
        auto as_spirit_parser(IcOptions<Tag> const& o) {
            return x3::no_case [ as_spirit_parser(o, true) ];
        }
    }
    

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

        auto set = [](auto member, auto p) {
            auto propagate = [member](auto& ctx) {
                traits::move_to(_attr(ctx), _val(ctx).*(member));
            };
            return as_parser(p)[propagate];
        };
    
        using T = ast::ShowSymbolsCommand;;
        return qualifier("all")  >> set(&T::all, attr(true))
             | qualifier("full") >> set(&T::full, attr(true))
             | qualifier("out")  >> set(&T::out, '=' >> Filespec)
             | qualifier("type") >> set(&T::types, '=' >> SymbolTypes)
             | set(&T::wildcard, Wildcard);
    

Я настоятельно рекомендую вам использовать эти примеры по отдельности, чтобы понять, насколько мощна компоновка X3. Я бы подумал о воссоздании чего-то вроде qi::lazy в X3 только тогда, когда вы / действительно / потребуете этого.


¹ или вообще что-нибудь на основе Proto, например, Phoenix

person sehe    schedule 08.04.2018
comment
Связаны несколько вдохновляющих примеров, которые должны дать вам хорошее представление о том, как вы можете создавать синтаксические анализаторы в X3 - person sehe; 09.04.2018
comment
Только что вспомнил, что писать собственные парсеры также намного проще в x3. Вот 3 сообщения, содержащие примеры этого - person sehe; 09.04.2018