基础知识
语法分析程序生成器
1
(Parser Generator)
下载和安装生成器
由于系统部分代码使用了语法分析程序生成器 (Parser Generator),所以本文档对生成
器进行介绍。
生成器的版本 0.61。在网上直接下载,便可安装。你可以到网址 http://www.cix.co.uk/
~phil-stearns/,下载生成程序的最新版本。你也可以在网上,检索 Parser Generator,进行下
载。
生成器的主要组成部分
Parser Generator 是集成开发环境,在这里可以进行主要的操作,进行词法文件和语法
文件的编辑,支持高亮语法。
Parser Generator Help 是 WinHelp 形式的帮助文档。
生成器支持的编译器(或称开发环境)
支持 VC1.52
支持 VC4.00
支持 Borland C++4.52
因为一般的编译器都向前兼容,而我们采用 VC6.0,所以我们参考了帮助生成器的帮
助手册中,关于 VC4.00 的配置部分。
生成器的目录结构
bin 目录存放的是主要的可执行程序和帮助。其中, ParGen.exe 是集成开发环境;
1
以后简称生成器。
PARGEN.HLP 是帮助文件;pgvars16.bat 和 pgvars32.bat 未知;ALex.exe 是对词法文件(*.L)
进行处理的命令行程序;ayacc.exe 是对语法文件(*.Y)进行处理的命令行程序。
Borland 目录里面存放了 BorlandC++编译器专用的文件。
EXAMPLES 目录存放了很多例子。可以对这些例子进行研究学习。
INCLUDE 目录存放了所有编译器都会用到的头文件。
LIB 目录存放了编译器使用的静态链接库。其中 BORLAND 存放了 BC++4.52 使用的
库;MSDEV 存放了 VC4.0 使用的库
2
;MSVC 存放了 VC1.52 使用的库。
SOURCE 目录存放了所有编译器都会用到的源码。
建立 VC++6.0 的开发环境
设置目录:运行 VC++6.0。点“ToolsOptionsDirectories”。添加头文件,库文件和
源码所在的目录
3
。
设置项目参数
如果是调试版,应该定义文本宏 YYDEBUG:
如果还是控制台程序,链接时需要链入单线程库 yld.lib;
如果是 GUI 程序,静态链入 MFC,则在链接时需要链入多线程库 ylmtd.lib
如 果 是 GUI 程 序 , 动 态 链 入 MFC , 则 在 链 接 时 需 要 链 入 多 线 程 库
ylmtdlld.lib。
如果是发行版,不得定义文本宏 YYDEBUG:
如果还是控制台程序,链接时需要链入单线程库 yl.lib;
如果是 GUI 程序,静态链入 MFC,则在链接时需要链入多线程库 ylmt.lib
如 果 是 GUI 程 序 , 动 态 链 入 MFC , 则 在 链 接 时 需 要 链 入 多 线 程 库
ylmtdll.lib。
在链接库文件时需要注意:
上面的库应该放在所有库的最后面链接。
词法文件的解析
词法文件的解析:是指根据词法文件(*.L)生成源码形式的词法分析器的过程。我们需
要根据不同的需求,对此解析过程进行控制。
可以通过两种途径来进行解析,所以也有两种途径来对解析过程进行控制:一种是通
过生成器的 IDE;另一种是直接运行命令行程序。
可以控制的内容
4
:
一、 目标语言是 C 语言,还是 C++语言;
二、 输出的实现文件的扩展名是*.C、*.CPP 还是*.CXX。
三、 是否输出头文件
5
。
2
因为我们使用 VC6.0,所以我们也使用该目录下的静态库。
3
需要注意,库文件的目录指向 MSDEV,只能使用最接近 VC6.0 的 4.0 版的文件。
4
这里只列出了主要内容,并没有详细列出所有内容。
5
从而达到实现与声明分离的目的。
四、 如果输出头文件,输出的头文件的扩展名是*.H、*.HPP 还是*.HXX。
五、 如果目标语言是 C 语言,还可以配置目标词法分析器的模式
6
。
通过 IDE 控制词法文件的解析
一次完整的解析过程
运行生成器的 IDE:点“开始程序 Parser Generator Parser Generator”。
打开词法文件:点“fileopen”,在生成器自带的 EXAMPLES 中,随便打开一个扩展
名为*.L 的文件
7
。
解析词法文件:点“ProjectComplie File”。
查看词法分析器:在词法文件所在的目录,会自动生成一个头文件 (*.H、*.HPP 或
*.HXX)和一个实现文件(*.C、*.CXX 或*.CPP)。
设置解析参数
点“OptionsProjectaLex”,以下设置需要注意。
一、 设置目标语言(Terget Language)
二、 是否生成头文件,把声明部分和实现部分隔开(generate include file)
三、 实现文件的扩展名(output file name extension)
四、 头文件的扩展名(include file name extension)
通过命令行程序控制词法文件的解析
准备工作
设置环境变量:为 Path 增加 aLex.exe 所在目录。
一次完整的解析过程
进入文本界面的命令解释程序:点“开始运行”,输入“command”,点“确定”
进入语法文件所在目录:使用 CD 命令
8
。
解析:输入命令 aLex +词法文件名,按回车。便会自动生成扩展名为.C 的文件。
这就是词法分析器。
设置解析参数
你可以运行命令 aLex -? 来查看不同的参数,所代表的意义。
常用参数 -i –Tcpp 来生成 VC++的源码。
建立 VC6.0 的词法分析项目
扩展 VC 的编译能力
目标:在 VC 中,编译器能处理的文件是有限的。对一些特殊的文件,比如词法文件
或语法文件,VC 不能直接处理。VC 提供了一种编译的扩展机制,使得 VC 可以编译任何
6
因为我们使用 C++语言,所以对此不进行更深入的阐述。
7
以下假设打开 C:\ParGen\EXAMPLES\class\ lexer.l。
8
这里假设进入 C:\ParGen\EXAMPLES\class
类型的文件,VC 编译扩展机制。通过 VC 的编译扩展机制,可以使 VC 能处理任何类型的
文件。扩展 VC 的编译能力,应该采取的途径是定制文件的编译。
要定制文件的编译,可以采取的步骤如下:
1、 在文件视图中选中文件,选择设置,切换到“custom build”选项卡。
2、 在 commands 里面输入编译命令。编译命令里面很多地方都需要用到路径。重要路
径如下。
当前目录(.\): (点符号表示)项目文件所在文件夹,而不是工作区所在文件夹。
输入文件的完整路径:$(InputPath),是个相对路径(参照以上当前目录),包括文件
夹、基本名、扩展名三者齐全。
输入文件所在目录:$(InputDir)\,是个相对路径(参照以上当前目录)。
输入文件的基本名:$(InputName),只包括基本名,不包含扩展名和所在目录。
在 Comands 里面同时支持 move、echo 等命令,尤其是 echo 命令,非常适合调试
commands 命令的编写。
具体可以参看项目样例 。
词法文件的写法
9
词法文件的分段
文 件 被两 个 分段 符 (%%) 分 成三 个 段 (section) 。 第一 部分 叫做 声 明 段(Declarations
Section);第二部分叫做规则段(Rules Section);第三部分叫做程序段(Programs Section)。
声明段(Declarations Section):词法分析器的核心是一些正则表达式,在定义正则表达
式的词义动作时,可能会用到一些变量、函数等。因此,要求这些变量和函数,必须在所
有正则表达式之前定义。所以,引入了声明段的概念。提供声明段,专门用于声明这些函
数、定义这些变量。
规则段(Rules Section):专门用来定义正则表达式,及对应的词义动作。
程序段(Programs Section):是一个可选段。用来书写主函数等。
声明段(Declarations Section)
声明段是一系列声明的列表。在声明段中,可以跟 7 种声明,但声明的个数不限。这
7 种声明分别是:①开始状态的声明(Start Declaration);②表声明(Table Declaration);③宏
声明(Macro Declaration);④数组声明(Array Declaration);⑤参数声明(Option Declaration);
⑥名字声明(Name Declaration);⑦包含声明(Name Declaration)。
这些声明的用途:①如果词法分析器要使用开始状态,就可以使用开始状态的声明
(Start Declaration)。②如果有必要改变字符与字符值(也叫字符编码)的对应关系,比如应用
9
书写词法文件,采用词法描述语言。
程序使用的不是 ASCII 字符集,而是其它字符集,就可以使用表声明(Table Declaration)。
③如果想简化规则段中的正则表达式,就可以使用宏声明(Macro Declaration)。④如果要开
发 C++的词法分析器都必须使用名字声明。⑤数组声明(Array Declaration)和⑥参数声明
(Option Declaration)是为了保持与以前的版本的兼容性而保留的。
名字声明(Name Declaration)及紧随其后的代码块
名字声明用来声明词法分析器的名字。如果目标是生成一重的 C 语言词法分析器(a
single model C lexical analyser),有没有名字都无所谓。但是,如果目标是生成多重 C 语言
词法分析器、多实例 C 语言词法分析器或者是 C++ 的词法分析器类 。就必须对名字进行声
明。
需要注意的是:名字声明只影响词法分析器的声明部分,不影响其实现部分
10
。声明
部分使用这里定义的名字,声明词法分析器类。实现部分,使用类名 YYLEXNAME 实现
词法分析器类。同时在头文件中,使用 C 语言的宏定义语句定义了 YYLEXNAME 的别名
为用户指定的类名。从而实现了用户定义的类。
建议:用户定义的类名为 YYLEXNAME。这样一来,通过 VC 的类视图,可以查看各
方法的实现。
定义词法分析器类名的语法为:%name 词法分析器类名 代码块声明(Declaration Code
Block)
11
比如:%name YYLEXNAME
因为我们的目标是使用 C++语言书写词法分析器,所以我们忽略名字声明对 C 语言词
法分析器的影响,而只研究名字声明对 C++语言词法分析器类的影响。首先,非常重要的
一条就是名字声明确定了词法分析器的类名。
生成器自动生成的词法分析器类并不是祖先类,而是一个派生类。从生成器自带的库
中的类派生而出。如果生成默认的词法分析器,将会从类 yyflexer 派生而出;如果生成紧
缩的词法分析器,将会从 yyclexer 派生而出。目前的词法分析器都继承了那些成员,我们
可以参看后面的词法分析器类的成员。
值得高兴的是,生成器为我们的词法分析器自动生成了构造函数。
如果需要为词法分析器类新增成员的话,我们还需要在名字声明之后紧跟代码块声明
(Declaration Code Block)。
代码块声明用一对花括号{}标志始末。花括号里面的内容会一字不漏的复制到生成的
源码中。有的代码块内容会被复制到源码的头文件中,有的代码块会被复制到源码的实现
文件中。书写词法文件,必须采用词法描述语言。根据词法描述语言的规定,代码块声明
只能出现在声明段中。但实际上,不但可以把代码块声明放在声明段中,很多时候规则段
中也放置了代码块。
很多代码块声明看上去和 C 语言的结构体声明很相似。但实际上,我们应该意识到,
在 C 语言或 C++中,花括号是用来定义复合语句、结构体或局部类的。所以,我们在声明
代码块时里面可能也会用到花括号。词法描述语言要求,代码块声明语句里面可以出现花
括号,但是必须成对。
词法文件使用词法描述语言编写,目标是生成词法分析器的源码。而生成的词法分析
器采用 C++语言书写。所以,不可避免地在词法文件中需要嵌入一些 C++语言的单词
(token)。所以很多代码块声明都当作 C++语言的单词串处理了。在里面可以定义字符常量、
字符串常量、块注释和行注释。
在名字声明后面紧跟的第一个代码块声明,专门用来添加成员。在名字声明后的第二
10
当然,前提是声明部分和实现部分,分别放在了头文件中和源文件中。
11
名字后面的代码块声明(Declaration Code Block)是可选的。数量可以是 0 个到 2 个。