gpt4 book ai didi

c++ - Boost.Spirit 语法。属性和 _val 问题

转载 作者:行者123 更新时间:2023-12-01 14:51:07 25 4
gpt4 key购买 nike

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

start   = roster;
roster = *student;
student = int >> string;

代码的目标是根据正在解析的输入文件创建命令对象树。创建此语法的迭代器是给定的精神文件迭代器。

基本上,我遇到的麻烦是移动和使用每个规则的综合属性。我需要根据这些数据创建对象树,而创建所述对象的唯一功能需要当时知道父对象。我正在使用命令模式来延迟创建,直到我解析了所有数据并可以正确构建树。到目前为止,我实现这一点的方式是我的命令都包含其他命令的 vector 。执行命令时,它只需要父对象,并相应地创建和附加子对象。然后对象将执行它自己的 vector 中的每个命令,并将自己作为父对象传递。这将创建我需要的树结构,其中包含完整的数据。

问题:

我遇到的问题是如何在解析数据时构建命令,以及如何将它们加载到适当的 vector 中。到目前为止,我已经尝试了 3 种不同的方法。
  • 我试图将每条规则的属性更改为 std::vector 并将属性解析为一次一个命令。问题在于它将 vector 嵌套到 std::vector> 类型数据中,这是我无法使用的。
  • 我尝试使用 boost::phoenix 占位符 _val 作为正在创建的命令的代理。我为这个解决方案感到自豪,但对它不起作用感到有点沮丧。我为所有命令重载了 += 运算符,这样当 A 和 B 都是命令时,A += B 将 B 插入 A 的命令 vector 中。 _val 不是命令,所以编译器不喜欢这样。我似乎无法将任何东西修补成更可行的状态。如果可能的话,这是最干净的解决方案,我希望它能够工作。
  • 当前形式的代码让我尝试将这些操作绑定(bind)在一起。如果我有一个指向 _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;
    };

    这就是语法被实例化和使用的方式。
    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 = std::ifstream(path);
    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);

    长话短说,我有一个语法,我想用它来构建命令(调用 T3_Command)。命令有一个 std:Vector 数据成员,它在树中保存其他命令。

    我需要一种将命令创建为语义 Action 的干净方法,我需要能够将其加载到其他命令的 vector 中(通过属性或直接函数调用)。命令具有应该在创建时指定的类型(将定义它创建的树节点的类型),并且一些命令具有数据值(一个 int、string 或 float,所有在它们各自的命令中命名的值)。

    或者,如果可能有更好的方法来 build 一棵树,我愿意接受建议。
    非常感谢您提供的任何帮助!

    编辑:
    我将尝试更清楚地了解我要解决的原始问题。谢谢你的耐心。

    鉴于该语法(或实际上的任何语法),我希望能够解析它并根据解析器中采取的语义操作创建一个命令树。
    所以使用我的示例语法和输入
    "23 Bryan 45 Tyler 4 Stephen"

    我希望最终的树产生以下数据结构。
    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_Command
    {
    public:
    T3_Command(void);
    T3_Command(const std::string &type);
    ~T3_Command(void);

    //Executes this command and all subsequent commands in the command vector.
    void Execute(/*const Folder_in parent AND const Model_in parent*/);

    //Pushes the passed T3_Command into the Command Vector
    //@param comm - The command to be pushed.
    void add_command(const T3_Command &comm);

    //Sets the Identity of the command.
    //@param ID - the new identity to be set.
    void set_identity(std::string &ID);

    private:

    const std::string ident;
    std::vector <T3_Command> command_vec;
    T3_Command& operator+=(const T3_Command& rhs);
    };

    #pragma once
    #include "T3_command.h"
    class T3_Int_Command :
    public T3_Command
    {
    public:
    T3_Int_Command();
    T3_Int_Command(const std::string &type, const int val);
    ~T3_Int_Command(void);
    void Execute();
    void setValue(int val);
    private:
    int value;
    };

    所以我遇到的问题是我希望能够创建各种命令的数据结构,这些命令代表解析树,因为精神解析它。

    最佳答案

    更新以响应已编辑的问题

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

    因此,事不宜迟,让我解释一下:

  • 您仍然只想解析 (int, string) 对,但每行
  • 使用 qi::blank_type 作为船长
  • roster % eol解析名册行
  • 我的样本解析为名册 vector (每行一个)
  • 每个名册包含可变数量的学生:
    start   = roster %  eol;
    roster = +student;
    student = int_ >> string_p;

    Note: Rule #1 Don't complicate your parser unless you really have to

  • 您想输出单个元素(“命令”?!?) - 我假设这将是不平凡的部分是相同的部分 Student可能会出现在几个名册中?
  • 通过定义学生的总排序:
    bool operator<(Student const& other) const {
    return boost::tie(i,s) < boost::tie(other.i, other.s);
    }

    您可以将独特的学生集合存储在例如一个 std::set<Student>


  • 也许生成“变量名”(我的意思是 r1s1s2 ...)也是任务的一部分。因此,为了为每个学生建立一个唯一的“变量名”,我创建了一个学生的双向映射(解析后,请参阅 规则 #1 :除非绝对必要,否则不要使解析器复杂化):
    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});

  • 这就是我能想到的一切。我在这里使用了 c++11 和 boost 来节省代码行,但是在没有 c++11/boost 的情况下编写它也相当简单。 C++03 version online now

    以下示例输入:
    ParsedT3Data const data = parseData(
    "23 Bryan 45 Tyler 4 Stephen\n"
    "7 Mary 45 Tyler 8 Stephane\n"
    "23 Bryan 8 Stephane");

    结果(见 Live On Coliru ):
    parse success
    s1 - Student - [int 23][string Bryan]
    s2 - Student - [int 45][string Tyler]
    s3 - Student - [int 4][string Stephen]
    s4 - Student - [int 7][string Mary]
    s5 - Student - [int 8][string Stephane]
    r1 [s1][s2][s3]
    r2 [s4][s2][s5]
    r3 [s1][s5]

    完整代码:
    #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.“问题在于它将 vector 嵌套到 std::vector> 类型数据中,我无法使用”

    这里的解决方案是
  • boost::vector<> 在实例化时允许不完整的元素类型(Boost Containers 还有其他几个漂亮的属性,去阅读它们!)
  • boost::variantrecursive_wrapper<>所以你确实可以制作逻辑树。我在 中有很多答案和 显示这种方法的标签(例如,用于表达式树)。

  • 2. 从语义 Action 中调用工厂方法

    我有一些小提示:
  • 您可以使用 qi::_1 , qi::_2 ... 引用复合属性的元素
  • 你应该更喜欢使用 phoenix::bind凤凰 Actor 内部(语义 Action 为凤凰 Actor )
  • 您可以分配给 qi::_pass指示解析器失败

  • 这是语法的简化版本,它显示了这些操作。我实际上并没有 build 一棵树,因为您没有描述任何所需的行为。相反,我只是在向树中添加节点时打印一条调试行。

    看到它 Live on Coliru
    #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
    '

    关于c++ - Boost.Spirit 语法。属性和 _val 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20170495/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com