首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Boost.Qi实现递归语法

用Boost.Qi实现递归语法
EN

Stack Overflow用户
提问于 2019-06-14 21:38:51
回答 1查看 296关注 0票数 4

我正在使用Boost.Spirit齐从一些文本数据构建相当复杂的结构。数据结构可能是递归定义的,所以我需要两个语法来相互引用,这就是问题出现的地方。

例如,我有一个语法:

代码语言:javascript
复制
element = line | text | circle | box | composite_element
composite_element = 'C', int, int, '[', +element, ']'

显然,我需要这样的东西:

代码语言:javascript
复制
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <tuple>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/include/qi_eol.hpp>
#include <boost/phoenix.hpp>
#include <vector>
#include <string>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

struct line {
    int x1;
    int y1;
    int x2;
    int y2;
    int color;
    int width;
    int capstyle;
    int dashstyle;
    int dashlength;
    int dashspace;
};

struct box {
    int x;
    int y;
    int width;
    int height;
    int color;
    int line_width;
    int capstyle;
    int dashstyle;
    int dashlength;
    int dashspace;
    int filltype;
    int fillwidth;
    int angle1;
    int pitch1;
    int angle2;
    int pitch2;
};

struct circle {
    int x;
    int y;
    int radius;
    int color;
    int line_width;
    int capstyle;
    int dashstyle;
    int dashlength;
};

struct text {
    int x;
    int y;
    int color;
    int size;
    int visibility;
    int show_name_value;
    int angle;
    int alignment;
    int num_lines;
    std::vector<std::string> lines;
};

struct composite_component;
using element_t = boost::variant<line, box, circle, text, boost::recursive_wrapper<composite_component>>;

struct composite_component {
    int x;
    int y;
    std::string basename;
    // only used if component is embedded
    // i. e. stores its definition within the schematic file
    std::vector<element_t> elements;
};

struct element {
    // some other fields
    // ...
    element_t element;
};

struct document {
    std::vector<element> elements;
};

BOOST_FUSION_ADAPT_STRUCT(line, x1, y1, x2, y2, color, width, capstyle, dashstyle, dashlength, dashspace)
BOOST_FUSION_ADAPT_STRUCT(box, x, y, width, height, color, line_width, capstyle, dashstyle, dashlength, dashspace, filltype, fillwidth, angle1, pitch1, angle2, pitch2)
BOOST_FUSION_ADAPT_STRUCT(circle, x, y, radius, color, line_width, capstyle, dashstyle, dashlength)
BOOST_FUSION_ADAPT_STRUCT(text, x, y, color, size, visibility, show_name_value, angle, alignment, num_lines, lines)
BOOST_FUSION_ADAPT_STRUCT(composite_component, x, y, basename, elements)
BOOST_FUSION_ADAPT_STRUCT(element, element)
BOOST_FUSION_ADAPT_STRUCT(document, elements)

template <typename Iterator, typename Attribute>
using rule = qi::rule<Iterator, Attribute, qi::blank_type>;

template <typename Iterator>
class composite_element_parser;

template <typename Iterator>
class element_parser : public qi::grammar<Iterator, element(), qi::blank_type> {
public:
    element_parser(): element_parser::base_type{start_rule_}
    {
        using qi::int_;
        using qi::repeat;
        using phoenix::val;
        using phoenix::construct;

        /* other definitions except of the 'line' is omitted in sake of simplicity */
        line_ = 'L' >> int_ >> int_ >> int_ >> int_ >> int_ >>
                    int_ >> int_ >> int_ >> int_ >> int_ >> qi::eol;
        // box = ...
        // circle = ...
        // text = ...
        start_rule_ = (line_ /* || embedded_component_ */) >> qi::eoi;
    }
private:
    rule<Iterator, element()> start_rule_;
    rule<Iterator, line()> line_;
    // here comes the problem - CIRCULAR REFERENCE to incompletely defined template
    // composite_element_parser<Iterator> embedded_component_;
};

template <typename Iterator>
class composite_element_parser : public qi::grammar<Iterator, composite_component(), qi::blank_type> {
    public:
    composite_element_parser() : composite_element_parser::base_type{start_rule_}
    {
        using phoenix::at_c;
        using qi::int_;
        using phoenix::push_back;

        start_rule_ = "C" >> int_ >> int_ >>  qi::lexeme[(qi::char_)[at_c<2>(qi::_val) += qi::_1]]
                                                        >> -(
                                                           "[" >>
                                                           *(element_) [push_back(at_c<3>(qi::_val), qi::_1)] >>
                                                           "]"
                                                           );
    }
    private:
    rule<Iterator, composite_component()> start_rule_;
    element_parser<Iterator> element_;
};

template <typename Iterator>
class document_parser : public qi::grammar<Iterator, document(), qi::blank_type> {
public:
    document_parser() : document_parser::base_type{start_rule_}
    {

        using phoenix::at_c;
        using phoenix::push_back;
        using qi::_val;
        using qi::_0;
        using qi::_1;

        start_rule_ = +(element_)[push_back(at_c<0>(_val), _1)] >> qi::eoi;
    }
    private:
    rule<Iterator, document()> start_rule_;
    element_parser<Iterator> element_;
};

int main(int , char **) {
    document_parser<std::string::const_iterator> parser;
    document doc;
    const std::string text = "v 20180904 2\n"
                             "L 1 2 3 4 5 6 7 8 9 10\n"
                             "C 10 10 FOO\n"
                             "[ "
                             "L 1 2 3 4 5 6 7 8 9 10\n"
                             "]\n";
    bool r = qi::phrase_parse(text.cbegin(), text.cend(), parser, qi::blank, doc);
    std::cout << (r ? "OK" : "FAIL") << std::endl;

     return 0;
}

但是,“文本”、“圆圈”和“框”的规则的定义被省略了。注意,element_parser定义的私有部分中的注释-编译器将无法实例化不完整的类模板composite_element_parser<Iterator>。我该拿它怎么办?显然,我不能让element_parsercomposite_element_parser作为顶级语法的成员(在我的例子中是document_parser),并在构造函数初始化程序列表中相互传递引用/指针,因为它们目前还没有初始化。

更新:这个线程很可能被认为是具有合成和继承属性的深度递归的qi语法(解析器)的复制,但我真的不能理解被批准的答案。

EN

回答 1

Stack Overflow用户

发布于 2019-06-15 13:12:01

通常情况下,你只是不分裂语法的方式。但是如果你真的想要的话,有多种方法:

  1. 单独创建语法并将语法分配给外部的rule占位符: #include 命名空间qi =boost::致气;模板结构grammar1 : qi::grammar { grammar1():grammar1::base_type{ start_ } {start_=‘>> int >>’;} qi::rule外层;模板结构grammar2 : qi::grammar { grammar2():grammar2::base_type{ start_ } { qi::int_;} qi::rule外层;私有: qi::rule start_;};int main() { char const* s= "[123]",*e=s+ std::strlen(s);grammar2 g2;grammar1 g1;g2.out= g1;g1.out= g2;int值= 0;if (qi::parse(s,e,g1,value)) std:cout <<值<< '\n';否则std::cout << "failed\n"; https://wandbox.org/permlink/QhA18pIZwVlQ2osi
  2. 在另一个语法中动态创建一个语法,并将前者的引用传递给它: #包含命名空间qi = boost::spirit::qi;template struct grammar2;template struct grammar1 : qi::grammar { grammar1():grammar1::base_type{ start_ } { outer_ =std::make_unique;start_=‘>> *outer_ >>’;//注意:它不是kleen!} private: std::unique_ptr outer_;qi::rule start_;};模板结构grammar2 : qi::grammar {显式grammar2(qi::rule const& outer):grammar2::base_type{ start_ } {start_=外层qi::int_;int main() { char const* s= "[123]",*e=s+ std::strlen(s);grammar1 const g1;int值= 0;if (qi:解析(s,e,g1,value )std:cout <<值<< '\n'; https://wandbox.org/permlink/hJz3v1ApK8GCkquS
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56605435

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档