Spirit 是什么

简单来说,Spirit 是一个 parser generator,功能与 Yacc,ANTLR 类似,且也是基于 EBNF 来描述文法,再基于文法生成 parser,但与前面这些工具相比,它最大的不同点在于它使用了 C++ 代码来对文法进行描述,通过非常残暴的模板编程技巧,在编译阶段就生成了相应的 parser。从使用者的角度来看,文法是用代码进行描述的,因此它天生就能直接加入到你当前的工程中与现成代码揉合在一起。

当然,Spirit 的文法在形式上 EBNF 还是有一点点的出入,比如说,用 ">>" 来连接不同表达式,表示重复的符号放在了表达式的前面等,这些都是受 c++ 语法的限制所做出的折衷,文法的语义其实未变。

初体验

一个经典的整数四则运算如用 EBNF 来描述的话,可以写成如下的形式:

    group       ::= '(' expression ')'factor      ::= integer | groupterm        ::= factor (('*' factor) | ('/' factor))*expression  ::= term (('+' term) | ('-' term))*

其中 group, factor, term, expression 分别称为一个 rule,用于表示怎么去匹配一条相应的文本,上述 EBNF 文法在 Spirit 中可以写成如下形式:

    group       = '(' >> expression >> ')';factor      = integer | group;term        = factor >> *(('*' >> factor) | ('/' >> factor));expression  = term >> *(('+' >> term) | ('-' >> term));

看起来语法好像差不多,只是运算符有些不同,需要指明的是,在 Spirit 中一个 rule 就是一个 parser 对象,一个 parser 对象包含了相应的语法规则使得该 parser 只能 parse 符合这些规则的文本,比如说,我们现在想 parse 出一组用逗号隔开的整数(CSV),则我们可以定义如下一个 rule:

boost::spirit::rule<> csv_int = int_p >> *(',' >> int_p);

上述代码中,int_p 是 Spirit 内建的一个 rule 或者说 parser,该 parser 专门用于 parse 一个整型(除了 int_p,Spirit 还内建了一系列用于 parse 其它基本数据类型的 parser, 具体列表参考这里)。parser 与 parser 通过 ">>" 运算符连接在一起后就组成了一个新的 parser,那么怎么来使用 csv_int 这个新生成的 parser 呢? Spirit 内建定义了一个函数,原型大概如下:

boost::spirit::parse_info<> parse(const char* text, parser, separator);

通过调用 parse() 函数传入需要 parse 的文本与相应的 parser 就能对该文本进行相应的解释,返回结果会指明 parse 的过程是否成功了,及如果出错,在哪个位置出错了。

Semantic actions

前面定义的 csv_int 这个 parser 虽然定义了文法,但它基本没做什么事情,只能用来检查一下某段文本是不是一组逗号隔开的整型,功能显然太弱了,因为通常来说,我们是需要从文本中提取出数据来的,因此 parse 的过程需要支持某些动作,我们需要 parser 在 parse 到某些内容时,能够执行用户指定的行为动作,在 Spirit 中,这个些动作就叫作 semantic action.

Semantic action 是属于一个 parser 的,它的意义在于指明当该 parser 执行成功了之后,要执行哪些操作,而这些操作是由用户指定的。我们可以通过如下方式将一个 semantic action 与一个 parser 联系起来:

parser[func];

至于 func 的原型,当然是有要求的,而且这个要看具体的 parser, 比如说 int_p 这样的 parser,它就只能接受 void func(const int val); 这样的函数,很简洁的语法!现在我们来在将前面用于解释一组整型的代码中加入一个新功能,在 parse 完每一个整型后,我们将得到的数据保存下来。

#include <vector>
#include <boost/spirit.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_object.hpp>std::vector<int> g_output;int on_parse_int(const int val)
{g_output.push_back(val);
}int main()
{boost::spirit::rule<> int_csv_rule = int_p[on_parse_int] >> *(',' >> int_p[on_parse_int]);boost::spirit::parse("2,3,4", int_csv_rule);return 0;
}

semantic action 的实现依赖于 boost 里另一个名声显赫的函数式模板库:phoenix, 上面的例子只是一个简单示范,未及冰山一角,spirit 其实还支持用 lambda 来写回调函数,以及用闭包来在不同的 rule 之间传递用户定义的上下文信息等,功能很强大,有兴趣的读者可以参考下这里相对完整点的一个例子,它实现了一个简单的四则运算及基本的函数调用。

生成的 Parser 的类型

Parser 是整个 Spirit 库的核心功能所在,那么 Spirit 生成的 parser 是怎么进行工作呢? 结论是,spirit 所生成的 parser 就是所谓的 LL recursive decent parser,因此 parse 的时候是从左往右扫描输入,而对 parser 中的文法,采取先左后右的顺序进行匹配的,因此左递归之类的问题需要使用者自己消除,对如下一个例子:

rule<> rule1 = (int_p >> ',' >> int_p) | real_p;

rule1 在 parse 文本时,会优先匹配 (int_p >> ',' >> int_p),如若失败,则再去匹配 real_p。

优缺点

因为使用该库的时间还不是很长,初步上手的感觉,优点上个人觉得有如下几点:

  1. 使用非常方便,尤其当要 parse 一些不太复杂的文本时,写代码的效率很高。
  2. 生成的 parser 执行效率也很好。

结论就是:很好很强大,但与此同时,缺点也明显:

  1. 错误提示不够友好。当一段文本格式上有错误时,spirit 直接从出错的地方返回但却不提供相应的错误信息,上层的代码只知道在哪里出了错,却不知道是因什么出了错,因此很难生成一个有意义的错误提示。
  2. 该库的实现大量使用了模板及符号重载,代码写起来很酷炫,但是一旦出错,错误提示基本没有包含太多有意义的信息,因此调试起来很痛苦,尤其是在使用不够熟练的情况下,因此个人建议在使用 Spirit 时,最好把任务进行适当分解,每完成一个小的任务就先测试确认它功能正常稳定再进行下一步,不要一下子写一大堆代码,先不说功能如不正常难以调试,甚至编译错误时,找到出错的地方都困难。
  3. 该库在实现上严重依赖模板元编程,使用了诸多如 expression template 这样的 coding idiom,parser 的生成实际上是在编译阶段完成,因此当你写的 rule 很复杂时,编译时间会很长,真的很长。

karma::format 例子

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/karma_stream.hpp>#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib> using namespace boost::spirit;
using namespace boost::spirit::ascii;///
int main()
{///// vectorstd::vector<int> v (8);std::generate(v.begin(), v.end(), std::rand); // randomly fill the vectorstd::cout << "Output 8 integers from a std::vector<int>..." << std::endl;// output the container as a sequence without any separationstd::cout << "...without any separation" << std::endl;std::cout << karma::format(*int_,                                // format descriptionv                                     // data) << std::endl << std::endl;// output the container as a space separated sequencestd::cout << "...as space delimited list" << std::endl;std::cout << karma::format_delimited(*int_,                                // format descriptionspace,                                // delimiterv                                     // data) << std::endl << std::endl;std::cout << karma::format_delimited('[' << *int_ << ']',                  // format descriptionspace,                                // delimiterv                                     // data) << std::endl << std::endl;// output the container as a comma separated liststd::cout << "...as comma separated list" << std::endl;std::cout << karma::format(int_ % ", ",                          // format descriptionv                                     // data) << std::endl << std::endl;std::cout << karma::format('[' << (int_ % ", ") << ']',          // format descriptionv                                     // data) << std::endl << std::endl;// output the container as a comma separated list of double'sstd::cout << "...as comma separated list of doubles" << std::endl;std::cout << karma::format(double_ % ", ",                       // format descriptionv                                     // data) << std::endl << std::endl;// output the container as a comma separated list of items enclosed in '()'std::cout << "..as list of ints enclosed in '()'" << std::endl;std::cout << karma::format(('(' << int_ << ')') % ", ",          // format descriptionv                                     // data) << std::endl << std::endl;std::cout << karma::format('[' << (  ('(' << int_ << ')') % ", ")  << ']',                           // format descriptionv                                     // data) << std::endl << std::endl;// output the container as a HTML liststd::cout << "...as HTML bullet list" << std::endl;std::cout << karma::format_delimited("<ol>" << // no delimiting within verbatim*verbatim["  <li>" << int_ << "</li>"]<< "</ol>",                           // format description'\n',                                 // delimiterv                                     // data) << std::endl;// output the container as right aligned columnstd::cout << "...right aligned in a column" << std::endl;std::cout << karma::format_delimited(*verbatim["|" << right_align[int_] << "|"],                                    // format description'\n',                                 // delimiterv                                     // data) << std::endl;std::cout << std::endl;return 0;
}

https://www.boost.org/doc/libs/1_41_0/libs/spirit/example/karma/quick_start1.cpp

karma::format 例子

spirit拼接例子


template<class T>
std::string spiritKarmaJoin(const T &Arr)
{return static_cast<std::stringstream&>(std::stringstream().seekp(0)<< boost::spirit::karma::format((boost::spirit::karma::auto_ % ","), Arr)).str();
}

其中:

  1. aArr可以为std::list<TYPE>  std::vector<TYPE>
  2. TYPE可以为string  short, int, long, double, ... 参考:https://www.boost.org/doc/libs/1_66_0/libs/spirit/doc/html/spirit/karma/reference/auto.html

join拼接例子


#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>int main()
{using boost::adaptors::transformed;using boost::algorithm::join;std::vector<double> v{1.1, 2.2, 3.3, 4.4};std::cout << join( v | transformed( static_cast<std::string(*)(double)>(std::to_string) ), ", " );
}

参考:

https://www.cnblogs.com/catch/p/3921751.html

https://www.ibm.com/developerworks/cn/aix/library/au-boost_parser/index.html

https://blog.csdn.net/GW569453350game/article/details/47807123 
https://panthema.net/2018/0912-Boost-Spirit-Tutorial/

https://panthema.net/2018/0912-Boost-Spirit-Tutorial/examples/

boost spirit ——编译器,语法解析器相关推荐

  1. Boost学习之语法解析器--Spirit

    Boost.Spirit能使我们轻松地编写出一个简单脚本的语法解析器,它巧妙利用了元编程并重载了大量的C++操作符使得我们能够在C++里直接使用类似EBNF的语法构造出一个完整的语法解析器(同时也把C ...

  2. 用 C 语言开发一门编程语言 — 语法解析器

    目录 文章目录 目录 前文列表 编程语言的本质 词法分析 语法分析 使用 MPC 解析器组合库 安装 快速入门 实现波兰表达式的语法解析 波兰表达式 正则表达式 代码实现 前文列表 <用 C 语 ...

  3. CSS大会 | 打破常“规”:挖掘语法解析器规则漏洞

    2019年7月30-31日,第五届互联网安全领袖峰会(CSS 2019)在北京开幕.作为前沿技术安全研究团队代表,Tencent Blade Team两位高级安全研究员受邀登台,探讨如何挖掘语法解析器 ...

  4. boost::coroutine2模块实现解析器的测试程序

    boost::coroutine2模块实现解析器的测试程序 实现功能 C++实现代码 实现功能 boost::coroutine2模块实现解析器的测试程序 C++实现代码 #include <b ...

  5. mysql ddl 语法解析工具_sharding-sphere之语法解析器

    语法解析器,根据不同类型的语句有不同的语法解析器去解析成成SQLStatement,SQL解析器的类图我用脑图画出来如下: SQLParser.png 可以看到,不同的sql有不同的处理解析器去解析, ...

  6. NET Core中使用Irony实现自己的查询语言语法解析器

    在之前<在ASP.NET Core中使用Apworks快速开发数据服务>一文的评论部分,.NET大神张善友为我提了个建议,可以使用Compile As a Service的Roslyn为语 ...

  7. 使用ANTLR做一个简单的Python SQL语法解析器 - 推酷

    使用ANTLR做一个简单的Python SQL语法解析器 - 推酷 使用ANTLR做一个简单的Python SQL语法解析器 - 推酷 posted on 2016-11-14 13:11 lexus ...

  8. sqlparser mysql_SQL语法解析器JSQLParser | IT瘾

    相关 [sql 语法 解析器] 推荐: SQL 语法解释器jsqlparser. 是用java 开发的解析器, 可以生成java类层次结构.. 可以完美解析 表的 增删查改等操作.. 展开它的源码你会 ...

  9. Python语法解析器PLY——lex and yacc in Python - 娄振林专栏 - 博客频道 - CSDN.NET

    Python语法解析器PLY--lex and yacc in Python - 娄振林专栏 - 博客频道 - CSDN.NET Python语法解析器PLY--lex and yacc in Pyt ...

最新文章

  1. 在NVIDIA A100 GPU上利用硬件JPEG解码器和NVIDIA nvJPEG库
  2. java集合租车_Java入门第二季 租车系统
  3. 接口自动化测试系列(一):HTTP状态码
  4. axure rp 创建弹框_如何在Axure RP 9中创建交换机
  5. java 读取集合到流中_Java 10:将流收集到不可修改的集合中
  6. python2版本libnum
  7. matlab的示波器保存figure图像
  8. xNFT Protocol完成天使轮和A轮融资,LD Capital、Fundamental Labs分别领投
  9. java队列 双队列_Java队列– Java队列
  10. python xpath 中文乱码_Python爬虫实战 批量下载高清美女图片!让你们开开眼!
  11. Kali 解决默认启动HDMI没有声音问题
  12. 易灵思FPGA烧写EFINIX 芯片下载使用步骤
  13. 网络通信,IP地址, 端口,socket
  14. 什么是搜索引擎优化(SEO)
  15. CSS学习笔记 01、CSS3基础知识学习
  16. 国货崛起,科技潮流——雷神星驰轮胎
  17. 你应该知道的 89 个操作系统核心概念
  18. 网站服务器内存满了,云服务器内存满了怎么办
  19. 【mysql】mysql 中 text,longtext,mediumtext 字段类型的意思, 以及区别
  20. STM32写FLASH期间导致中断无法响应的解决思路

热门文章

  1. 揭秘肖特基二极管与电源流串联的反应
  2. Hystrix熔断器简介
  3. 大学物理--电磁辐射的量子理论部分
  4. 安卓系统管理软件_安卓平板电脑用户,你的微信又要变了!
  5. 联机/中断网络磁盘驱动器对话框
  6. 例3.2 计算存款利息。有1000元,存一年。(1)活期,年利率为r1,(2)一年期定期,年利率为r2,(3)存两年半年定期,年利率为r3。
  7. 分号与逗号的区别及举例_顿号和逗号的区别
  8. 物联网传感技术——无线传感网概述
  9. 基于lnmp环境配置wordpress,以及403 Forbidden错误解决
  10. 增量式pid分析 及 参数整定