C++ 精神语法。属性与价值问题
我正在尝试创建一个Boost::Spirit语法类,它可以阅读相当简单的语法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文件迭代器 基本上,我遇到的问题是移动和使用每个规则的合成属性。我需要根据这些数据创建一个对象树,并且创建所述对象的唯一函数需要在那个时候知道父对象。我使用命令模式延迟创建,直到我解析了所
start = roster;
roster = *student;
student = int >> string;
代码的目标是基于正在解析的输入文件创建命令对象树。创建此语法所使用的迭代器是给定的spirit文件迭代器
基本上,我遇到的问题是移动和使用每个规则的合成属性。我需要根据这些数据创建一个对象树,并且创建所述对象的唯一函数需要在那个时候知道父对象。我使用命令模式延迟创建,直到我解析了所有数据并能够正确构建树为止。到目前为止,我实现这一点的方式是,我的命令都包含其他命令的向量。执行命令时,它只需要父对象,并将相应地创建和附加子对象。然后,对象将在自己的向量中执行每个命令,并将自身作为父对象传递。这将创建我需要的树结构,并将数据保存在tact中
问题是:
我面临的问题是如何在解析数据时构建命令,以及如何将它们加载到适当的向量中。到目前为止,我已经尝试了三种不同的方法
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;
注意:规则#1不要使解析器复杂化,除非您真的必须这样做bool operator<(Student const& other) const { return boost::tie(i,s) < boost::tie(other.i, other.s); }
- 您想输出单个元素(“命令”?!?)-我假设这非常重要的部分是相同的
学生
可能出现在多个名册中的部分
- 通过定义学生的总排序:
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); }
以下示例输入:#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 '
- 通过定义学生的总排序: