将属性传递给子规则以提升精神

2021-12-24 00:00:00 c++ boost boost-spirit

我有两个具有相同属性的规则.

是否可以将matrix_规则的属性传递给matrixBlock_子规则?我想防止重复指令创建形式 vector< 的属性.>.相反,它应该继续写入 matrix_ 的属性(numBlocks 的次数).我试图将属性作为继承属性传递给子规则并编译(见下文).但是我在我的向量中得到了几个幽灵"条目,它们不是来自 phoenix::push_back.此外,这对我来说似乎不是最佳方式.是否可以在 matrixBlock_ 中使用自动属性传播而不是语义动作?

typedef vector矩阵;matrix_ = repeat(numBlocks)[ matrixBlock_(_val) ];matrixBlock_ = *column[phoenix::push_back(_r1, _1)];qi::rule<Iterator, Matrix(), ascii::space_type>矩阵_;qi::rule<Iterator, void(Matrix&), ascii::space_type>矩阵块_;

<小时>

更新

澄清问题:

如果我编写没有语义动作的规则,matrix_ 的合成属性将是

vector<向量<列T >>

-

typedef vector矩阵;matrix_ = repeat(numBlocks)[ matrixBlock_ ];矩阵块_ = *列;qi::rule<Iterator, Matrix(), ascii::space_type>矩阵_;qi::rule<Iterator, Matrix(), ascii::space_type>矩阵块_;

我希望它具有与 matrixBlock_ 相同的属性类型,一个一维数组.

<小时>

我的实际解决方案是只使用一个规则.(看起来很简单:-))

typedef vector矩阵;matrix_ = repeat(numBlocks)[ *column_[ phoenix::push_back(_val, _1) ] ];//矩阵块_ = *列;qi::rule<Iterator, Matrix(), ascii::space_type>矩阵_;//qi::rule矩阵块_;

<小时>

更新

我能够在 vs2010 和 boost 1.46.1 中使用此代码重现幻像条目

http://liveworkspace.org/code/505091dc4631a379763567168a728e><块引用>

输出为:42, 45, -9, 3, 2, 1, 12, 34, 56, 0, 0, 0

我的错误是使用了旧的 Boost 版本.1.5没有幻影.

现在我有两个可用的语法版本.是否可以在不使用 push_back 语义动作的情况下重新设计语法?

解决方案

更新

回答您编辑的问题:是的,您可以在没有语义操作的情况下完成此操作,只需:

template结构解析器:qi::grammar{Parser() : Parser::base_type(matrix_){matrixBlock_ = qi::lit(";") >>*qi::int_;矩阵_ = qi::repeat(3)[矩阵块_];}qi::rule矩阵块_,矩阵_;};

请注意,您可能想要验证行数/列数.查看我的扩展示例,它使用额外的语义操作来检查(注意从 *int_+int_ 的细微变化以避免空行):

#include #include #include 命名空间 qi = boost::spirit::qi;命名空间业力 = boost::spirit::karma;命名空间 phx = boost::phoenix;typedef std::vector矩阵;模板结构解析器:qi::grammar{Parser() : Parser::base_type(matrix_){使用命名空间qi;matrixBlock_ = lit(";") >>+int_>>eps( 0 == (phx::size(_val) % 3));矩阵_ =重复(3)[矩阵块_];}qi::rule矩阵块_,矩阵_;};int main(){std::string 测试 = ";42 45 -9; 3 2 1; 12 34 56";std::string::const_iterator f(test.begin()), l(test.end());解析器解析器;矩阵 m;如果 (qi::phrase_parse(f,l,parser,qi::space, m))std::cout <<"哇哦
";别的std::cerr <<"呃
";std::cout <<业力::格式(业力::自动_%,",米)<<"
";}

<小时>

旧答案:

是的,您可以使用 Spirit 的自定义点将您的用户定义类型视为容器.我为此建议的文档条目在这里:

  • http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html

这是一个简单的例子,展示了如何使用它,现场:

  • http://liveworkspace.org/code/2d708ecb64ad6fbf9708c203f26965ce莉>
<块引用>

旁注关于幻像条目"的一般情况:

请注意,有一些与回溯语法和容器属性相关的常见问题解答.问题是,出于性能原因,解析器不会在回溯时撤消(回滚")对其底层容器的更改.您可以使用 qi::hold 但将语法重新设计为

  • 避免回溯或
  • 在稍后阶段提交属性(使用语义操作)

完整代码示例:

#include #include 命名空间 qi = boost::spirit::qi;命名空间业力 = boost::spirit::karma;结构矩阵{std::vector数据;};命名空间提升 { 命名空间精神 { 命名空间特征 {模板 <>struct is_container<矩阵>{};模板 struct push_back_container{静态布尔调用(Matrix& c, Attrib const& val){c.data.push_back(val);返回真;}};模板 <>struct container_value<Matrix>{typedef int 类型;};} } }模板结构解析器:qi::grammar{Parser() : Parser::base_type(start){开始 = *qi::int_;}qi::rule开始;};int main(){std::string 测试 = "42 45 -9";std::string::const_iterator f(test.begin()),l(test.end());解析器解析器;矩阵 m;如果 (qi::phrase_parse(f,l,parser,qi::space, m))std::cout <<"哇哦
";别的std::cerr <<"呃
";std::cout <<业力::格式(业力::自动_%,",m.data)<<"
";}

输出:

哇哦42, 45, -9

<小时>

更新

多一点背景:

当然,对于这样一个简单的例子,它只是包装了一个标准支持的容器类型,使用fusion adaptation 代替:( http://liveworkspace.org/code/56aea8619867451a21cd49fddb1e93bd )

#include #include #include 命名空间 qi = boost::spirit::qi;命名空间业力 = boost::spirit::karma;struct Matrix { std::vector;数据;};BOOST_FUSION_ADAPT_STRUCT(矩阵,(std::vector,数据));int main(){std::string 测试 = "42 45 -9";std::string::const_iterator f(test.begin()), l(test.end());矩阵 m;如果 (qi::phrase_parse(f,l, qi::eps >> *qi::int_, qi::space, m))std::cout <<业力::格式(业力::自动_%,",m.data)<<"
";}

请注意,qi::eps 是必需的,因为结构中存在仅包含一个数据元素的错误 (AFAICT).见例如此处讨论(以及其他一些提及)

I have two rules with the same attribute.

Is it possible to pass the attribute of the matrix_ rule to the matrixBlock_ child rule? I want to keep the repeat directive from creating an attribute of the form vector< >. Instead it should just keep writing into the attribute of matrix_ (numBlocks's times). I tried to pass the attribute as inherited attribute to the child rule and it compiles(see below). But I get several "ghost" entries in my vector which come not from the phoenix::push_back. Also this seems not to be the optimal way for me. Is it possible to us automatic attribute propagation in matrixBlock_ instead of semantic actions?

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_(_val) ];
matrixBlock_ = *column[phoenix::push_back(_r1, _1)];

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, void(Matrix&), ascii::space_type> matrixBlock_;


Update

to clarify the question:

if I write the rule with no semantic actions the synthesized attribute of matrix_ would be

vector< vector< columnT > >

-

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_ ];
matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

I want it to have the same attribute type as matrixBlock_, a 1-dimansional array.


my actual solution is to use only one rule. (looks to easy :-) )

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ *column_[ phoenix::push_back(_val, _1) ] ];
//matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
//qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;


Update

I was able to reproduce the the phantom entries with this code in vs2010 and boost 1.46.1

http://liveworkspace.org/code/505091dc4631a379763567168a728e0c

output was: 42, 45, -9, 3, 2, 1, 12, 34, 56, 0, 0, 0

My mistake was using an old Boost version. There are no phontoms with 1.5.

Now I have two working versions of my grammar. Is it possible to redesign the grammar without the use of the push_back semantic action?

解决方案

Updated

Answering your edited question: Yes you can do this without semantic actions, doing simply:

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        matrixBlock_ = qi::lit(";") >> *qi::int_;
        matrix_      = qi::repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

Note that you may want to validate the number of rows/columns. See my extended sample, which uses an extra semantic action to check that (note the subtle change from *int_ to +int_ to avoid empty rows):

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi    = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx   = boost::phoenix;

typedef std::vector<int> Matrix;

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        using namespace qi;
        matrixBlock_ = lit(";") >> +int_ >> eps( 0 == (phx::size(_val) % 3));
        matrix_      = repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

int main()
{
    std::string test = ";42 45 -9; 3 2 1; 12 34 56";

    std::string::const_iterator f(test.begin()), l(test.end());

    Parser<std::string::const_iterator> parser;
    Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
        std::cout << "Wokay
";
    else
        std::cerr << "Uhoh
";

    std::cout << karma::format(karma::auto_ % ", ", m) << "
";
}


Old answer:

Yes, you can use Spirit's customization points to treat your user-defined type as a container. The documentation entry I'd suggest for this is here:

  • http://www.boost.org/doc/libs/1_51_0/libs/spirit/doc/html/spirit/advanced/customize/is_container.html

Here is a simple example showing how to use it, live:

  • http://liveworkspace.org/code/2d708ecb64ad6fbf9708c203f26965ce

Side note with regards to 'phantom entries', in general:

Note that there is a bit of a FAQ related to backtracking grammars and container attributes. The thing is, for performance reasons, parsers won't undo ('rollback') changes to their underlying containers on backtracking. You can force this behaviour using qi::hold but it may worth the effort to redesign the grammar to either

  • avoid backtracking or
  • commit to the attribute at a later stage (using semantic actions)

Full code sample:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix
{
   std::vector<int> data;
};

namespace boost { namespace spirit { namespace traits {
   template <>
      struct is_container<Matrix>
      {
      };

   template <typename Attrib>
      struct push_back_container<Matrix, Attrib>
      {
         static bool call(Matrix& c, Attrib const& val)
         {
            c.data.push_back(val);
            return true;
         }
      };

   template <>
      struct container_value<Matrix>
      {
         typedef int type;
      };
} } }

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
   Parser() : Parser::base_type(start)
   {
      start = *qi::int_;
   }
   qi::rule<It, Matrix(), qi::space_type> start;
};

int main()
{
   std::string test = "42 45 -9";

   std::string::const_iterator f(test.begin()),
      l(test.end());

   Parser<std::string::const_iterator> parser;
   Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
      std::cout << "Wokay
";
   else
      std::cerr << "Uhoh
";

   std::cout << karma::format(karma::auto_ % ", ", m.data) << "
";
}

Output:

Wokay
42, 45, -9


Update

A little more background:

Of course, for a trivial example like this, that just wraps a standard supported container type, it would be fairly easy to employ fusion adaptation instead: ( http://liveworkspace.org/code/56aea8619867451a21cd49fddb1e93bd )

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted/struct.hpp>

namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;

struct Matrix { std::vector<int> data; }; 
BOOST_FUSION_ADAPT_STRUCT(Matrix, (std::vector<int>, data));

int main()
{
    std::string test = "42 45 -9";
    std::string::const_iterator f(test.begin()), l(test.end());

    Matrix m;
    if (qi::phrase_parse(f,l, qi::eps >> *qi::int_, qi::space, m))
        std::cout << karma::format(karma::auto_ % ", ", m.data) << "
";
}

Note that the qi::eps is necessary due to a bug (AFAICT) with structs that contain only one data element. See e.g. discussion here (and some other mentions)

相关文章