Boost Spirit x3 : 복합 속성 컴파일 시간 오류 (열거 형 클래스)

Jan 13 2021

최근에 boost spirit x3을 사용하여 가능한 가장 간단한 파서를 작성했습니다 . 여기에는 식별자 와 단일 문자 연산자의 두 가지 규칙이 있습니다 . 당연히 연산자 유형을 생성하는 기호 테이블을 사용 하여 연산자 를 구현했습니다 enum class. 식별자std::strings 로 구문 분석됩니다 . 그러나 코드는 식별자와 연산자를 단일 파서로 결합 할 때 컴파일을 거부합니다 (질문 끝에있는 코드 부분 참조).

정수로 연산자 유형 열거 형을 변경하면 모든 것이 잘 작동합니다. 연산자와 식별자도 분리 될 때 잘 구문 분석됩니다.

템플릿 오류 메시지는 첨부하기에는 상당히 크고 이해하기에는 너무 모호하지만 .NET의 구성 / 이동 의미와 관련이 있다고 생각합니다 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;
}

복합 파서를 작성할 때 함정이 있습니까? 내가 무엇을 놓치고 있습니까? 시간 내 줘서 고마워.

답변

2 sehe Jan 13 2021 at 10:26

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}