Boost spirit x3: error de tiempo de compilación de atributo compuesto (clase de enumeración)

Jan 13 2021

Recientemente estaba escribiendo un analizador sintáctico más simple posible usando boost spirit x3 . Contiene 2 reglas: identificador y un operador de un solo carácter . Naturalmente, implementé el operador usando una tabla de símbolos, que produce un tipo de operador enum class. Los identificadores se analizan como std::strings. Sin embargo, el código se niega a compilar cuando se combinan identificadores y operadores en un solo analizador (consulte el fragmento de código al final de la pregunta).

Tenga en cuenta que si cambia el tipo de operador enum con un número entero, todo funciona bien. Los operadores e identificadores también se analizan bien cuando están separados.

El mensaje de error de la plantilla es bastante grande para adjuntarlo y demasiado oscuro para que lo entienda, pero sospecho que tiene algo que ver con la semántica de construcción / movimiento de std::variant<std::string, OperType>. Sin embargo, enum classno debe ser drásticamente diferente de la llanura int. ¿Tiene algo que ver con enum classel constructor predeterminado? ¿Cómo se puede evitar esto?

Aquí está la pieza de código

#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;
}

¿Existen errores al escribir analizadores compuestos? ¿Qué me estoy perdiendo? Gracias por tu tiempo.

Respuestas

2 sehe Jan 13 2021 at 10:26

std::variant aún no se admite para la compatibilidad de atributos.

Cambiar a boost::varianthace que se compile: Explorador del compilador

Alternativamente, aquí hay formas de hacer que realmente funcione si necesita std :: variant: Transición del analizador Boost Spirit de boost :: variant a 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));
    }
}

Huellas dactilares

Parsed: {iden1, plus, minus, iden2}