Spirit.X3 с лямбдой, возвращающей разные типы парсеров - PullRequest
0 голосов
/ 21 мая 2018

Здесь я пытаюсь преобразовать строковый литерал в число, в котором базовый спецификатор является динамическим:

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

namespace ast {
    struct literal {
        enum base_specifier { bin, oct, hex };

        base_specifier  base;
        std::string     literal;
    };
}

namespace x3 = boost::spirit::x3;


template<typename T>
auto as = [](auto p) { return x3::rule<struct _, T>{} = x3::as_parser(p); };


template <typename TargetT>
std::pair<bool, TargetT> convert(ast::literal const& node)
{
    auto const parse = [](ast::literal::base_specifier base, auto const& literal) {

        using base_specifier = ast::literal::base_specifier;

        auto const parser = [](base_specifier base) {
            switch(base) {
                case base_specifier::bin: {
                    using parser_type = x3::uint_parser<TargetT, 2>;
                    parser_type const p = parser_type{};
                    return as<TargetT>( p );
                }
                case base_specifier::oct: {
                    using parser_type = x3::uint_parser<TargetT, 8>;
                    parser_type const p = parser_type{};
                    return as<TargetT>( p );
                }
                case base_specifier::hex: {
                    using parser_type = x3::uint_parser<TargetT, 16>;
                    parser_type const p = parser_type{};
                    return as<TargetT>( p );
                }
                default:
                    abort();
            }
        };

        auto iter      = std::begin(literal);
        auto const end = std::cend(literal);
        TargetT attribute;

        bool parse_ok  = x3::parse(iter, end, parser(base), attribute);

        return std::make_tuple(parse_ok && (iter == end), attribute);
    };

    // other complex stuff here

    return parse(node.base, node.literal);
}


int main()
{
    ast::literal literal{ ast::literal::hex, "AFFE" };
    auto const [parse_ok, result] = convert<int32_t>(literal);
}

, но не удается выполнить:

error: return type 'rule_definition<_, uint_parser<[...], 8, [2 * ...]>, [2 * ...]>' must match previous return type
  'rule_definition<_, uint_parser<[...], 2, [2 * ...]>, [2 * ...]>' when lambda expression has unspecified explicit return type

Сообщение об ошибке ясноно у меня нет решения, чтобы получить желаемое поведение.От динамического выбора типа парсера на основе базового спецификатора зависят другие операции, поэтому этот подход для моего варианта использования полезен.Это было бы особенно полезно, если решение также работает с реальными / двойными типами и их политиками.Я предполагаю, что это скорее проблема C ++ по духу.

Кстати, можно ли сохранить конкретный парсер таким образом?Требуется копия экземпляра, которая может быть неэффективной, не так ли?Существуют ли другие / более эффективные способы обработки обнаружения / переполнения TargetT при простом сбое анализа?

Для удобства код также можно найти по адресу Wandbox .

1 Ответ

0 голосов
/ 01 июля 2018

То, что вы пытаетесь выполнить, не представляется возможным, потому что:

returning different parser types

^ Это не разрешено языком C ++.


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

Если вы пришли в X3 из Ци, я понимаю вашу путаницу,В Qi rule делает фактический парсер и сохраняет его в boost::function (при этом теряется всякая статическая информация и подразумевается стоимость вызова виртуальной функции), но X3 rule ближе к subrule Qi.

...