C++ 精神语法。属性与价值问题

C++ 精神语法。属性与价值问题,c++,parsing,boost-spirit,boost-spirit-qi,C++,Parsing,Boost Spirit,Boost Spirit Qi,我正在尝试创建一个Boost::Spirit语法类,它可以阅读相当简单的语法 start = roster; roster = *student; student = int >> string; 代码的目标是基于正在解析的输入文件创建命令对象树。创建此语法所使用的迭代器是给定的spirit文件迭代器 基本上,我遇到的问题是移动和使用每个规则的合成属性。我需要根据这些数据创建一个对象树,并且创建所述对象的唯一函数需要在那个时候知道父对象。我使用命令模式延迟创建,直到我解析了所

我正在尝试创建一个Boost::Spirit语法类,它可以阅读相当简单的语法

start   = roster;
roster  = *student;
student = int >> string;
代码的目标是基于正在解析的输入文件创建命令对象树。创建此语法所使用的迭代器是给定的spirit文件迭代器

基本上,我遇到的问题是移动和使用每个规则的合成属性。我需要根据这些数据创建一个对象树,并且创建所述对象的唯一函数需要在那个时候知道父对象。我使用命令模式延迟创建,直到我解析了所有数据并能够正确构建树为止。到目前为止,我实现这一点的方式是,我的命令都包含其他命令的向量。执行命令时,它只需要父对象,并将相应地创建和附加子对象。然后,对象将在自己的向量中执行每个命令,并将自身作为父对象传递。这将创建我需要的树结构,并将数据保存在tact中

问题是:

我面临的问题是如何在解析数据时构建命令,以及如何将它们加载到适当的向量中。到目前为止,我已经尝试了三种不同的方法

  • 我尝试将每个规则的属性更改为std::vector,并将属性作为命令逐个解析。问题是它将向量嵌套到std::vector>type数据中,我无法处理这些数据

  • 我尝试使用boost::phoenix placehold\u val作为所创建命令的代理。我为这个解决方案感到自豪,但也有点沮丧,因为它不起作用。我重载了所有命令的+=操作符,因此当A和B都是命令时,A+=B将B推到A的命令向量中_val不是命令,因此编译器不喜欢这样。我似乎无法将任何东西修补成更可行的状态。如果可能的话,这是最干净的解决方案,我希望它能起作用

  • 当前形式的代码让我试图将操作绑定在一起。如果我有一个指向_val的成员函数指针,并将创建的命令传递给它,它会将它推回。再说一次_val实际上并不是一个命令,所以它不起作用

  • 我将发布这堵代码墙,这是我编写的语法,以及调用它的地方

    template <typename Iterator>
    struct roster_grammar : qi::grammar<Iterator, qi::space_type, T3_Command()>
    {   
    //roster_grammar constructor
    roster_grammar() : 
        roster_grammar::base_type(start_)
    {
        using qi::lit;
        using qi::int_;
        using qi::char_;
        using qi::lexeme;
    
        start_ = student[boost::bind(&T3_Command::add_command, qi::_val, _1)];
    
        //I removed the roster for the time being to simplify the grammar
        //it still containes my second solution that I detailed above. This 
        //would be my ideal solution if it could work this way.
        //roster = *(student[qi::_val += _1]);
    
        student = 
            qi::eps       [ boost::bind(&T3_Command::set_identity, qi::_val, "Student") ]
            >>
            int_age       [ boost::bind(&T3_Command::add_command, qi::_val, _1) ]
            >>
            string_name   [ boost::bind(&T3_Command::add_command, qi::_val, _1) ];
    
        int_age     =
            int_          [ boost::bind(&Command_Factory::create_int_comm, &cmd_creator, "Age", _1) ];
        string_name =
            string_p      [ boost::bind(&Command_Factory::create_string_comm, &cmd_creator, "Name", _1) ];
    
        //The string parser. Returns type std::string
        string_p %= +qi::alnum;
    }
    
    qi::rule<Iterator,   qi::space_type, T3_Model_Command()>  roster;
    qi::rule<Iterator,   qi::space_type, T3_Atom_Command()>   student;
    qi::rule<Iterator,   qi::space_type, T3_Int_Command()>    int_age;
    qi::rule<Iterator,   qi::space_type, T3_String_Command()> string_name;
    qi::rule<Iterator,   qi::space_type, T3_Command()>        start_;
    qi::rule<Iterator,   std::string()>  string_p;
    Command_Factory cmd_creator;
    };
    
    我希望最终的树产生以下数据结构

    Command with type = "Roster" holding 3 "Student" type commands.
    Command with type = "Student" each holding an Int_Command and a String_Command
    Int_Command holds the stored integer and String_Command the stored string.
    
    E.g.
    
    r1 - Roster - [s1][s2][s3]
    s1 - Student - [int 23][string Bryan]
    s2 - Student - [int 45][string Tyler]
    s3 - Student - [int 4][string Stephen]
    
    这是我编写的命令的当前结构(实现非常简单)

    class T3\u命令
    {
    公众:
    T3_指挥部(无效);
    T3_命令(const std::string&type);
    ~T3_指挥部(无效);
    //执行此命令以及命令向量中的所有后续命令。
    void Execute(/*const Folder_在父级中,const Model_在父级中*/);
    //将传递的T3_命令推送到命令向量中
    //@param comm—要推送的命令。
    无效添加命令(常数T3命令和通信);
    //设置命令的标识。
    //@param ID-要设置的新标识。
    void set_标识(std::string&ID);
    私人:
    常量std::字符串标识;
    std::vector命令\u vec;
    T3_命令和操作员+=(常数T3_命令和rhs);
    };
    #布拉格语一次
    #包括“T3_command.h”
    类T3_Int_命令:
    公共交通指挥部
    {
    公众:
    T3_Int_命令();
    T3_Int_命令(const std::string&type,const Int val);
    ~T3_Int_命令(无效);
    void Execute();
    无效设置值(int-val);
    私人:
    int值;
    };
    

    因此,我遇到的问题是,我希望能够创建各种命令的数据结构,这些命令在spirit解析解析树时表示解析树。

    根据编辑的问题更新

    尽管仍有很多信息丢失(请参见我的[新评论]),但至少现在您显示了一些输入和输出:)

    因此,恕我直言,让我解释一下:

    • 您仍然希望只解析(int,string)对,但每行

      • 使用qi::blank_类型作为跳过程序
      • 执行
        花名册%eol
        以分析花名册行
      • 我的示例解析为名册向量(每行一个)
      • 每个名册包含不同数量的学生:

        start   = roster %  eol;
        roster  = +student;
        student = int_ >> string_p;
        
        bool operator<(Student const& other) const {
            return boost::tie(i,s) < boost::tie(other.i, other.s);
        }
        
        注意:规则#1不要使解析器复杂化,除非您真的必须这样做

    • 您想输出单个元素(“命令”?!?)-我假设这非常重要的部分是相同的
      学生
      可能出现在多个名册中的部分

      • 通过定义学生的总排序:

        start   = roster %  eol;
        roster  = +student;
        student = int_ >> string_p;
        
        bool operator<(Student const& other) const {
            return boost::tie(i,s) < boost::tie(other.i, other.s);
        }
        
      这就是我能想到的一切。我在这里大量使用了c++11和boost来节省代码行,但是如果没有c++11/boost,编写这篇文章也会相当简单

      #define BOOST_SPIRIT_USE_PHOENIX_V3
      #include <boost/fusion/adapted.hpp>
      #include <boost/spirit/include/qi.hpp>
      #include <boost/spirit/include/phoenix.hpp>
      #include <fstream>
      
      namespace qi    = boost::spirit::qi;
      namespace phx   = boost::phoenix;
      
      struct T3_Command
      {
          bool add_command(int i, std::string const& s) 
          {
              std::cout << "adding command [" << i << ", " << s << "]\n";
              return i != 42; // just to show how you can do input validation
          }
      };
      
      template <typename Iterator>
      struct roster_grammar : qi::grammar<Iterator, T3_Command(), qi::space_type>
      {   
          roster_grammar() : 
              roster_grammar::base_type(start_)
          {
              start_   = *(qi::int_ >> string_p) 
                  [qi::_pass = phx::bind(&T3_Command::add_command, qi::_val, qi::_1, qi::_2)];
      
              string_p = qi::lexeme[+(qi::graph)];
          }
      
          qi::rule <Iterator, T3_Command(), qi::space_type> start_;
          qi::rule <Iterator, std::string()> string_p;
      };
      
      int main()
      {
          typedef boost::spirit::istream_iterator iter_type;
          typedef roster_grammar<iter_type> student_p;
          student_p my_parser;
      
          //open the target file and wrap istream into the iterator
          std::ifstream in("input.txt");
          in.unsetf(std::ios::skipws);//Disable Whitespace Skipping
          iter_type begin(in);
          iter_type end;
      
          using boost::spirit::qi::space;
          using boost::spirit::qi::phrase_parse;
          bool r = phrase_parse(begin, end, my_parser, space);
      
          if (r)
              std::cout << "parse (partial) success\n";
          else      
              std::cerr << "parse failed: '" << std::string(begin,end) << "'\n";
          if (begin!=end) 
              std::cerr << "trailing unparsed: '" << std::string(begin,end) << "'\n";
      
          return r?0:255;
      }
      
      以下示例输入:

      ParsedT3Data const data = parseData(
              "23 Bryan 45 Tyler 4 Stephen\n"
              "7 Mary 45 Tyler 8 Stephane\n"
              "23 Bryan 8 Stephane");
      
      结果(请参见它):

      完整代码:

      #include <boost/fusion/adapted.hpp>
      #include <boost/spirit/include/qi.hpp>
      #include <boost/tuple/tuple_comparison.hpp>
      #include <boost/bimap.hpp>
      
      namespace qi    = boost::spirit::qi;
      
      struct Student
      {
          int i;
          std::string s;
      
          bool operator<(Student const& other) const {
              return boost::tie(i,s) < boost::tie(other.i, other.s);
          }
          friend std::ostream& operator<<(std::ostream& os, Student const& o) {
              return os << "Student - [int " << o.i << "][string " << o.s << "]";
          }
      };
      
      struct Roster
      {
          std::vector<Student> students;
      };
      
      BOOST_FUSION_ADAPT_STRUCT(Student, (int, i)(std::string, s))
      BOOST_FUSION_ADAPT_STRUCT(Roster, (std::vector<Student>, students))
      
      typedef std::vector<Roster> ParsedT3Data;
      
      template <typename Iterator>
      struct roster_grammar : qi::grammar<Iterator, ParsedT3Data(), qi::blank_type>
      {   
          roster_grammar() : 
              roster_grammar::base_type(start)
          {
              using namespace qi;
      
              start   = roster %  eol;
              roster  = eps    >> +student; // known workaround
              student = int_   >> string_p;
      
              string_p = lexeme[+(graph)];
      
              BOOST_SPIRIT_DEBUG_NODES((start)(roster)(student)(string_p))
          }
      
          qi::rule <Iterator, ParsedT3Data(), qi::blank_type> start;
          qi::rule <Iterator, Roster(),       qi::blank_type> roster;
          qi::rule <Iterator, Student(),      qi::blank_type> student;
          qi::rule <Iterator, std::string()>  string_p;
      };
      
      ParsedT3Data parseData(std::string const& demoData)
      {
          typedef boost::spirit::istream_iterator iter_type;
          typedef roster_grammar<iter_type> student_p;
          student_p my_parser;
      
          //open the target file and wrap istream into the iterator
          std::istringstream iss(demoData);
          iss.unsetf(std::ios::skipws);//Disable Whitespace Skipping
      
          iter_type begin(iss), end;
          ParsedT3Data result;
          bool r = phrase_parse(begin, end, my_parser, qi::blank, result);
      
          if (r)
              std::cout << "parse (partial) success\n";
          else      
              std::cerr << "parse failed: '" << std::string(begin,end) << "'\n";
          if (begin!=end) 
              std::cerr << "trailing unparsed: '" << std::string(begin,end) << "'\n";
      
          if (!r) 
              throw "TODO error handling";
      
          return result;
      }
      
      int main()
      {
          ParsedT3Data const data = parseData(
                  "23 Bryan 45 Tyler 4 Stephen\n"
                  "7 Mary 45 Tyler 8 Stephane\n"
                  "23 Bryan 8 Stephane");
      
          // now produce that list of stuff :)
          boost::bimap<std::string, Student> student_vars;
          auto generate_id = [&] () { return "s" + std::to_string(student_vars.size()+1); };
      
          for(Roster const& r: data)
              for(Student const& s: r.students)
                  student_vars.insert({generate_id(), s});
      
          for(auto const& s: student_vars.left)
              std::cout << s.first << " - " << s.second << "\n";
      
          int r_id = 1;
          for(Roster const& r: data)
          {
              std::cout << "r" << (r_id++) << " ";
              for(Student const& s: r.students)
                  std::cout << "[" << student_vars.right.at(s) << "]";
              std::cout << "\n";
          }
      }
      
      输入:

      1 klaas-jan
      2 doeke-jan
      3 jan-herbert
      4 taeke-jan
      42 oops-invalid-number
      5 not-parsed
      
      输出:

      adding command [1, klaas-jan]
      adding command [2, doeke-jan]
      adding command [3, jan-herbert]
      adding command [4, taeke-jan]
      adding command [42, oops-invalid-number]
      parse success
      trailing unparsed: '42 oops-invalid-number
      5 not-parsed
      '
      

      您没有提到如何将输入转换为(命令)树。事实上,您甚至没有提供输入的示例。你能不能把注意力集中在你想要实现的目标上,而不是模糊地描述你已经尝试过的事情——同时跳过关键部分:目标:)即使现在,我也不知道你想要实现什么。为什么T3/Student甚至是“Int”命令?为什么他们有一个“执行”?“执行”是做什么的?为什么数据结构中的所有内容都在getter/setter后面?当解析为PoCo@Rabidhseep好的,我已经用C++11和C++03更新了答案,向您展示了一些方法。我希望这能给你一些启发。你应该注意规则1:)@sehe我真的需要提高我的沟通技巧才是问题所在。这是一个更大的项目,我正试图wo的组成部分
      #define BOOST_SPIRIT_USE_PHOENIX_V3
      #include <boost/fusion/adapted.hpp>
      #include <boost/spirit/include/qi.hpp>
      #include <boost/spirit/include/phoenix.hpp>
      #include <fstream>
      
      namespace qi    = boost::spirit::qi;
      namespace phx   = boost::phoenix;
      
      struct T3_Command
      {
          bool add_command(int i, std::string const& s) 
          {
              std::cout << "adding command [" << i << ", " << s << "]\n";
              return i != 42; // just to show how you can do input validation
          }
      };
      
      template <typename Iterator>
      struct roster_grammar : qi::grammar<Iterator, T3_Command(), qi::space_type>
      {   
          roster_grammar() : 
              roster_grammar::base_type(start_)
          {
              start_   = *(qi::int_ >> string_p) 
                  [qi::_pass = phx::bind(&T3_Command::add_command, qi::_val, qi::_1, qi::_2)];
      
              string_p = qi::lexeme[+(qi::graph)];
          }
      
          qi::rule <Iterator, T3_Command(), qi::space_type> start_;
          qi::rule <Iterator, std::string()> string_p;
      };
      
      int main()
      {
          typedef boost::spirit::istream_iterator iter_type;
          typedef roster_grammar<iter_type> student_p;
          student_p my_parser;
      
          //open the target file and wrap istream into the iterator
          std::ifstream in("input.txt");
          in.unsetf(std::ios::skipws);//Disable Whitespace Skipping
          iter_type begin(in);
          iter_type end;
      
          using boost::spirit::qi::space;
          using boost::spirit::qi::phrase_parse;
          bool r = phrase_parse(begin, end, my_parser, space);
      
          if (r)
              std::cout << "parse (partial) success\n";
          else      
              std::cerr << "parse failed: '" << std::string(begin,end) << "'\n";
          if (begin!=end) 
              std::cerr << "trailing unparsed: '" << std::string(begin,end) << "'\n";
      
          return r?0:255;
      }
      
      1 klaas-jan
      2 doeke-jan
      3 jan-herbert
      4 taeke-jan
      42 oops-invalid-number
      5 not-parsed
      
      adding command [1, klaas-jan]
      adding command [2, doeke-jan]
      adding command [3, jan-herbert]
      adding command [4, taeke-jan]
      adding command [42, oops-invalid-number]
      parse success
      trailing unparsed: '42 oops-invalid-number
      5 not-parsed
      '