Boost Spirit x3: Fehler bei der Kompilierungszeit des zusammengesetzten Attributs (Aufzählungsklasse)
Ich habe kürzlich einen möglichst einfachen Parser mit Boost Spirit x3 geschrieben . Es enthält zwei Regeln: einen Bezeichner und einen einzelnen Zeichenoperator . Natürlich habe ich den Operator mithilfe einer Symboltabelle implementiert, die einen Operatortyp erzeugt enum class
. Bezeichner werden als std::string
s analysiert . Der Code weigert sich jedoch zu kompilieren, wenn Bezeichner und Operatoren in einem einzigen Parser kombiniert werden (siehe den Code am Ende der Frage).
Beachten Sie, dass alles einwandfrei funktioniert, wenn Sie den Operatortyp enum mit einer Ganzzahl ändern. Operatoren und Bezeichner werden auch dann gut analysiert, wenn sie getrennt sind.
Die Vorlagenfehlermeldung ist ziemlich groß, um angehängt zu werden, und zu dunkel, als dass ich sie verstehen könnte, aber ich vermute, dass sie etwas mit der Konstruktions- / Verschiebungssemantik von zu tun hat std::variant<std::string, OperType>
. Sollte sich enum class
jedoch nicht drastisch von der Ebene unterscheiden int
. Hat es etwas mit dem enum class
Standardkonstruktor zu tun ? Wie kann dies umgangen werden?
Hier ist das Codestück
#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;
}
Gibt es Fallstricke beim Schreiben von zusammengesetzten Parsern? Was vermisse ich? Vielen Dank für Ihre Zeit.
Antworten
std::variant
wird aus Gründen der Attributkompatibilität noch nicht unterstützt.
Durch Ändern in boost::variant
wird es kompiliert: Compiler Explorer
Alternativ gibt es folgende Möglichkeiten, damit es tatsächlich funktioniert, wenn Sie std :: variante benötigen : Übergang des Boost Spirit-Parsers von boost :: variante zu std :: variante
#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));
}
}
Druckt
Parsed: {iden1, plus, minus, iden2}