• 12.4. 动作

    12.4. 动作

    到目前为止,你已经知道了如何定义一个语法,以得到一个新的词法分析器,用于识别一个给定的文本是否具有该语法的规则所规定的结构。 但是此刻,数据的格式仍未被解释,因为从结构化格式如 JSON 中所读取的数据并没有被进一步处理。

    要对由分析器识别出来的符合某个特定规则的数据进行处理,可以使用动作(action)。 动作是一些与规则相关联的函数。 如果词法分析器识别出某些数据符合某个特定的规则,则相关联的动作会被执行,并把识别得到的数据传入进行处理,如下例所示。

    1. #include <boost/spirit.hpp>
    2. #include <string>
    3. #include <fstream>
    4. #include <sstream>
    5. #include <iostream>
    6.  
    7. struct json_grammar
    8. : public boost::spirit::grammar<json_grammar>
    9. {
    10. struct print
    11. {
    12. void operator()(const char *begin, const char *end) const
    13. {
    14. std::cout << std::string(begin, end) << std::endl;
    15. }
    16. };
    17.  
    18. template <typename Scanner>
    19. struct definition
    20. {
    21. boost::spirit::rule<Scanner> object, member, string, value, number, array;
    22.  
    23. definition(const json_grammar &self)
    24. {
    25. using namespace boost::spirit;
    26. object = "{" >> member >> *("," >> member) >> "}";
    27. member = string[print()] >> ":" >> value;
    28. string = "\"" >> *~ch_p("\"") >> "\"";
    29. value = string | number | object | array | "true" | "false" | "null";
    30. number = real_p;
    31. array = "[" >> value >> *("," >> value) >> "]";
    32. }
    33.  
    34. const boost::spirit::rule<Scanner> &start()
    35. {
    36. return object;
    37. }
    38. };
    39. };
    40.  
    41. int main(int argc, char *argv[])
    42. {
    43. std::ifstream fs(argv[1]);
    44. std::ostringstream ss;
    45. ss << fs.rdbuf();
    46. std::string data = ss.str();
    47.  
    48. json_grammar g;
    49. boost::spirit::parse_info<> pi = boost::spirit::parse(data.c_str(), g, boost::spirit::space_p);
    50. if (pi.hit)
    51. {
    52. if (pi.full)
    53. std::cout << "parsing all data successfully" << std::endl;
    54. else
    55. std::cout << "parsing data partially" << std::endl;
    56. std::cout << pi.length << " characters parsed" << std::endl;
    57. }
    58. else
    59. std::cout << "parsing failed; stopped at '" << pi.stop << "'" << std::endl;
    60. }
    • 下载源代码

    动作被实现为函数或函数对象。 如果动作需要被初始化或是要在多次执行之间维护某些状态信息,则后者更好一些。 以上例子中将动作实现为函数对象。

    print 是一个函数对象,它将数据写出至标准输出流。 当其被调用时,重载的 operator()() 操作符将接受一对指向数据起始点和结束点的指针,所指范围即为被执行该动作的规则所识别出来的数据。

    这个例子将这个动作关联至在 member 之后作为第一个符号出现的非终结符号 string。 一个类型为 print 的实例被放在方括号内传递给非终结符号 string。 由于 string 表示的是 JSON 对象的键-值对中的键,所以每次找到一个键时,类 print 中的重载 operator()() 操作符将被调用,将该键写出到标准输出流。

    我们可以定义任意数量的动作,或将它们关联至任意数量的符号。 要把一个动作关联至一个字面值,必须明确给出一个词法分析器。 这与在非终结符号 string 的定义中指定 boost::spirit::ch_p 类没什么不同。 以下例子使用了 boost::spirit::str_p 类来将一个 print 类型的对象关联至字面值 true

    1. #include <boost/spirit.hpp>
    2. #include <string>
    3. #include <fstream>
    4. #include <sstream>
    5. #include <iostream>
    6.  
    7. struct json_grammar
    8. : public boost::spirit::grammar<json_grammar>
    9. {
    10. struct print
    11. {
    12. void operator()(const char *begin, const char *end) const
    13. {
    14. std::cout << std::string(begin, end) << std::endl;
    15. }
    16.  
    17. void operator()(const double d) const
    18. {
    19. std::cout << d << std::endl;
    20. }
    21. };
    22.  
    23. template <typename Scanner>
    24. struct definition
    25. {
    26. boost::spirit::rule<Scanner> object, member, string, value, number, array;
    27.  
    28. definition(const json_grammar &self)
    29. {
    30. using namespace boost::spirit;
    31. object = "{" >> member >> *("," >> member) >> "}";
    32. member = string[print()] >> ":" >> value;
    33. string = "\"" >> *~ch_p("\"") >> "\"";
    34. value = string | number | object | array | str_p("true")[print()] | "false" | "null";
    35. number = real_p[print()];
    36. array = "[" >> value >> *("," >> value) >> "]";
    37. }
    38.  
    39. const boost::spirit::rule<Scanner> &start()
    40. {
    41. return object;
    42. }
    43. };
    44. };
    45.  
    46. int main(int argc, char *argv[])
    47. {
    48. std::ifstream fs(argv[1]);
    49. std::ostringstream ss;
    50. ss << fs.rdbuf();
    51. std::string data = ss.str();
    52.  
    53. json_grammar g;
    54. boost::spirit::parse_info<> pi = boost::spirit::parse(data.c_str(), g, boost::spirit::space_p);
    55. if (pi.hit)
    56. {
    57. if (pi.full)
    58. std::cout << "parsing all data successfully" << std::endl;
    59. else
    60. std::cout << "parsing data partially" << std::endl;
    61. std::cout << pi.length << " characters parsed" << std::endl;
    62. }
    63. else
    64. std::cout << "parsing failed; stopped at '" << pi.stop << "'" << std::endl;
    65. }
    • 下载源代码

    另外,这个例子还将一个动作关联至 boost::spirit::real_p。 大多数分析器会传递一对指向被识别数据起始点和结束点的指针,而 boost::spirit::real_p 则将所找到的数字作为 double 来传递。 这样可以使对数字的处理更为方便,因为这些数字不再需要被显式转换。 为了传递一个 double 类型的值给这个动作,我们相应地增加了一个重载的 operator()() 操作符给 print

    除了在本章中介绍过的分析器,如 boost::spirit::str_pboost::spirit::real_p 以外,Boost.Spirit 还提供了很多其它的分析器。 例如,如果要使用正则表达式,我们有 boost::spirit::regex_p 可用。 此外,还有用于验证条件或执行循环的分析器。 它们有助于创建动态的词法分析器,根据条件来对数据进行不同的处理。 要对 Boost.Spirit 提供的这些工具有一个大概的了解,你应该看一下这个库的文档。