使用 boostspirit 将结构体解析为结构体

问题描述 投票:0回答:1

我正在尝试从 .net 文件解析此网表:

V1 N001 N002 10
R1 N001 N002 24.9

我有以下结构:

struct ElementStatement {
  boost::optional<std::string> element_label;
  boost::optional<std::string> element_node1;
  boost::optional<std::string> element_node2;
  boost::optional<double> element_value;
};

struct SpiceNetlist {

  std::vector<ElementStatement> element_statements;
//to be expanded
};

这是我在.cpp文件中的代码,用于解析并打印出解析后的变量:

BOOST_FUSION_ADAPT_STRUCT(ElementStatement, element_label, element_node1,
                      element_node2, element_value)

BOOST_FUSION_ADAPT_STRUCT(SpiceNetlist, element_statements)

namespace qi = boost::spirit::qi;

class SpiceGrammar
    : public boost::spirit::qi::grammar<std::string::const_iterator,
                                    SpiceNetlist()> {

public:
  using Iterator = std::string::const_iterator;
  SpiceGrammar() : SpiceGrammar::base_type(spice_netlist) {

    spice_netlist %=
        element_statements >> *(element_statements);

    element_statements %=
        element_label >> element_node1 >> element_node2 >> element_value >>
        qi::eol; 

    element_label %= (qi::char_ >> qi::char_) >> qi::space;
    element_node1 %=
        (qi::char_ >> qi::char_ >> qi::char_ >> qi::char_) >> qi::space;

    element_node2 %=
        qi::char_ >> qi::char_ >> qi::char_ >> qi::char_ >> qi::space;

    element_value %= qi::double_;

    BOOST_SPIRIT_DEBUG_NODE(spice_netlist);
    BOOST_SPIRIT_DEBUG_NODE(element_statements);
    BOOST_SPIRIT_DEBUG_NODE(element_label);
    BOOST_SPIRIT_DEBUG_NODE(element_node1);
    BOOST_SPIRIT_DEBUG_NODE(element_node2);
    BOOST_SPIRIT_DEBUG_NODE(element_value);
  }
  qi::rule<Iterator, SpiceNetlist()> spice_netlist;
  qi::rule<Iterator, ElementStatement> element_statements;
  qi::rule<Iterator, boost::optional<std::string>()> element_label;
  qi::rule<Iterator, boost::optional<std::string>()> element_node1;
   qi::rule<Iterator, boost::optional<std::string>()> element_node2;
   qi::rule<Iterator, boost::optional<double>()> element_value;
 };

 NetlistLoader::NetlistLoader() = default;

 SpiceNetlist NetlistLoader::parse_netlist_from_string(
     const std::string &netlist_string) const {

   SpiceNetlist netlist;
   std::cout << "Trying to parse:\n" << netlist_string << "\n";

   std::cout << "Size of netlist_string: " << netlist_string.size() << std::endl;
   auto iter = netlist_string.begin();
   auto last = netlist_string.end();

   std::cout << "Characters to go " << std::distance(iter, last) << std::endl;

   bool success = qi::parse(iter, last, SpiceGrammar(), netlist);

   std::cout << "Parsed: " << success << "\n";
   std::cout << "Characters to go " << std::distance(iter, last) << std::endl
             << std::endl;

   if (success) {
     std::cout << "Parsed netlist content:" << std::endl;

     for (std::size_t i = 0; i < netlist.element_statements.size(); ++i) {
       const auto &statement = netlist.element_statements[i];

       std::cout << "Element Label: ";
       if (statement.element_label) {
         std::cout << *statement.element_label;
       } else {
         std::cout << "Not specified";
       }
       std::cout << "\n ";

       std::cout << "Element Node1: ";
       if (statement.element_node1) {
         std::cout << *statement.element_node1;
       } else {
         std::cout << "Not specified";
       }
       std::cout << "\n";

       std::cout << "Element Node2: ";
       if (statement.element_node2) {
         std::cout << *statement.element_node2;
       } else {
         std::cout << "Not specified";
       }
       std::cout << "\n";

       std::cout << "Element Value: ";
       if (statement.element_value) {
         std::cout << *statement.element_value;
       } else {
         std::cout << "Not specified";
       }
       std::cout << std::endl;
       std::cout << std::endl;
     }
   } else {
     std::cout << "Failed to parse netlist.\n";
   }
   return netlist;
 }

我能够成功解析所有字符,但我只得到第一行,即 netlist.element_statement[0] 作为输出。

Parsed netlist content
Element Label: V1 
Element Node1: N001 
Element Node2: N002 
Element Value: 10

我尝试将 element_statements 的规则修改为 qi::rule element_statements;但它会产生构建错误。

我哪里出错了?

编辑:太棒了,非常感谢。我现在必须扩展我的代码来解析如下所示的网表:

*comment line 1
V1 N001 N002 10
R1 N001 N002 24.9

*comment line 2
R2 N002 N003 20
V2 N002 N003 5

这就是我的新结构的样子: (注释行需要处于单独的结构中,以防以后需要扩展这部分)

  struct CommentLine {
  boost::optional<std::string> comment_line;
};

struct ElementStatement {
  boost::optional<std::string> label;
  boost::optional<std::string> node1;
  boost::optional<std::string> node2;
  boost::optional<double> value;
};

struct SpiceNetlist {
  std::vector<CommentLine> comment_lines;
  std::vector<ElementStatement> elements;
};

这是我添加到@sehe 的以下建议代码中的部分

BOOST_FUSION_ADAPT_STRUCT(CommentLine, comment_line)
BOOST_FUSION_ADAPT_STRUCT(SpiceNetlist, comment_lines, elements)

spice_netlist = qi::skip(qi::blank)[comment_lines >> -statements];

comment_lines = comment_line % qi::eol;
comment_line = qi::lit('*') >> *(qi::graph);

BOOST_SPIRIT_DEBUG_NODES((
        spice_netlist)(comment_lines)(comment_line)(statements)(statement)(label)(node1)(node2)(value))

qi::rule<Iterator, std::vector<CommentLine>, Skipper> comment_lines;
//lexemes
qi::rule<Iterator, std::string()> comment_line;



   int main(){
    for (const auto &[comment] : netlist.comment_lines) {
          std::cout << "Comment: " << comment << std::endl;
        }
    for (auto const& [label, node1, node2, value] : netlist.elements) {
        std::cout                                                                              //
            << "Element Label: " << label.value_or("Not specified") << "\n"                    //
            << "        Node1: " << node1.value_or("Not specified") << "\n"                    //
            << "        Node2: " << node2.value_or("Not specified") << "\n"                    //
            << "        Value: " << (value ? std::to_string(*value) : "Not specified") << "\n" //
            << std::endl;
    }
    }

解析成功,直到“comment li”,属性也填错了。 是我的spice_netlist或comment_line语法有问题还是我用Skipper定义的规则错误?

c++ data-structures boost-spirit-qi ninja netlist
1个回答
0
投票

您正在手动处理空格,但从不处理换行符。

我建议将空白留给船长忽略,并指定换行符(假设需要):

spice_netlist = statement_ % qi::eol;

请注意,

p >> *p
相当于
+p
。请参阅文档了解
operator%

使用

qi::blank
船长您可以简化所有规则。我建议将毯子
qi::char_
替换为
qi::graph
,这可能就是您想要的

另外,不要理会可选属性,只需 使表达式可选

statement_ = -label_ >> -node1_ >> -node2_ >> -value_;

演示

住在科里鲁

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
struct ElementStatement {
    boost::optional<std::string> label, node1, node2;
    boost::optional<double>      value;
};
using ElementStatements = std::vector<ElementStatement>;
struct SpiceNetlist { ElementStatements elements; }; // to be expanded

BOOST_FUSION_ADAPT_STRUCT(ElementStatement, label, node1, node2, value)
BOOST_FUSION_ADAPT_STRUCT(SpiceNetlist, elements)

namespace qi = boost::spirit::qi;

template <typename It>
class SpiceGrammar : public qi::grammar<It, SpiceNetlist()> {
  public:
    SpiceGrammar() : SpiceGrammar::base_type(spice_netlist_) {
        spice_netlist_ = qi::skip(qi::blank)[statements_];
        statements_    = statement_ % qi::eol;
        statement_     = -label_ >> -node1_ >> -node2_ >> -value_;
        label_         = qi::graph >> qi::graph;
        node1_         = qi::graph >> qi::graph >> qi::graph >> qi::graph;
        node2_         = qi::graph >> qi::graph >> qi::graph >> qi::graph;
        value_         = qi::double_;

        BOOST_SPIRIT_DEBUG_NODES((spice_netlist_)(statements_)(statement_)(label_)(node1_)(node2_)(value_))
    }

  private:
    qi::rule<It, SpiceNetlist()> spice_netlist_;

    using Skipper = qi::blank_type;
    Skipper                                    skipper_;
    qi::rule<It, ElementStatements(), Skipper> statements_;
    qi::rule<It, ElementStatement(), Skipper>  statement_;
    // lexemes
    qi::rule<It, std::string()> label_, node1_, node2_;
    qi::rule<It, double()>      value_;
};

SpiceNetlist parse_netlist_from_string(std::string_view input) {
    using It = std::string_view::const_iterator;
    static SpiceGrammar<It> const g;

    SpiceNetlist netlist;

    It f = input.begin(), l = input.end();
    bool success = qi::parse(f, l, g, netlist);

    std::cout << "Parsing: " << quoted(input) << " -> "
              << "Parse " << (success ? "SUCCESS" : "FAILED") << "\n"
              << "Remaining: " << quoted(std::string_view(f, l)) << std::endl;

    return netlist;
}

int main() {
    auto netlist = parse_netlist_from_string(R"(V1 N001 N002 10
            R1 N001 N002 24.9)");

    for (auto const& [label, node1, node2, value] : netlist.elements) {
        std::cout                                                                              //
            << "Element Label: " << label.value_or("Not specified") << "\n"                    //
            << "        Node1: " << node1.value_or("Not specified") << "\n"                    //
            << "        Node2: " << node2.value_or("Not specified") << "\n"                    //
            << "        Value: " << (value ? std::to_string(*value) : "Not specified") << "\n" //
            << std::endl;
    }
}

印刷

Parsing: "V1 N001 N002 10
            R1 N001 N002 24.9" -> Parse SUCCESS
Remaining: ""
Element Label: V1
        Node1: N001
        Node2: N002
        Value: 10.000000

Element Label: R1
        Node1: N001
        Node2: N002
        Value: 24.900000

总结/注释

我的列表中有许多的简化。

我想指出一个更微妙的点:标记为“lexemes”的规则使用声明的船长。这对于避免跨空白匹配内容非常重要:Boost Spirit Captain issues

© www.soinside.com 2019 - 2024. All rights reserved.