【问题标题】:Parsing recursive structure on boost::spirit在 boost::spirit 上解析递归结构
【发布时间】:2013-12-12 16:03:47
【问题描述】:

我赢了解析像“text { }”这样的结构。 Spirit 文档内容类似AST 示例。 像这样解析字符串

<tag1>text1<tag2>text2</tag1></tag2>

此代码有效:

    templ     = (tree | text)       [_val = _1];

    start_tag = '<' 
            >> !lit('/') 
            >> lexeme[+(char_- '>') [_val += _1]] 
            >>'>'; 

    end_tag   =  "</" 
            >> string(_r1) 
            >> '>'; 

    tree =  start_tag          [at_c<1>(_val) = _1]
            >> *templ          [push_back(at_c<0>(_val), _1) ]
            >> end_tag(at_c<1>(_val) )
            ;

像这样解析字符串

<tag<tag>some_text>

此代码无效:

    templ     = (tree | text)       [_val = _1];


    tree =  '<'
            >> *templ          [push_back(at_c<0>(_val), _1) ]
            >> '>'
            ;

templ 正在解析带有 recursive_wrapper 的结构:

namespace client {

   struct tmp;

   typedef boost::variant <
        boost::recursive_wrapper<tmp>,
        std::string
   > tmp_node;

   struct tmp {
     std::vector<tmp_node> content;
     std::string text;
   };
}

BOOST_FUSION_ADAPT_STRUCT(
     tmp_view::tmp,
     (std::vector<tmp_view::tmp_node>, content)
     (std::string,text)
)

谁能解释为什么会这样?也许谁知道类似的解析器在 boost::spirit 上写过?

【问题讨论】:

  • 问题是什么?我看到两个无效的 XML sn-ps,以及两个相对不相关的语法,它们显然都不应该解析无效的 XML。我在 Spirit 中编写了很多解析器。也许他们很相似。但是你忘了提到“发生了什么”(所以我们不能说“为什么会发生”),你也忘了说你想要实现的目标。所以谁知道有类似的解析器...
  • 我会忘记文本规则。在第二种情况下(工作)它必须是 "text = lexeme[+(char_ - '') [_val += _1]];"

标签: c++ parsing boost-spirit boost-spirit-qi


【解决方案1】:

只是猜测您实际上根本不想解析 XML,而是某种用于分层文本的混合内容标记语言,我会这样做

        simple = +~qi::char_("><");
        nested = '<' >> *soup >> '>';
        soup   = nested|simple;

将 AST/规则定义为

typedef boost::make_recursive_variant<
        boost::variant<std::string, std::vector<boost::recursive_variant_> > 
    >::type tag_soup;

qi::rule<It, std::string()>           simple;
qi::rule<It, std::vector<tag_soup>()> nested;
qi::rule<It, tag_soup()>              soup;

看到它Live On Coliru

////  #define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>

#include <iostream>
#include <fstream>

namespace client
{
    typedef boost::make_recursive_variant<
            boost::variant<std::string, std::vector<boost::recursive_variant_> > 
        >::type tag_soup;

    namespace qi = boost::spirit::qi;

    template <typename It>
    struct parser : qi::grammar<It, tag_soup()>
    {
        parser() : parser::base_type(soup)
        {
            simple = +~qi::char_("><");
            nested = '<' >> *soup >> '>';
            soup   = nested|simple;

            BOOST_SPIRIT_DEBUG_NODES((simple)(nested)(soup))
        }
      private:
        qi::rule<It, std::string()>           simple;
        qi::rule<It, std::vector<tag_soup>()> nested;
        qi::rule<It, tag_soup()>              soup;
    };
}

namespace boost { // leverage ADL on variant<>
    static std::ostream& operator<<(std::ostream& os, std::vector<client::tag_soup> const& soup)
    {
        os << "<";
        std::copy(soup.begin(), soup.end(), std::ostream_iterator<client::tag_soup>(os));
        return os << ">";
    }
}

int main(int argc, char **argv)
{
    if (argc < 2) {
        std::cerr << "Error: No input file provided.\n";
        return 1;
    }

    std::ifstream in(argv[1]);
    std::string const storage(std::istreambuf_iterator<char>(in), {}); // We will read the contents here.

    if (!(in || in.eof())) {
        std::cerr << "Error: Could not read from input file\n";
        return 1;
    }

    static const client::parser<std::string::const_iterator> p;

    client::tag_soup ast; // Our tree
    bool ok = parse(storage.begin(), storage.end(), p, ast);

    if (ok) std::cout << "Parsing succeeded\nData: " << ast << "\n";
    else    std::cout << "Parsing failed\n";

    return ok? 0 : 1;
}

如果您定义 BOOST_SPIRIT_DEBUG,您将获得解析过程的详细输出。

对于输入

<some text with nested <tags <etc...> >more text>

打印

Parsing succeeded
Data: <some text with nested <tags <etc...> >more text>

请注意,输出是从变体打印的,而不是原始文本。

【讨论】:

  • 我调试了解析器。如何使用多个标签类型('')。例如“text2}>”。使用关闭标记 ('>','}') 解析器如何知道打开表达式的标记是什么(在 mini_xml 示例中使用参考)。
  • @crastinus 您仍然需要告诉我们您要达到的目标。即使不使用 Spirit,我也可以很好地“解析”这些标签。 (cat input.txt &gt; /dev/null 即,如果没有所需/必需的行为)。
  • 感谢您的帮助。不管我想要什么。我只是明白打开或关闭什么标签并不重要。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多