首页 > 解决方案 > Boost Spirit x3:复合属性编译时错误(枚举类)

问题描述

我最近正在使用boost spirit x3编写一个最简单的解析器。它包含 2 条规则:标识符和单字符运算符。自然地,我使用符号表实现了运算符enum class,它产生了一个运算符类型。标识符被解析为std::strings。但是,当将标识符和运算符组合到单个解析器中时,代码拒绝编译(请参阅问题末尾的代码段)。

请注意,如果您将运算符类型枚举更改为整数,则一切正常。运算符和标识符在分开时也能很好地解析。

模板错误消息很大,无法附加,而且太晦涩难懂,但我怀疑它与std::variant<std::string, OperType>. 但是,enum class应该不会与平原有太大的不同int。它与enum class默认构造函数有什么关系吗?如何绕过?

这是代码片段

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

编写复合解析器时有什么陷阱吗?我错过了什么?谢谢你的时间。

标签: c++boostboost-spiritboost-spirit-x3

解决方案


std::variant尚不支持属性兼容性。

更改为boost::variant使其编译:编译器资源管理器

或者,如果您需要std::variant ,以下是使其实际工作的方法:将Boost Spirit 解析器从 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));
    }
}

印刷

Parsed: {iden1, plus, minus, iden2}

推荐阅读