I was recently writing a simplest possible parser using boost spirit x3. It contains 2 rules: identifier and a single character operator. Naturally, I implemented the operator using a symbol table, which produces an operator type enum class
. Identifiers are parsed as std::string
s. However, the code refuses to compile when combining identifiers and operators into a single parser (see the code piece at the end of the question).
Note, that if you change operator type enum with an integer, everything works fine. Operators and identifiers are parsed well when separate too.
The template error message is quite big to be attached and too obscure for me to understand, but I suspect it has something to do with construction/move semantic of std::variant<std::string, OperType>
. However, enum class
should not be drastically different from the plain int
. Does it have anything to do with enum class
default constructor? How this can be bypassed?
Here is the 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;
}
Are there any pitfalls when writing compound parsers? What am I missing? Thanks for your time.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…