月度归档:2011年09月

PHP的词法解析器:re2c

re2c是一个扫描器制作工具,可以创建非常快速灵活的扫描器。它可以产生高效代码,基于C语言,可以支持C/C++代码。 与其它类似的扫描器不同,它偏重于为正则表达式产生高效代码(和他的名字一样)。因此,这比传统的词法分析器有更广泛的应用范围。 你可以在sourceforge.net获取源码。

PHP在最开始的词法解析器是使用的是flex,后来PHP的改为使用re2c。 在源码目录下的Zend/zend_language_scanner.l 文件是re2c的规则文件, 如果需要修改该规则文件需要安装re2c才能重新编译。

re2c调用方式:

re2c [-bdefFghisuvVw1] [-o output] [-c [-t header]] file

我们通过一个简单的例子来看下re2c。如下是一个简单的扫描器,它的作用是判断所给的字符串是数字/小写字母/大小字母。 当然,这里没有做一些输入错误判断等异常操作处理。示例如下:

#include <stdio.h>

char *scan(char *p){
#define YYCTYPE char
#define YYCURSOR p
#define YYLIMIT p
#define YYMARKER q
#define YYFILL(n)
    /*!re2c
      [0-9]+ {return "number";}
      [a-z]+ {return "lower";}
      [A-Z]+ {return "upper";}
      [^] {return "unkown";}
     */
}

int main(int argc, char* argv[])
{
    printf("%s\n", scan(argv[1]));

    return 0;
}

如果你是在ubuntu环境下,可以执行下面的命令生成可执行文件。

re2c -o a.c a.l
gcc a.c -o a
chmod +x a
./a 1000

此时程序会输出number。

我们解释一下我们用到的几个re2c约定的宏。

  • YYCTYPE 用于保存输入符号的类型,通常为char型和unsigned char型
  • YYCURSOR 指向当前输入标记, -当开始时,它指向当前标记的第一个字符,当结束时,它指向下一个标记的第一个字符
  • YYFILL(n) 当生成的代码需要重新加载缓存的标记时,则会调用YYFILL(n)。
  • YYLIMIT 缓存的最后一个字符,生成的代码会反复比较YYCURSOR和YYLIMIT,以确定是否需要重新填充缓冲区。

参照如上几个标识的说明,可以较清楚的理解生成的a.c文件,当然,re2c不会仅仅只有上面代码所显示的标记, 这只是一个简单示例,更多的标识说明和帮助信息请移步 re2c帮助文档http://re2c.org/manual.html

更多编译器相关算法: Compiler Algorithms

项目延期和重构

项目背景及延期原因分析

这是一个用作宣传的系统,它由客户端,Flash端,服务端三部分组成, 客户端放在客户机器上,实现一些本地的播放及相关操作; Flash端被嵌入到客户端中,调用服务端的数据。 服务端做播放内容的管理及提供数据接口服务。

现在已经有一个1.2的版本在生产环境运行。现在的需求是增加一些功能并且将旧的耦合较多的部分进行重构,以插件的方式提供播放内容。

这三块分别是三个研发部门抽调的开发人员实现,并且开发人员没有换到一个区域办公,人在不同的楼层。 考虑到这是一个不到9人月的小项目,所以在项目估算的时候并没有做基于代码行的估算,而是以一种比较粗犷的工作量估算方法。 在估算过程中,有针对这三块功能进行分别估算,但是在Flash端的估算过程中没有体现出这是一个以细化需求为目的的估算过程。 开发在正常的进行,项目一切正常,然而由于其它项目需要,Flash端的开发人员需要进入其它项目, 于是将此项目的开发工作交接给另一个开发人员,此时Flash端的开发进度就开始脱离了项目经理的掌控。 等到服务端和客户端的开发完成,测试完成,风险时间用完,Flash端的开发工作才基本完成。但是Flash端是客户端和服务端的中转地, 起着至关重要的作用。待开发工作完成后,进行需求确认,发现现有的功能较旧版有较多出入,一部分必须有的功能并没有实现, 实现的功能中还有较多的BUG。于是项目延期……

原因分析

基于项目管理的角度分析整个项目过程,存在以下问题:

  1. 估算过程问题
  2. 人员变更问题
  3. 项目经理进度把控问题
  4. 风险识别不充分

估算过程应该是一个细化需求,指导开发人员更了解所开发功能的过程,从而在对需求的理解上对整个开发周期进行估算。 此项目估算过程过于草率,没有体现估算的价值。 人员变更没有在项目管理过程中体现,在人员变更时对于工作的交换及需求学习等活动都不充分,甚至没有。 虽然有一些客观原因,但是项目经理确实没有实时的现场的跟进Flash端开发的进度,过于充分相信开发人员对于进度的描述。 风险识别过程不充分,特别是人员变更及采用工作量估算时,没有对风险有一个充分的识别。

重构对项目的影响和如果可以重来

基于代码开发的角度,主要是Flash端重构的问题。 重新认识下Martin Fowler在《重构》这本书中对于重构的定义:在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。

我们这次重构是有必要的,在需求列表中有将旧的耦合较多的部分进行重构,以插件的方式提供播放内容的需求项。 但是从现状来看,这次的重构已经不再是纯粹的重构了,它变成了一个重写的过程。旧的所有的功能都重写了,并且一些资源的使用都是采用的新的。 没有认清重构的本质,没有定义好重构的范围,这是此次问题的关键所在。 不改变代码外在行为的前提在本次重构中没有体现,并且对于外在行为的定义和识别活动也没有进行,这是一个问题。

如果可以重来,重构前开发人员对于原有代码外在行为的进行需求识别和功能细节识别, 这个可以简单点,开发人员给出一个check list,产品、测试都可以使用这个list, 同样这也是自己重构过程中的指向灯。在给出列表后,产品和测试人员需要对这个列表进行审核, 确认是否和已经的外在行为一致,这些活动在开发前和开发完成后都要进行。

另一些思考

  1. 基于不同的技术的项目,可能项目经理对这些领域不了解,但是可以看到实际的产出,在一些功能实现完成后,尽量到开发人员的位置上确认其描述。
  2. 对于跨部门的项目,虽然推动其它部门的人做事有一定的难度,但是事是必须要做的,需要更多的关注和协调。
  3. 如果可以,项目组成员集中在一个区域是最好的,不过有时候也只是想想而已。