Boost Spirit x3: erreur de compilation de l'attribut composé (classe enum)

Jan 13 2021

J'écrivais récemment un analyseur syntaxique le plus simple possible en utilisant boost spirit x3 . Il contient 2 règles: un identifiant et un opérateur à un seul caractère . Naturellement, j'ai implémenté l' opérateur en utilisant une table de symboles, qui produit un type d'opérateur enum class. Les identificateurs sont analysés comme std::strings. Cependant, le code refuse de se compiler lors de la combinaison d'identifiants et d'opérateurs en un seul analyseur (voir le morceau de code à la fin de la question).

Notez que si vous modifiez le type d'opérateur enum avec un entier, tout fonctionne correctement. Les opérateurs et les identificateurs sont également bien analysés lorsqu'ils sont séparés.

Le message d'erreur du modèle est assez gros pour être joint et trop obscur pour que je le comprenne, mais je soupçonne qu'il a quelque chose à voir avec la construction / déplacement sémantique de std::variant<std::string, OperType>. Cependant, enum classne devrait pas être radicalement différent de la plaine int. Cela a-t-il quelque chose à voir avec enum classle constructeur par défaut? Comment cela peut-il être contourné?

Voici le codepiece

#include <variant>
#include <string>

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

namespace x3 = boost::spirit::x3;

auto addCharacter = [](auto &context) {
    x3::_val(context).push_back(x3::_attr(context));
};

x3::rule<class IdentifierTag, std::string> identifier{"identifier"};
const auto identifier_def = x3::lexeme[x3::char_("a-zA-Z")[addCharacter] >> *(x3::char_("a-zA-Z0-9")[addCharacter])];

BOOST_SPIRIT_DEFINE(identifier);

enum class OperType
{
    plus,
    minus
};

struct Opers_ : x3::symbols<OperType>
{
    Opers_()
    {
        add("+", OperType::plus)("-", OperType::minus);
    }
} opers_;

x3::rule<class OperTypeTag, OperType> oper{"operator"};
const auto oper_def = x3::lexeme[opers_];

BOOST_SPIRIT_DEFINE(oper);

int main()
{
    std::string input{"iden1 + - iden2"};

    std::vector<std::variant<std::string, OperType>> tokens;

    auto start = input.cbegin();
    auto result = x3::phrase_parse(start, input.cend(), (+(identifier | oper)), x3::space, tokens);

    return 0;
}

Existe-t-il des pièges lors de l'écriture d'analyseurs composés? Qu'est-ce que je rate? Merci pour votre temps.

Réponses

2 sehe Jan 13 2021 at 10:26

std::variant n'est pas encore pris en charge pour la compatibilité des attributs.

Changer pour le boost::variantfaire compiler: Explorateur de compilateurs

Alternativement, voici des moyens de le faire fonctionner réellement si vous avez besoin de std :: variant: Transition de l'analyseur Boost Spirit de boost :: variant à std :: variant

#include <boost/spirit/home/x3.hpp>
#include <variant>
#include <fmt/ranges.h>
#include <fmt/ostream.h>

namespace x3 = boost::spirit::x3;

auto addCharacter = [](auto& context) {
    x3::_val(context).push_back(x3::_attr(context));
};

x3::rule<class IdentifierTag, std::string> identifier{"identifier"};
const auto identifier_def =
    x3::lexeme[x3::char_("a-zA-Z")[addCharacter] >> *(x3::char_("a-zA-Z0-9")[addCharacter])];

BOOST_SPIRIT_DEFINE(identifier)

enum class OperType
{
    plus,
    minus
};

static inline std::ostream& operator<<(std::ostream& os, OperType ot) {
    switch(ot) {
        case OperType::plus: return os << "plus";
        case OperType::minus: return os << "minus";
    }
    return os << "?";
}

struct Opers_ : x3::symbols<OperType>
{
    Opers_()
    {
        add("+", OperType::plus)
           ("-", OperType::minus);
    }
} opers_;

x3::rule<class OperTypeTag, OperType> oper{"operator"};
const auto oper_def = x3::lexeme[opers_];

BOOST_SPIRIT_DEFINE(oper)

int main() {
    std::string const input{"iden1 + - iden2"};

    std::vector<boost::variant<std::string, OperType>> tokens;

    auto f = input.begin(), l = input.end();
    auto result = x3::phrase_parse(
            f, l,
            +(identifier | oper),
            x3::space,
            tokens);

    if (result) {
        fmt::print("Parsed: {}\n", tokens);
    } else {
        fmt::print("Parse failed\n");
    }

    if (f!=l) {
        fmt::print("Remaining: '{}'\n", std::string(f,l));
    }
}

Impressions

Parsed: {iden1, plus, minus, iden2}