Boost Spirit X3:“属性没有预期的大小" redux [英] Boost Spirit X3: “Attribute does not have the expected size” redux

查看:49
本文介绍了Boost Spirit X3:“属性没有预期的大小" 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中,有一个针对更高版本修复的回归:

演示

请注意,代码可能会更简单:

在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);
}

由于您没有使用递归规则,因此实际上不需要_defBOOST_SPIRIT_DEFINE

在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);
}

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:

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);
}

这篇关于Boost Spirit X3:“属性没有预期的大小" redux的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆