Boost Spirit X3:“属性没有预期的大小" redux [英] Boost Spirit X3: “Attribute does not have the expected size” redux
问题描述
似乎精神上发生了一些变化:: x3破坏了我喜欢的小asdl解析器.当我从Qi移植过来时,它工作得很好(在最初的x3错误修正进入fedora之后),但现在失败了:
/usr/include/boost/spirit/home/x3/operator/detail/sequence.hpp:143:9:错误:静态断言失败:属性没有预期的大小.
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include "asdl.h"
typedef boost::variant<asdl::ast::Product, asdl::ast::Sum> type_variant;
typedef std::vector<asdl::ast::Field> field_vec;
BOOST_FUSION_ADAPT_STRUCT(
asdl::ast::Module,
(std::string, id)
(std::vector<asdl::ast::Type>, dfns))
BOOST_FUSION_ADAPT_STRUCT(
asdl::ast::Type,
(std::string, name)
(type_variant, value))
BOOST_FUSION_ADAPT_STRUCT(
asdl::ast::Field,
(std::string, type)
(boost::optional<char>, flag)
(boost::optional<std::string>, name))
BOOST_FUSION_ADAPT_STRUCT(
asdl::ast::Constructor,
(std::string, name)
(boost::optional<field_vec>, fields))
BOOST_FUSION_ADAPT_STRUCT(
asdl::ast::Sum,
(std::vector<asdl::ast::Constructor>, types)
(boost::optional<field_vec>, attributes))
BOOST_FUSION_ADAPT_STRUCT(
asdl::ast::Product,
(field_vec, fields))
namespace asdl
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
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";
auto const alpha = '_' | ascii::upper | ascii::lower;
auto const alpha_num = alpha | ascii::digit;
auto const typ_id = x3::lexeme[ascii::lower >> *alpha_num];
auto const con_id = 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, ascii::space, mod) && iter == end);
}
} //namespace asdl
注意:BOOST_FUSION_ADAPT_STRUCT是具有完全相同字段的裸结构的直接包装.
我搜索了所有其他(2)个有关属性没有预期大小"的引用,但对问题的理解不够,甚至没有开始修复代码-问题的一部分是程序生成了ast它解析为(按照他们的说法,吃自己的dogfood),所以不知道为什么断言失败,我不知道要修改哪个,解析器或输出ast结构的代码.
在这里,我认为牛已经剃光了...
就像其他链接的答案一样,属性传播/兼容性规则与Qi中的规则不同.
有时候这很令人惊讶,有时可能是一个错误.
YOu始终可以通过包装规则来使属性类型显式.几乎所有时间都可以修复该错误.
注意:在Boost 1.66.0中,有一个针对更高版本修复的回归:
演示
请注意,代码可能会更简单:
#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); }
由于您没有使用递归规则,因此实际上不需要
_def
和BOOST_SPIRIT_DEFINE
等>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); }
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 their way into fedora) but now fails with:
/usr/include/boost/spirit/home/x3/operator/detail/sequence.hpp:143:9: error: static assertion failed: Attribute does not have the expected size.
#include <boost/spirit/home/x3.hpp> #include <boost/fusion/include/adapt_struct.hpp> #include "asdl.h" typedef boost::variant<asdl::ast::Product, asdl::ast::Sum> type_variant; typedef std::vector<asdl::ast::Field> field_vec; BOOST_FUSION_ADAPT_STRUCT( asdl::ast::Module, (std::string, id) (std::vector<asdl::ast::Type>, dfns)) BOOST_FUSION_ADAPT_STRUCT( asdl::ast::Type, (std::string, name) (type_variant, value)) BOOST_FUSION_ADAPT_STRUCT( asdl::ast::Field, (std::string, type) (boost::optional<char>, flag) (boost::optional<std::string>, name)) BOOST_FUSION_ADAPT_STRUCT( asdl::ast::Constructor, (std::string, name) (boost::optional<field_vec>, fields)) BOOST_FUSION_ADAPT_STRUCT( asdl::ast::Sum, (std::vector<asdl::ast::Constructor>, types) (boost::optional<field_vec>, attributes)) BOOST_FUSION_ADAPT_STRUCT( asdl::ast::Product, (field_vec, fields)) namespace asdl { namespace x3 = boost::spirit::x3; namespace ascii = boost::spirit::x3::ascii; 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"; auto const alpha = '_' | ascii::upper | ascii::lower; auto const alpha_num = alpha | ascii::digit; auto const typ_id = x3::lexeme[ascii::lower >> *alpha_num]; auto const con_id = 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, ascii::space, mod) && iter == end); } } //namespace asdl
Note: BOOST_FUSION_ADAPT_STRUCT is a straight wrapper around bare structs with exactly the same fields.
I searched all (2) other references to "Attribute does not have the expected size" but don't quite understand the problem enough to even begin to fix the code -- part of the problem is the program generates the ast that it parses into (eat your own dogfood as they say) so without knowing why the assertion fails I don't know which to modify, the parser or the code that outputs the ast structs.
And here I thought that yak was done shaved...
解决方案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; };
Demo
Note that the code can be a bit simpler:
#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
andBOOST_SPIRIT_DEFINE
etc: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); }
这篇关于Boost Spirit X3:“属性没有预期的大小" redux的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!