【问题标题】:pass attribute to child rule in boost spirit将属性传递给子规则以增强精神
【发布时间】:2012-09-20 20:44:21
【问题描述】:

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

是否可以将matrix_规则的属性传递给matrixBlock_子规则? 我想阻止重复指令创建表单向量的属性。相反,它应该继续写入 matrix_ 的属性(numBlocks 的时间)。 我试图将属性作为继承属性传递给子规则并编译(见下文)。但是我的向量中有几个“幽灵”条目不是来自 phoenix::push_back。 此外,这似乎不是我的最佳方式。我们是否可以在 matrixBlock_ 中自动传播属性而不是语义动作?

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_;

更新

澄清问题:

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

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_;

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


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

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_;

更新

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

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

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

我的错误是使用旧的 Boost 版本。 1.5 中没有 phontoms。

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

【问题讨论】:

  • 您能查看/编辑问题标题吗?我觉得它不能准确反映实际问题
  • 我看不到你在这里问什么。你能举一个工作的例子吗?请参阅sscce.org(例如,可能以我现有示例中的 两个 SSCCE 为例)
  • 感谢您的样品。现在答案出奇地简单
  • 或多或少令人惊讶的是,您的答案不适用于 boost 1_46_1...
  • 是的,这在 V2.5 中得到了改进 - boost 1_47 Change note: "The attribute handling for container attributes of sequences and container components (list, Kleene, Plus, and repeat) has been completely rewritten. It now supports many more use cases and behaves much more predictable than the older version. Thanks to Thomas Taylor, Richard Crossley, Semen, Adalberto Castelo, and many others for reporting bugs and helping in making the new code behave as expected."

标签: c++ boost boost-spirit


【解决方案1】:

更新

回答您编辑的问题:是的,您可以在没有语义操作的情况下做到这一点,只需简单地:

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

请注意,您可能想要验证行数/列数。 See my extended sample,它使用额外的语义操作来检查(注意从 *int_ 到 +int_ 的细微变化以避免空行):

#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\n";
    else
        std::cerr << "Uhoh\n";

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

旧答案:

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

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

旁注关于“虚拟条目”,一般来说:

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

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

完整代码示例:

#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\n";
   else
      std::cerr << "Uhoh\n";

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

输出:

Wokay
42, 45, -9

更新

更多背景知识:

当然,对于这样一个简单的例子,它只是包装了一个标准支持的容器类型,使用融合 adaptation 会相当容易:(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) << "\n";
}

请注意,qi::eps 是必要的,因为存在仅包含一个数据元素的结构的错误 (AFAICT)。参见例如discussion here(以及其他一些提及)

【讨论】:

  • 在链接中编辑,因为LWS 再次上线。还添加了背景,显示了完整性的常用替代方法
  • 感谢您的精彩解释。我不明白这如何解决我的问题。我稍微澄清了这个问题。
猜你喜欢
  • 1970-01-01
  • 2013-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多