C++ Boost Spirit精通教程
作者:无水先生
rule规则
在 Boost.Spirit 中,解析器由规则组成。由于规则通常基于 Boost.Spirit 提供的解析器,因此没有明显的区别。例如, boost::spirit::ascii::digit 既可以是解析器,也可以是规则。通常,规则指的是更复杂的表达式,例如 qi::int_ % ','。
在迄今为止的所有示例中,规则都直接传递给 boost::spirit::qi::parse() 或 boost::spirit::qi::phrase_parse()。通过 boost::spirit::qi::rule,Boost.Spirit 提供了一个类来定义规则变量。例如,如果应该将规则存储在类的成员变量中,则需要 boost::spirit::qi::rule。
Example11.13.Defining rules withboost::spirit::qi::rule
#include <boost/spirit/include/qi.hpp> #include <string> #include <vector> #include <iterator> #include <algorithm> #include <iostream> using namespace boost::spirit; int main() { std::string s; std::getline(std::cin, s); auto it = s.begin(); qi::rule<std::string::iterator, std::vector<int>(), ascii::space_type> values = qi::int_ % ','; std::vector<int> v; if (qi::phrase_parse(it, s.end(), values, ascii::space, v)) { std::ostream_iterator<int> out{std::cout, ";"}; std::copy(v.begin(), v.end(), out); } }
示例 11.13 的工作方式与示例 11.12 类似。如果您输入多个以逗号分隔的整数,它们将用分号显示。与前面的示例相比,解析器没有直接传递给 boost::spirit::qi::phrase_parse(),而是在 boost::spirit::qi::rule 变量中定义。
boost::spirit::qi::rule 是一个类模板。唯一的强制参数是被解析字符串的迭代器类型。在示例中,还传递了另外两个可选模板参数。
第二个模板参数是 std::vector<int>(),它是返回 std::vector<int> 类型向量且不需要参数的函数的签名。该模板参数表示解析的属性类型为int向量。
第三个模板参数是 boost::spirit::qi::phrase_parse() 使用的 skipper 的类型。在示例中,使用了船长 boost::spirit::ascii::space。该船长的类型可通过 boost::spirit::ascii::space_type 获得,并作为模板参数传递给 boost::spirit::qi::rule。
如果您希望您的代码独立于平台并使用 C++11 开发环境,您应该更喜欢 boost::spirit::qi::rule 而不是关键字 auto。如果值是用 auto 定义的,则该示例在 GCC 和 Clang 中可以正常工作。然而,在 Visual C++ 2013 中,只有第一个数字被解析并写入标准输出。
Example11.14.Nesting Rules
#include <boost/spirit/include/qi.hpp> #include <boost/variant.hpp> #include <string> #include <vector> #include <algorithm> #include <iostream> using namespace boost::spirit; struct print : public boost::static_visitor<> { template <typename T> void operator()(T t) const { std::cout << std::boolalpha << t << ';'; } }; int main() { std::string s; std::getline(std::cin, s); auto it = s.begin(); qi::rule<std::string::iterator, boost::variant<int, bool>(), ascii::space_type> value = qi::int_ | qi::bool_; qi::rule<std::string::iterator, std::vector<boost::variant<int, bool>>(), ascii::space_type> values = value % ','; std::vector<boost::variant<int, bool>> v; if (qi::phrase_parse(it, s.end(), values, ascii::space, v)) { for (const auto &elem : v) boost::apply_visitor(print{}, elem); } }
Example11.14.Nesting Rules
#include <boost/spirit/include/qi.hpp> #include <boost/variant.hpp> #include <string> #include <vector> #include <algorithm> #include <iostream> using namespace boost::spirit; struct print : public boost::static_visitor<> { template <typename T> void operator()(T t) const { std::cout << std::boolalpha << t << ';'; } }; int main() { std::string s; std::getline(std::cin, s); auto it = s.begin(); qi::rule<std::string::iterator, boost::variant<int, bool>(), ascii::space_type> value = qi::int_ | qi::bool_; qi::rule<std::string::iterator, std::vector<boost::variant<int, bool>>(), ascii::space_type> values = value % ','; std::vector<boost::variant<int, bool>> v; if (qi::phrase_parse(it, s.end(), values, ascii::space, v)) { for (const auto &elem : v) boost::apply_visitor(print{}, elem); } }
example11.14 定义了两个规则,其中一个引用另一个:values 定义为 value % ',',value 设置为 qi::int_ | qi::bool_。 values 表示可以解析由逗号分隔的任意数量的值。 value 将值定义为整数或布尔值。总之,规则规定用逗号分隔的整数和布尔值可以按任何顺序输入。
为了存储任意数量的值,提供了一个 std::vector 类型的容器。因为值的类型是 int 或 bool,所以需要一个可以存储 int 或 bool 值的类。根据属性类型和运算符的概述,必须使用来自 Boost.Variant 的类 boost::variant。
如果您启动示例并输入以逗号分隔的整数和布尔值,则这些值将写入以分号分隔的标准输出流。这是在 Boost.Variant 提供的函数 boost::apply_visitor() 的帮助下完成的。这个函数需要一个访问者——在这个例子中是类 print 的一个对象。
请注意,布尔值必须输入为 true 和 false。
语法
如果要解析复杂的格式,需要定义多个相互引用的规则,可以用 boost::spirit::qi::grammar 进行分组。
Example11.15.Grouping rules in a grammar
#include <boost/spirit/include/qi.hpp> #include <boost/variant.hpp> #include <string> #include <vector> #include <iostream> using namespace boost::spirit; template <typename Iterator, typename Skipper> struct my_grammar : qi::grammar<Iterator, std::vector<boost::variant<int, bool>>(), Skipper> { my_grammar() : my_grammar::base_type{values} { value = qi::int_ | qi::bool_; values = value % ','; } qi::rule<Iterator, boost::variant<int, bool>(), Skipper> value; qi::rule<Iterator, std::vector<boost::variant<int, bool>>(), Skipper> values; }; struct print : public boost::static_visitor<> { template <typename T> void operator()(T t) const { std::cout << std::boolalpha << t << ';'; } }; int main() { std::string s; std::getline(std::cin, s); auto it = s.begin(); my_grammar<std::string::iterator, ascii::space_type> g; std::vector<boost::variant<int, bool>> v; if (qi::phrase_parse(it, s.end(), g, ascii::space, v)) { for (const auto &elem : v) boost::apply_visitor(print{}, elem); } }
示例 11.15 的工作方式与示例 11.14 类似:您可以按任何顺序输入整数和布尔值,并以逗号分隔。它们将以相同的顺序写入标准输出流,但用分号分隔。该示例使用与前一个相同的规则——值和值。然而,这一次规则被分组在一个语法中。语法在一个名为 my_grammar 的类中定义,该类派生自 boost::spirit::qi::grammar。
my_grammar 和 boost::spirit::qi::grammar 都是类模板。 boost::spirit::qi::grammar 期望的模板参数与 boost::spirit::qi::rule 期望的模板参数相同。要解析的字符串的迭代器类型必须传递给 boost::spirit::qi::grammar。您还可以传递定义属性类型和船长类型的函数的签名。
在 my_grammar 中, boost::spirit::qi::rule 用于定义规则值和值。规则被定义为成员变量并在构造函数中初始化。
请注意,最外层的规则必须与 base_type 一起传递给基类的构造函数。这样,Boost.Spirit 就知道哪个规则是语法的入口点。
一旦定义了语法,就可以像解析器一样使用它。在示例 11.15 中,my_grammar 在 main() 中实例化以创建 g。然后将 g 传递给 boost::spirit::qi::phrase_parse()。
Example11.16.Storing parsed values in structures
#include <boost/spirit/include/qi.hpp> #include <boost/variant.hpp> #include <boost/fusion/include/adapt_struct.hpp> #include <string> #include <vector> #include <iostream> using namespace boost::spirit; typedef boost::variant<int, bool> int_or_bool; struct int_or_bool_values { int_or_bool first; std::vector<int_or_bool> others; }; BOOST_FUSION_ADAPT_STRUCT( int_or_bool_values, (int_or_bool, first) (std::vector<int_or_bool>, others) ) template <typename Iterator, typename Skipper> struct my_grammar : qi::grammar<Iterator, int_or_bool_values(), Skipper> { my_grammar() : my_grammar::base_type{values} { value = qi::int_ | qi::bool_; values = value >> ',' >> value % ','; } qi::rule<Iterator, int_or_bool(), Skipper> value; qi::rule<Iterator, int_or_bool_values(), Skipper> values; }; struct print : public boost::static_visitor<> { template <typename T> void operator()(T t) const { std::cout << std::boolalpha << t << ';'; } }; int main() { std::string s; std::getline(std::cin, s); auto it = s.begin(); my_grammar<std::string::iterator, ascii::space_type> g; int_or_bool_values v; if (qi::phrase_parse(it, s.end(), g, ascii::space, v)) { print p; boost::apply_visitor(p, v.first); for (const auto &elem : v.others) boost::apply_visitor(p, elem); } }
示例 11.16 基于前面的示例,但需要至少两个值。规则值定义为 value >> ',' >> value % ','。
values 中的第一个组件是 value,第二个是 value % ','。第一个组件解析的值必须存储在 boost::variant 类型的对象中。第二个组件解析的值必须存储在容器中。使用 int_or_bool_values,该示例提供了一个结构来存储由规则值的两个组件解析的值。
要将 int_or_bool_values 与 Boost.Spirit 一起使用,必须使用宏 BOOST_FUSION_ADAPT_STRUCT。该宏由 Boost.Fusion 提供。该宏可以将 int_or_bool_values 视为具有 int_or_bool 和 std::vector<int_or_bool> 类型的两个值的元组。因为这个元组具有正确数量的正确类型的值,所以可以使用签名 int_or_bool_values() 定义值。 values 将在 first 中存储第一个解析值,在其他中存储所有其他解析值。
int_or_bool_values 类型的对象作为属性传递给 boost::spirit::qi::phrase_parse()。如果您启动示例并输入至少两个以逗号分隔的整数或布尔值,则它们都存储在属性中并写入标准输出流。
解析器已从上一个示例中使用的内容进行了更改。如果值是用 value % ',' 定义的,则 int_or_bool_values 将只有一个成员变量,并且所有解析的值都可以存储在一个向量中,如前面的示例所示。因此,int_or_bool_values 就像一个只有一个值的元组——Boost.Spirit 不支持。只有一个成员变量的结构将导致编译器错误。该问题有多种解决方法。
练习
创建一个可以加减整数的解析器。解析器应该能够处理像 1+2-5+8 这样的输入,并将结果(这里是 6)写入标准输出。
扩展您的解析器:它现在应该也支持浮点数。此外,应该可以使用分数。新的解析器应该能够处理像 1.2+6/5-0.9 这样的输入,并且应该将结果(这里是 1.5)写入标准输出。
到此这篇关于C++ Boost Spirit精通教程的文章就介绍到这了,更多相关C++ Boost Spirit内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!