OWenT's blog
  • Introduction
  • About Me
  • 2020
    • 近期对libatapp的一些优化调整(增加服务发现和连接管理,支持yaml等)
    • xresloader转表工具链增加了一些新功能(map,oneof支持,输出矩阵,基于模板引擎的加载代码生成等)
    • 在游戏服务器中使用分布式事务
    • libcopp接入C++20 Coroutine和一些过渡期的设计
    • libatbus 的大幅优化
    • nftables初体验
    • 容器配置开发环境小计
  • 2019
    • PALM Tree - 适合多核并发架构的B+树 - 论文阅读小记
    • 跨平台协程库 - libcopp 简介
    • C++20 Coroutine 性能测试 (附带和libcopp/libco/libgo/goroutine/linux ucontext对比)
    • 尝鲜Github Action
    • 一些xresloader(转表工具)的改进
    • protobuf、flatbuffer、msgpack 针对小数据包的简单对比
    • 协程框架(libcopp) 小幅优化
    • Excel转表工具(xresloader) 增加protobuf插件功能和集成 UnrealEngine 支持
    • Anna(支持任意扩展和超高性能的KV数据库系统)阅读笔记
    • C++20 Coroutine
    • libcopp merge boost.context 1.69.0
    • Google去中心化分布式系统论文三件套(Percolator、Spanner、F1)读后感
    • Rust玩具-企业微信机器人通用服务
  • 2018
    • 使用ELK辅助监控开发测试环境服务质量和问题定位
    • Webpack+vue+boostrap+ejs构建Web版GM工具
    • 2018年的新通用伪随机数算法(xoshiro / xoroshiro)的C++(head only)实现
    • Rust的第二次接触-写个小服务器程序
    • 理解和适配AEAD加密套件
    • atsf4g-co的进化:协程框架v2、对象路由系统和一些其他细节优化
    • 协程框架(libcopp)v2优化、自适应栈池和同类库的Benchmark对比
    • 可执行文件压缩
    • 初识Rust
    • 使用restructedtext编写xresloader文档
    • atframework的etcd模块化重构
    • C++的backtrace
  • 2017
    • ECDH椭圆双曲线(比DH快10倍的密钥交换)算法简介和封装
    • protobuf-net的动态Message实现
    • pbc的proto3接入
    • atgateway内置协议流程优化-加密、算法协商和ECDH
    • 整理一波软件源镜像同步工具+DevOps工具
    • Blog切换到Hugo
    • libcopp v2的第一波优化完成
    • libcopp(v2) vs goroutine性能测试
    • libcopp的线程安全、栈池和merge boost.context 1.64.0
    • GCC 7和LLVM+Clang+libc++abi 4.0的构建脚本
    • libatbus的几个藏得很深的bug
    • 用cmake交叉编译到iOS和Android
    • 开源项目得一些小维护
    • atapp的c binding和c#适配
    • 对象路由系统设计
    • 2016年总结
    • 近期的一个协程流程BUG
  • 2016
    • 重写了llvm+clang+libc++和libc++abi的构建脚本
    • atsf4g完整游戏工程示例
    • atframework基本框架已经完成
    • 游戏服务器的不停服更新
    • 对atbus的小数据包的优化
    • Android和IOS的TLS问题
    • pbc的一个陈年老BUG
    • boost.context-1.61版本的设计模型变化
    • 接入letsencrypt+全面启用HTTP/2
    • 理解Raft算法
    • libatbus基本功能及单元测试终于写完啦
    • 博客文章和文档迁移到gitbook
  • 2015
    • 博客文章和文档迁移到gitbook
    • 给客户端写得LRU缓存
    • 近期活动比较零散
    • 关于BUS通信系统的一些思考(三)
    • 针对Java JIT的优化(转表工具:xresloader)
    • libcopp更新 (merge boost 1.59 context)
    • 小记最近踩得两个C++坑
    • Redis全异步(HA)Driver设计稿
    • Vim常用命令
    • 关于firewalld和systemd的一些命令速记
    • Jenkins(hudson)插件记录
    • 我们的Lua类绑定机制
    • LLVM+Clang+Libcxx+Libcxxabi(3.6)工具链编译(完成自举编译)
    • 回顾2014
    • Android NDK undefined reference to ___tls_get_addr 错误
    • gitlab腾讯企业邮箱配置
  • 2014
    • 回顾2013
    • C++11动态模板参数和type_traits
    • C++又一坑:动态链接库中的全局变量
    • tolua++内存释放坑
    • [转]类似github的框架
    • Lua性能分析
    • 集成Qt Webkit 到cocos2d-x
    • Gitlab环境搭建小计
    • 近期研究VPN的一些记录(OpenVPN,pptp,l2tp)
    • LLVM + Clang + Libcxx + Libcxxabi 工具链编译
    • 关于BUS通信系统的一些思考(二)
    • 关于BUS通信系统的一些思考(一)
    • [libiniloader] Project
    • 记录一些在线编辑器
    • [WP Code Highlight.js] Project
    • 再议 C++ 11 Lambda表达式
    • 基于Chrome插件的开发工具链
    • [ACM] HDU 1006 解题报告
    • Linux 编译安装 GCC 4.9
    • 又碰到了这个解谜游戏,顺带记下地址
    • 简单C++单元测试框架(支持一键切到GTest或Boost.Test)
    • 捣鼓一个协程库
  • 2013
    • std和boost的function与bind实现剖析
    • 不知道是哪一年的腾讯马拉松题目 照片评级 解题报告
    • Lua 挺好用的样子
    • VC和GCC成员函数指针实现的研究(三)
    • VC和GCC成员函数指针实现的研究(二)
    • VC和GCC内成员函数指针实现的研究(一)
    • 一个C++关于成员变量偏移地址的小Trick
    • ptmalloc,tcmalloc和jemalloc内存分配策略研究
    • POJ 2192 Zipper HDU 2059 龟兔赛跑
    • 从Javascript到Typescript到Node.js
    • 网络编程小结
    • 试试Boost.Asio
    • Lnmp yum 安装脚本 (for CentOS)
    • ARM 交叉编译环境搭建
    • Linux 编译安装 GCC 4.8
    • [记录]虚拟硬盘的压缩|磁盘写零
  • 2012
    • Boost.Spirit 初体验
    • “C++的90个坑”-阅读笔记
    • AC自动机
    • C++ 标准过渡期
    • 程序员修炼之道 -- 阅读笔记
    • [转载]狼与哈士奇
    • C++ 新特性学习(八) — 原子操作和多线程库[多工内存模型]
    • C++ 新特性学习(七) — 右值引用
    • 理解Protobuf的数据编码规则
    • 忆往昔ECUST的ACM时代
    • Linux编译安装GCC 4.7
    • JSON显示库 -- showJson (Javascript)
    • C++ 新特性学习(六) — 新的字符串编码和伪随机数
    • C++ 新特性学习(五) — 引用包装、元编程的类型属性和计算函数对象返回类型
    • C++ 新特性学习(四) — Bind和Function
  • 2011
    • C++ 新特性学习(三) — Regex库
    • C++ 新特性学习(二) -- Array、Tuple和Hash库
    • C++ 新特性学习(一) -- 概述+智能指针(smart_ptr)
    • Linux 和 Windows PowerShell 常用工具/命令 记录
    • 非常帅气的Linq to sql
    • 2011 Google Code Jam 小记
    • C++总是很神奇
    • 大学生创新项目[国家级]经费使用记录
    • 常用官方文档整理
    • 我们学校的IPV6很不错嘛
  • 2010
    • 线段树相关问题 (引用 PKU POJ题目) 整理
    • 2010 ACM 赛前笔记
    • POJ PKU 2596 Dice Stacking 解题报告
    • POJ PKU 3631 Cuckoo Hashing 解题报告
    • POJ PKU 1065 Wooden Sticks 3636 Nested Dolls 解题报告
    • HDU 3336 Count the string 解题报告
    • Hash模板 个人模板
    • ZOJ 3309 Search New Posts 解题报告
    • POJ PKU Let's Go to the Movies 解题报告
    • 注册表常用键值意义
    • PKU POJ 1724 ROADS 解题报告
    • 《神奇古今秘方集锦》&《民间秘术大全》
    • PKU POJ 1720 SQUARES 解题报告
    • POJ PKU 2155 Matrix 解题报告
    • PKU POJ 1141 Brackets Sequence 解题报告
    • PKU POJ 2728 Desert King 解题报告
    • PKU POJ 2976 Dropping tests 解题报告
    • PKU POJ 3757 Simple Distributed storage system 解题报告
    • GCD Determinant 解题报告
    • Southeastern European 2008 Sky Code 解题报告
    • HDU HDOJ 3400 Line belt 解题报告
    • 线性筛法求质数(素数)表 及其原理
    • HDU HDOJ 3398 String 解题报告
    • 树状数组模块(个人模板)
    • 浙江理工 省赛总结 team62 By OWenT of Coeus
    • POJ PKU 3659 Cell Phone Network 解题报告
    • USACO 2008 March Gold Cow Jogging 解题报告
    • C#格式化输出(记录)
    • 参加有道难题笔记
    • POJ PKU 2446 Chessboard 解题报告
    • POJ PKU 1986 Distance Queries 解题报告
    • 计算几何算法概览[转载]
    • 关于差分约束(转载)
    • POJ PKU 2826 An Easy Problem?! 解题报告
    • 数论模板(个人模板)
    • 简易四则运算(ACM个人模板)
    • Catalan 数
    • The 35th ACM/ICPC Asia Regional Tianjin Site —— Online Contest 1009 Convex 解题报告
    • JQuery扩展插件--提示信息
    • ACM 计算几何 个人模板
    • 解析网站字符串型参数 Javascript QueryString 操作 TQueryString类
    • POJ PKU 1474 Video Surveillance 解题报告
  • 2009
    • 模式匹配(kmp)个人模板
    • 并查集 模板
    • POJ 3267 The Cow Lexicon 解题报告
    • C/C++语言常用排序算法
    • POJ 2606 Rabbit hunt 2780 Linearity 1118 Lining Up 解题报告
    • 打造最快的Hash表(转) [以暴雪的游戏的Hash为例]
    • ECUST 09年 校赛个人赛第六,七场总结
    • ECUST 09年 校赛个人赛第三场部分解题报告(A,D,F,I)
    • 牛顿迭代解方程 ax^3+bX^2+cx+d=0
    • 09年8月9日 ECUST ACM 练习赛总结
    • 连接最多点直线 (OWenT 个人模板)
    • 点到直线距离 和 线段间最短距离 (OWenT 模板)
    • ECUST 09年 校赛个人训练赛第五场总结
    • ECUST 09年 校赛个人赛第八场(最后一场)总结
    • 09年8月14日 ECUST ACM 练习赛总结
    • 矩阵相关 (增强中)
    • Prime最小生成树(个人模板)
    • 最长单调子序列 复杂度nlog(n)
    • POJ PKU 2549 Sumsets 解题报告
    • POJ PKU 3277 City Horizon 解题报告
    • 我的ACM生涯
    • POJ PKU 2528 Mayor's posters 解题报告
    • POJ PKU 2378 Tree Cutting 解题报告
    • POJ PKU 1990 MooFest 解题报告
Powered by GitBook
On this page
  • 首先来试用Qi库:
  • 接下来是Karma库:
  • 最后是Lex库:
  • 各种Sample的运行结果如下:

Was this helpful?

  1. 2012

Boost.Spirit 初体验

使用代码生成代码是一件十分美妙的事情,于是有了各种代码生成器。但是生成代码,意味着要有对生成规则的分析和处理。 Boost.Spirit 就是这么一个语法分析工具,它实现了对上下文无关文法的LL分析。支持EBNF(扩展巴科斯范式)。 Boost.Spirit 的使用真的是把模板嵌套用到了极致。确实这么做造成了非常强的扩展性,生成的代码也非常高效,但是嵌套的太复杂了,对于初学者而言真心难看懂。 你能想象在学习阶段一个不是太明白的错误导致编译器报出的几十层模板嵌套错误信息的感受吗?而且,这么复杂的模板嵌套还直接导致了编译速度的巨慢无比。 其实在之前,我已经使用过Spirit的Classic版本,即1.X版本,但是过多的复制操作让我觉得当时用得很低效,还好分析的内容并不复杂所以没。体现出来 这回就来研究下功能更强劲的2.X 版本。

Boost.Spirit V2 大体上分为三个部分,Qi、Karma和Lex

Qi 库主要是规则生成和解析器,使用方式类似巴科斯范式 Karma 库则是格式化输出工具 Lex 库是类似Flex的规则生成工具,使用正则表达式,某些时候比直接使用Qi更容易看懂一些

注:所有示例的最终运行结果都放在最后

首先来试用Qi库:

Qi库是以解析器Parser为核心的,首先提供了一些基本的解析器,比如整型、字符、浮点数等等。 具体内容参见Boost.Spirit的Qi部分的Qi Parsers 章节

Qi还包含属性的定义,参见Qi部分的 Compound Attribute Rules 章节,属性定义主要是描述了不同的语法规则锁使用的数据结构,帮助我们判断数据转储和读取的。这里面也描述了Qi的解析器支持的操作符。 使用属性定义说明中的操作符、qi::rule和上一条提到的基本解析器,可以组成复杂地满足我们需求的解析规则

另外就是Qi的动作器部分了,见Qi部分的 Parser Semantic Actions 章节,动作器用于处理匹配玩解析器之后的操作。 申明形式是 parser[f] 其动作函数的形式支持以下格式:

// 如果f是普通或静态函数,f必须满足以下形式之一
void f(Attrib const&);
void f(Attrib const&, Context&);
void f(Attrib const&, Context&, bool&);

// 如果f是仿函数,f必须重载下列操作符之一
void operator()(Attrib const&, unused_type, unused_type) const;
void operator()(Attrib const&, Context&, unused_type) const;
void operator()(Attrib const&, Context&, bool&) const;

// 以上的Attrib都指的是属性器类型

另外,Boost.Spirit还实现了一个Phoenix辅助框架,这是用于生成对类似Lambda表达式的支持的代码的。可以简化很多操作,只要复合Boost.Phoenix的一些规则 这里有个综合Sample:

#include <cstdio>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>


// 提供规则生成工具
#include <boost/spirit/include/qi.hpp>

// 提供高级规则生成工具
#include <boost/spirit/repository/include/qi_confix.hpp>

// 提供数据处理工具
#include <boost/spirit/include/phoenix.hpp>


// 高级自定义Action函数
struct SpiritQiStringAssign {
    mutable std::string& strAssigned;
    SpiritQiStringAssign(std::string& strAttached): strAssigned(strAttached){}

    void operator()(std::vector<char> const &strVecTestOut, boost::spirit::qi::unused_type, boost::spirit::qi::unused_type) const {
        strAssigned.assign(strVecTestOut.data(), strVecTestOut.size());
    }
};

void test_spirit_qi() {
    using namespace boost::spirit;
    using boost::phoenix::at;
    using boost::phoenix::ref;
    using boost::phoenix::push_back;
    using boost::spirit::ascii::space;
    using boost::spirit::ascii::char_;
    using boost::spirit::eol;
    using std::string;

    // ============================================================
    // ============ Spirit QI component, 规则构造工具 ============
    // ============================================================
    string strTest = "123.6 45 ABC";
    double dTestOut = 0.0;
    std::vector<int> stIntTestOut;
    int iTestOut;
    string strTestOut;
    std::vector<char> strVecTestOut;

    bool res = qi::parse(strTest.begin(), strTest.end(), qi::double_, dTestOut);
    std::cout<< "Spirit.Qi    => Line "<< std::setw(4)<< __LINE__<< ": QI::Parse double "<< (res?"true": "false")<< std::endl;

    dTestOut = 1.0;
    /**
     * 复合规则时可使用多种操作符,包括 >>、>、*、+、-、!、& 等等
     *     关于复合规则组合的操作符文档参照 Boost.Spirit 库 Qi 部分 Compound Attribute Rules 章节
     * 
     * qi::rule<Iterator, A1, A2, A3> 可用于建立复杂语法规则(注意要符合EBNF范式,要消除左递归)
     *     关于qi::rule的文档参照 Boost.Spirit 库 Qi 部分 Nonterminals 章节
     * 
     * Qi行为属性(申明形式 规则[函数/仿函数])接受的函数类型为:
     *     函数:
     *         void f(Attrib const&);
     *         void f(Attrib const&, Context&);
     *         void f(Attrib const&, Context&, bool&);
     *     仿函数(以下为N选1):
     *         定义 void operator()(Attrib const&, Context&, bool&) const;
     *         定义 void operator()(Attrib const&, Context&, unused_type) const;
     *         定义 void operator()(Attrib const&, unused_type, unused_type) const;
     * 也可以用 Boost.Phoenix 构造Qi行为属性
     *     示例中分别采用了Phoenix的ref、assign和push_back来获取输入数据
     */
    qi::rule<std::string::iterator> stQiRule = 
        // 第一部分,引用包装+等号操作符,浮点型
        qi::double_[ref(dTestOut) = qi::_1] >> space>>
        // 第二部分,push_back函数,整型
        qi::int_[push_back(boost::phoenix::ref(stIntTestOut), qi::_1)][ref(iTestOut) = qi::_1] >> space>>  
        // 第三部分,自定义函数\仿函数,stl数据结构 
        (*(qi::char_))[SpiritQiStringAssign(strTestOut)];

    res = qi::parse(strTest.begin(), strTest.end(), stQiRule);
    std::cout<< "Spirit.Qi    => Line "<< std::setw(4)<< __LINE__<< ": QI::Parse complex struct "<< (res?"true": "false")<< std::endl;
    std::cout<< "Spirit.Qi    => \t\t double:"<< dTestOut<< " int:"<< stIntTestOut[0]<< " string:"<< strTestOut<< std::endl;


    // Qi 高级规则生成器示例
    using boost::spirit::repository::confix;
    string c_commit_content, cpp_commit_content;
    auto c_comment = confix("/*", "*/")[*((char_ - "*/")[push_back(boost::phoenix::ref(c_commit_content), qi::_1)])];
    qi::rule<string::iterator> cpp_comment = confix("//", eol)[*((char_ - eol)[push_back(boost::phoenix::ref(cpp_commit_content), qi::_1)])];

    strTest = "\r\n Hello /* This is C style comment. */ also // This is CPP style comment \r\n Oh yeah.";

    res = qi::parse(strTest.begin(), strTest.end(), *(*(char_ - "/*" - "//")>> (c_comment | cpp_comment)));
    std::cout<< "Spirit.Qi    => Line "<< std::setw(4)<< __LINE__<< ": QI::Parse confix rule "<< (res?"true": "false")<< std::endl;
    std::cout<< "Spirit.Qi    => \t\t c style comment:"<< c_commit_content<< std::endl;
    std::cout<< "Spirit.Qi    => \t\t c++ style comment:"<< cpp_commit_content<< std::endl;
}

对于上面代码中的高级生成器,可以参见Boost.Spirit的Spirit Repository章节

接下来是Karma库:

这个库是用来把一些STL的数据结构按和Qi一样的规则转化成到输出流的,感觉用处不大,只是一个为了完整而存在的东西。 大体上和Qi差不多,只不过是反过来了。比如,Qi使用的是输入流,Karma使用的是输出流。 另外Karma有一个比较特别的地方,因为规则生成大多数的第一个数据不是Karma组件,所以有个函数karma::eps,用于生成一个空的Karma表达式。 直接上例程吧:

#include <cstdio>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

// 提供数据生成工具
#include <boost/spirit/include/karma.hpp>

// 生成Vector
static std::vector<int> GenIntVec(){
    std::vector<int> ret;
    ret.push_back(3);
    ret.push_back(1);
    ret.push_back(4);
    ret.push_back(1);
    ret.push_back(5);
    ret.push_back(9);
    ret.push_back(2);
    ret.push_back(6);
    ret.push_back(5);
    ret.push_back(3);
    ret.push_back(5);
    ret.push_back(9);

    return ret;
}

void test_spirit_karma() {
    using namespace boost::spirit;
    using boost::spirit::ascii::space;

    // ============================================================
    // ============ Spirit karma component, 输出生成器 ============
    // ============================================================
    std::vector<int> stKarmaIntVec = GenIntVec();
    std::string strKarma;
    std::back_insert_iterator<std::string> stStrInsertIter(strKarma);
    /**
     * 基本规则的使用方式为
     * karma::类型            生成输出. 如: karma::int_
     * karma::类型(匹配值)    只生成值为匹配值的输出. karma::int_()
     * karma::eps(...) << ... << ... 复合生成器
     * karma::eps() 函数,当其内部succeed属性被设为true时,会生成转换规则
     * 具体类型参照 Boost.Spirit 库 Karma 部分 Karma Generators 章节
     *
     * karma 的自定义规则、属性行为等类似 qi,可参照Karma部分的相应章节
     */
    karma::generate_delimited(stStrInsertIter, karma::eps(true)<< '[' < < (karma::int_ % ',')<< ']', space, stKarmaIntVec);
    std::cout<< "Spirit.Karma => Line "<< std::setw(4)<< __LINE__<< ": Karma::generate_delimited => "<< strKarma<< std::endl;

}

最后是Lex库:

可能有人之前听说过Flex库,用来生成代码的。而Boost.Spirit的Lex库的很多地方和它很像(我也没用过Flex,官方是这么说的)。 Lex的好处呢,就是可以用正则表达式描述一个规则,而且可以动态生成。而且可以可Qi混合起来使用。 在研究这个库的时候,我也同时发现,想要真正高效的使用Spirit库,还应该像这里的例程一样,各种模板继承,但是,这也会增加编程的复杂度。

Lex对并不是支持所有正则表达式的语法,其支持的正则表达式规则可以参见 Lex库 的 Supported Regular Expressions 章节

对于Lex库的规则类型分离,首先可以采用和Flex类似的做法,自定义数据分段处理的仿函数,只要完成

template <typename Token>
bool operator()(Token const& t) const

这样的操作符重载即可,在函数中,可以通过不同的ID区分匹配的内容,具体例程下面有

或者,和Qi一样,可以使用扩展的Phoenix功能实现简单的动作器操作

同时,Lex支持命名模式,可以使用lex::lexer::self.add_pattern来创建命名模式和使用{占位符名称}来设置命名占位符的token定义 另外,Lex还可以和Qi结合使用,无论是Lex的模式结构还是按自定义数据分段处理仿函数时使用的ID编号的方法,都有相应的方法让他依据Lex的规则分析,按Qi的动作处理函数处理

Lex还有一个重要的部分,静态规则生成。 静态规则生成比较特殊,首先,生成规则的写法和上面提到的一致,不同的地方有两点首先是lex::lexertl::lexer的使用要改成lex::lexertl::static_lexer 第二处比较特别,就是需要使用lex::lexertl::generate_static_dfa函数,依据规则,生成代码。

上诉所有要点的Sample如下:

#include <cstdio>
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <functional>

// 提供语法分析和转换工具
#include <boost/spirit/include/lex.hpp>
#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/lex_static_lexertl.hpp>
#include <boost/spirit/include/lex_generate_static_lexertl.hpp>

// 可结合Qi使用
#include <boost/spirit/include/qi.hpp>

// 提供类Lambda表达式功能
#include <boost/spirit/include/phoenix.hpp>

// 生成的静态lex文件
#include "spirit_lex_static_test.hpp"

namespace lex = boost::spirit::lex;
namespace qi = boost::spirit::qi;

static std::string g_strLexAnaContent = "Hello World!\r\n I'm OWenT\r\nMy blog is http://www.owent.net";
static enum token_ids
{
    ID_OWENT = 1000,
    ID_EOL,
    ID_CHAR,
    IDANY = boost::spirit::lex::min_token_id + 1,
};

// 自定义functor
static void test_spirit_lex_custom_functor();
// 使用 Phoenix
void test_spirit_lex_phoenix_functor();
// 混合Qi使用
void test_spirit_lex_qi_functor();
// 静态lex生成器
void test_spirit_lex_static();
void test_spirit_lex_static_gencode(); // 静态lex生成器

void test_spirit_lex() {
    using namespace boost::spirit;
    using boost::spirit::ascii::space;

    // ============================================================
    // ============  Spirit Lex component, 语法分析器  ============
    // ============================================================

    // ====== 支持动态和静态分析的类似 Flex 的语法分析生成器 ======
    std::cout<< "Spirit.Lex   => Line "<< std::setw(4)<< __LINE__<< ": Lex::Parse content "<< std::endl<< g_strLexAnaContent<< std::endl;

    // 自定义functor示例
    test_spirit_lex_custom_functor();

    // 使用 Phoenix示例
    test_spirit_lex_phoenix_functor();

    // 混合Qi使用示例
    test_spirit_lex_qi_functor();

    // 静态lex生成器示例
    // 第一步,使用 lex::lexertl::generate_static_dfa 生成代码
    test_spirit_lex_static_gencode();
    // 第二步,使用第一步生成的代码进行编译(第一次编译注释掉下面这行代码,不然不能编译通过的)
    test_spirit_lex_static();
}


// ========== 自定义functor ========== 
template <typename Lexer>
struct word_count_tokens1 : lex::lexer<Lexer>
{
    word_count_tokens1()
    {
        this->self.add
            ("(?i:owent)", ID_OWENT)    // 忽略大小写,统计 owent 个数
            ("\n", ID_EOL)              // 新的一行
            (".", ID_CHAR)              // 换行外的任意字符
        ;
    }
};

struct counter1
{
    int& o;
    int& l;
    counter1(int& _o, int& _l): o(_o), l(_l){}

    template <typename Token>
    bool operator()(Token const& t) const
    {
        switch (t.id()) {
        case ID_OWENT: 
            ++ o;
            break;
        case ID_EOL:
            ++l;
            break;
        case ID_CHAR:
            break;
        }

        return true;        // 继续分析
    }
};

void test_spirit_lex_custom_functor()
{
    int o = 0, l = 1;
    word_count_tokens1<lex::lexertl::lexer<> > functor;
    const char* begin = g_strLexAnaContent.c_str();
    const char* end = begin + g_strLexAnaContent.size();

    bool res = lex::tokenize(begin, end, functor, counter1(o, l));
    std::cout<< "Spirit.Lex   => Line "<< std::setw(4)<< __LINE__<< ": Lex::tokenize ("<< (res?"true": "false")<< ")"<< std::endl;
    std::cout<< "Spirit.Lex   => \t\t "<< "Lines: "<< l<< ", OWenTs: "<< o<< std::endl;
}

// ========== 使用 Phoenix ==========
template <typename Lexer>
struct word_count_tokens2 : lex::lexer<Lexer>
{
    word_count_tokens2()
      : o(0), l(1)
      , owent("(?i:owent)")
      , eol("\n")
      , any(".")
    {
        this->self
            =   owent   [++ boost::phoenix::ref(o)]
            |   eol     [++ boost::phoenix::ref(l)]
            |   any     
            ;
    }

    int o, l;
    lex::token_def<> owent, eol, any;
};

void test_spirit_lex_phoenix_functor()
{
    typedef lex::lexertl::token<std::string::iterator, lex::omit, boost::mpl::false_> token_type;
    typedef lex::lexertl::actor_lexer<token_type> lexer_type;
    word_count_tokens2<lexer_type> word_count_lexer;

    std::string::iterator begin = g_strLexAnaContent.begin();
    std::string::iterator end = g_strLexAnaContent.end();
    lexer_type::iterator_type iter = word_count_lexer.begin(begin, end);

    using lex::lexertl::token_is_valid;
    while(iter != word_count_lexer.end() && token_is_valid(*iter))
        ++ iter;
    std::cout<< "Spirit.Lex   => Line "<< std::setw(4)<< __LINE__<< ": Lex::token_is_valid ("<< (iter == word_count_lexer.end()?"true": "false")<< ")"<< std::endl;
    std::cout<< "Spirit.Lex   => \t\t "<< "Lines: "<< word_count_lexer.l<< ", OWenTs: "<< word_count_lexer.o<< std::endl;
}

// ========== 混合Qi使用 ==========
template <typename Lexer>
struct word_count_tokens3 : lex::lexer<Lexer>
{
    word_count_tokens3()
    {
        // 命名模式
        this->self.add_pattern("OWENT", "(?i:owent)"); 

        // 命名模式绑定
        owent = "{OWENT}";

        this->self.add
            (owent)          
            ('\n') 
            (".", ID_CHAR)
        ;
    }

    lex::token_def<std::string> owent;
};

template <typename Iterator>
struct word_count_grammar3 : qi::grammar<Iterator>
{
    template <typename TokenDef>
    word_count_grammar3(TokenDef const& tok)
      : word_count_grammar3::base_type(start)
      , o(0), l(1)
    {
        start =  *(   tok.owent          [++ boost::phoenix::ref(o)] // 使用Lex内的owent成员定义
                  |   qi::lit('\n')          [++ boost::phoenix::ref(l)] // 和Qi内的一样
                  |   qi::token(ID_CHAR)                               // 使用Lex内定义ID的token
                  )
              ;
    }

    int o, l;
    qi::rule<Iterator> start;
};

void test_spirit_lex_qi_functor()
{
    typedef lex::lexertl::token<std::string::iterator, boost::mpl::vector<std::string> > token_type;
    typedef lex::lexertl::lexer<token_type> lexer_type;
    typedef word_count_tokens3<lexer_type>::iterator_type iterator_type;

    word_count_tokens3<lexer_type> word_count;          // lexer 分析器
    word_count_grammar3<iterator_type> g(word_count);   // 语法解析器

    bool res = lex::tokenize_and_parse(g_strLexAnaContent.begin(), g_strLexAnaContent.end(), word_count, g);
    std::cout<< "Spirit.Lex   => Line "<< std::setw(4)<< __LINE__<< ": Lex::tokenize_and_parse ("<< (res?"true": "false")<< ")"<< std::endl;
    std::cout<< "Spirit.Lex   => \t\t "<< "Lines: "<< g.l<< ", OWenTs: "<< g.o<< std::endl;
}

// ========== 静态lex生成器 ==========
template <typename Lexer>
struct word_count_tokens4 : lex::lexer<Lexer>
{
    word_count_tokens4()
    {
        // 命名模式
        this->self.add_pattern("OWENT", "(?i:owent)"); 

        // 命名模式绑定
        owent = "{OWENT}";

        this->self.add
            (owent)          
            ('\n') 
            (".", IDANY)
        ;
    }

    lex::token_def<std::string> owent;
};

template <typename Iterator>
struct word_count_grammar4 : qi::grammar<Iterator>
{
    template <typename TokenDef>
    word_count_grammar4(TokenDef const& tok)
      : word_count_grammar4::base_type(start)
      , o(0), l(1)
    {
        start =  *(   tok.owent          [++ boost::phoenix::ref(o)]    // 使用Lex内的owent成员定义
                  |   qi::lit('\n')      [++ boost::phoenix::ref(l)]    // 和Qi内的一样
                  |   qi::token(IDANY)                                  // 使用Lex内定义ID的token
                  )
              ;
    }

    int o, l;
    qi::rule<Iterator> start;
};

void test_spirit_lex_static()
{
    typedef lex::lexertl::token<std::string::iterator, boost::mpl::vector<std::string> > token_type;
    typedef lex::lexertl::static_lexer<token_type, lex::lexertl::static_::lexer_owent> lexer_type;    // 【这里和第三个示例不一样】
    typedef word_count_tokens4<lexer_type>::iterator_type iterator_type;

    word_count_tokens4<lexer_type> word_count;          // lexer 分析器
    word_count_grammar4<iterator_type> g(word_count);   // 语法解析器

    bool res = lex::tokenize_and_parse(g_strLexAnaContent.begin(), g_strLexAnaContent.end(), word_count, g);
    std::cout<< "Spirit.Lex   => Line "<< std::setw(4)<< __LINE__<< ": Lex::tokenize_and_parse ("<< (res?"true": "false")<< ")"<< std::endl;
    std::cout<< "Spirit.Lex   => \t\t "<< "Lines: "<< g.l<< ", OWenTs: "<< g.o<< std::endl;
}

#include <fstream>
void test_spirit_lex_static_gencode()
{
    word_count_tokens4<lex::lexertl::lexer<> > word_count;
    std::ofstream out("spirit_lex_static_test.hpp");
    lex::lexertl::generate_static_dfa(word_count, out, "owent");
}

Lex的例程比较长,其实很多部分是一样的,但是为了方便观看,就复制了很多遍

各种Sample的运行结果如下:

============================== Test: Boost.Spirit.Qi ==============================
Spirit.Qi    => Line   49: QI::Parse double true
Spirit.Qi    => Line   80: QI::Parse complex struct true
Spirit.Qi    =>                  double:123.6 int:45 string:ABC
Spirit.Qi    => Line   93: QI::Parse confix rule true
Spirit.Qi    =>                  c style comment: This is C style comment.
Spirit.Qi    =>                  c++ style comment: This is CPP style comment



============================== Test: Boost.Spirit.Karma ==============================
Spirit.Karma => Line   50: Karma::generate_delimited =>  [ 3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 , 5 , 3 , 5 , 9 ]

============================== Test: Boost.Spirit.Lex ==============================
Spirit.Lex   => Line   54: Lex::Parse content
Hello World!
I’m OWenT
My blog is http://www.owent.net
Spirit.Lex   => Line  119: Lex::tokenize (true)
Spirit.Lex   =>                  Lines: 3, OWenTs: 2
Spirit.Lex   => Line  157: Lex::token_is_valid (true)
Spirit.Lex   =>                  Lines: 3, OWenTs: 2
Spirit.Lex   => Line  212: Lex::tokenize_and_parse (true)
Spirit.Lex   =>                  Lines: 3, OWenTs: 2
Spirit.Lex   => Line  267: Lex::tokenize_and_parse (true)
Spirit.Lex   =>                  Lines: 3, OWenTs: 2

最后,我觉得要用这个东西的话还是要比较慎重,这里面有大量模板嵌套,一旦出现一点错误极难分析和调试 另外感觉模板使用过度了些,会导致编译速度大幅下降。所以对其还是要有所取舍。如果要做比较复杂的东西,而不是所有成员都对他极为熟悉,还是少用为妙。 以上都是个人观点和学习记录,如果有不对的地方还请指正。

Previous2012Next“C++的90个坑”-阅读笔记

Last updated 6 years ago

Was this helpful?