Something seems to have changed in spirit::x3 that breaks my fancy little asdl parser. It worked just fine when I ported it over from Qi (after the initial x3 bugfixes made thei
Like in the other linked answers, the attribute propagation/compatibility rules aren't the same as in Qi.
Sometimes this is surprising, sometimes it might be a bug.
YOu can always make the attribute type explicit by wrapping in a rule. Almost all of the time this fixes the bug.
Note: in Boost 1.66.0 there is a regression that was fixed for later versions: How to make a recursive rule in boost spirit x3 in VS2017
In your case, the id rule don't magically assign to a string. Help it a bit:
auto const typ_id = as<std::string>(x3::lexeme[ascii::lower >> *alpha_num]);
auto const con_id = as<std::string>(x3::lexeme[ascii::upper >> *alpha_num]);
auto const id = typ_id | con_id;
Where as
is a simple helper:
template <typename T> static auto as = [](auto p) { return x3::rule<struct tag, T> {"as"} = p; };
Note that the code can be a bit simpler:
Live On Coliru
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace asdl { namespace ast {
struct Field {
std::string type;
boost::optional<char> flag;
boost::optional<std::string> name;
};
typedef std::vector<asdl::ast::Field> field_vec;
struct Constructor {
std::string name;
boost::optional<field_vec> fields;
};
struct Sum {
std::vector<Constructor> types;
boost::optional<field_vec> attributes;
};
struct Product {
field_vec fields;
};
typedef boost::variant<Product, Sum> type_variant;
struct Type {
std::string name;
type_variant value;
};
struct Module {
std::string id;
std::vector<Type> dfns;
};
} }
BOOST_FUSION_ADAPT_STRUCT(asdl::ast::Module, id, dfns)
BOOST_FUSION_ADAPT_STRUCT(asdl::ast::Type, name, value)
BOOST_FUSION_ADAPT_STRUCT(asdl::ast::Field, type, flag, name)
BOOST_FUSION_ADAPT_STRUCT(asdl::ast::Constructor, name, fields)
BOOST_FUSION_ADAPT_STRUCT(asdl::ast::Sum, types, attributes)
BOOST_FUSION_ADAPT_STRUCT(asdl::ast::Product, fields)
namespace asdl
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using ast::type_variant;
using ast::field_vec;
x3::rule<class module, ast::Module > const module = "module";
x3::rule<class definitions, ast::Type > const definitions = "definitions";
x3::rule<class type, type_variant > const type = "type";
x3::rule<class fields, field_vec > const fields = "fields";
x3::rule<class field, ast::Field > const field = "field";
x3::rule<class product, ast::Product > const product = "product";
x3::rule<class sum, ast::Sum > const sum = "sum";
x3::rule<class constructor, ast::Constructor> const constructor = "constructor";
template <typename T> static auto as = [](auto p) { return x3::rule<struct tag, T> {"as"} = p; };
auto const alpha = '_' | ascii::upper | ascii::lower;
auto const alpha_num = alpha | ascii::digit;
auto const typ_id = as<std::string>(x3::lexeme[ascii::lower >> *alpha_num]);
auto const con_id = as<std::string>(x3::lexeme[ascii::upper >> *alpha_num]);
auto const id = typ_id | con_id;
auto const module_def = "module" >> id >> '{' >> *definitions >> '}';
auto const definitions_def = typ_id >> '=' >> type;
auto const type_def = product | sum;
auto const product_def = fields;
auto const sum_def = constructor % '|' >> -("attributes" >> fields);
auto const constructor_def = con_id >> -fields;
auto const fields_def = '(' >> field % ',' >> ')';
auto const field_def = typ_id >> -(ascii::char_('?') | ascii::char_('*')) >> -id;
BOOST_SPIRIT_DEFINE(module, definitions, type, product, sum, constructor, fields, field)
bool parse(const std::string &input, ast::Module &mod) {
std::string::const_iterator iter = input.begin();
std::string::const_iterator end = input.end();
return x3::phrase_parse(iter, end, module >> x3::eoi, ascii::space, mod);
}
} //namespace asdl
int main() {
asdl::ast::Module mod;
asdl::parse("", mod);
}
Since you're not using recursive rules, there's no actual need for _def
and BOOST_SPIRIT_DEFINE
etc:
Live On Coliru
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using ast::type_variant;
using ast::field_vec;
template <typename T> static auto as = [](auto p) { return x3::rule<struct tag, T> {"as"} = p; };
auto const alpha = '_' | ascii::upper | ascii::lower;
auto const alpha_num = alpha | ascii::digit;
auto const typ_id = as<std::string>(x3::lexeme[ascii::lower >> *alpha_num]);
auto const con_id = as<std::string>(x3::lexeme[ascii::upper >> *alpha_num]);
auto const id = typ_id | con_id;
auto const field
= x3::rule<class constructor, ast::Field> {"field"}
= as<ast::Field>(typ_id >> -(ascii::char_('?') | ascii::char_('*')) >> -id);
auto const fields
= x3::rule<class sum, field_vec>{ "fields" }
= '(' >> field % ',' >> ')';
auto const constructor
= x3::rule<class product, ast::Constructor>{ "constructor" }
= as<ast::Constructor>(con_id >> -fields);
auto const sum
= x3::rule<class field, ast::Sum>{ "sum" }
= as<ast::Sum>(constructor % '|' >> -("attributes" >> fields));
auto const product
= x3::rule<class fields, ast::Product>{ "product" }
= as<ast::Product>(fields);
auto const type
= x3::rule<class type, type_variant>{ "type" }
= product | sum;
auto const definitions
= x3::rule<class definitions, ast::Type>{ "definitions" }
= as<ast::Type>(typ_id >> '=' >> type);
auto const module
= x3::rule<class module, ast::Module>{ "module" }
= "module" >> id >> '{' >> *definitions >> '}';
bool parse(const std::string &input, ast::Module &mod) {
std::string::const_iterator iter = input.begin();
std::string::const_iterator end = input.end();
return x3::phrase_parse(iter, end, module >> x3::eoi, ascii::space, mod);
}