Boost spirit x3: errore del tempo di compilazione dell'attributo composto (classe enum)

Jan 13 2021

Recentemente stavo scrivendo un parser il più semplice possibile usando boost spirit x3 . Contiene 2 regole: identificatore e un operatore a carattere singolo . Naturalmente, ho implementato l' operatore utilizzando una tabella dei simboli, che produce un tipo di operatore enum class. Gli identificatori vengono analizzati come std::strings. Tuttavia, il codice si rifiuta di compilare quando si combinano identificatori e operatori in un singolo parser (vedere la parte di codice alla fine della domanda).

Nota che se cambi il tipo di operatore enum con un numero intero, tutto funziona correttamente. Anche gli operatori e gli identificatori vengono analizzati bene quando sono separati.

Il messaggio di errore del modello è abbastanza grande per essere allegato e troppo oscuro per me da capire, ma sospetto che abbia qualcosa a che fare con la semantica di costruzione / spostamento std::variant<std::string, OperType>. Tuttavia, enum classnon dovrebbe essere drasticamente diverso dalla pianura int. Ha qualcosa a che fare con enum classil costruttore predefinito? Come questo può essere aggirato?

Ecco il 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;
}

Ci sono delle insidie ​​durante la scrittura di parser composti? Cosa mi sto perdendo? Grazie per il tuo tempo.

Risposte

2 sehe Jan 13 2021 at 10:26

std::variant non è ancora supportato per la compatibilità degli attributi.

Il passaggio a ne boost::variantfa la compilazione: Compiler Explorer

In alternativa, ecco i modi per farlo funzionare realmente se hai bisogno di std :: variant: Transitioning Boost Spirit parser from boost :: variant to 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));
    }
}

Stampe

Parsed: {iden1, plus, minus, iden2}