2006-09-2520:28 KDIRX 2006-09-2520:28 DIRX 2006-09-2520:07 71 frame.1 2006-09-2520:20 144 frame.y 2006-09-2520:28 36,9971ex.yy.c 3个文件 37,212字节 2个目录7,785,537,536可用字节 D:\work\lex_yacc\chapter03>bison -d frame.y D:\work\lex_yacc\chapter03>dir 驱动器D中的卷是工作区 卷的序列号是54D0-5FC0 D:\work1 lex_yacc\chapter0:3的目录 2006-09-2520:28 <DIR> 2006-09-2520:28 <DIR> 2006-09-2520:07 71 frame.1 2006-09-2520:28 19,416 frame.tab.c 2006-09-2520:28 74 frame.tab.h
2006-09-25 20:28 <DIR> . 2006-09-25 20:28 <DIR> .. 2006-09-25 20:07 71 frame.l 2006-09-25 20:20 144 frame.y 2006-09-25 20:28 36,997 lex.yy.c 3 个文件 37,212 字节 2 个目录 7,785,537,536 可用字节 D:\work\lex_yacc\chapter03>bison -d frame.y D:\work\lex_yacc\chapter03>dir 驱动器 D 中的卷是 工作区 卷的序列号是 54D0-5FC0 D:\work\lex_yacc\chapter03 的目录 2006-09-25 20:28 <DIR> . 2006-09-25 20:28 <DIR> .. 2006-09-25 20:07 71 frame.l 2006-09-25 20:28 19,416 frame.tab.c 2006-09-25 20:28 74 frame.tab.h
2006-09-2520:20 144 frame.y 2006-09-2520:28 36,9971ex.yy.c 5个文件 56,702字节 2个目录7,785,517,056可用字节 D:\work\lex_yacc\chapter03> 过程3.1.总的来说就是如下的几个步骤: 1.将前面的例子rame.1和frame.y保存成为相应的文件 2.flex frame.I 3.bison frame.y 4.gcc frame.tab.c lex.yy.c 提示 实际上经过f1ex和bison的转换之后的C/C+源程序是可以直接在VC里面使用 上面的frame..tab.c是由bison编译frame..y产生的,而lex.yy.c则是由flex 编译frame.1产生的。 好了,一个最简单的lex和yacc程序已经完备了,因此这一章的目的也就已经 达到了。在下一章里面将会对这里的框架例子进行扩充以适应自己特殊的需要, 逐步逐步的实现一个分析CC+源代码的工具程序,但是每一章的结尾都会尽 可能的给出 一个可以编译通过的1ex 源程序。本来也想给出一个计算器 的源程序作为例子的,但是这样的资料已经很多了。这些资料往往不能够让自 己说清楚问题,在自己的开发中还是会遇到千奇百怪的问题,因此为了让自己 能够有机会解决一个新手在开发新程序中可能出现的问题,我也就找了一个我 没有开发过的程序来让自己一步一步的解决这些问题。我想这种方式也许是比 较好的学习方式吧:) 本章完!
2006-09-25 20:20 144 frame.y 2006-09-25 20:28 36,997 lex.yy.c 5 个文件 56,702 字节 2 个目录 7,785,517,056 可用字节 D:\work\lex_yacc\chapter03> 过程 3.1. 总的来说就是如下的几个步骤: 1. 将前面的例子frame.l和frame.y保存成为相应的文件 2. flex frame.l 3. bison frame.y 4. gcc frame.tab.c lex.yy.c 提示 实际上经过 flex 和 bison 的转换之后的 C/C++源程序是可以直接在 VC 里面使用 的! 上面的 frame.tab.c 是由 bison 编译 frame.y 产生的,而 lex.yy.c 则是由 flex 编译 frame.l 产生的。 好了,一个最简单的 lex 和 yacc 程序已经完备了,因此这一章的目的也就已经 达到了。 在下一章里面将会对这里的框架例子进行扩充以适应自己特殊的需要, 逐步逐步的实 现一个分析 C/C++源代码的工具程序,但是每一章的结尾都会尽 可能的给出一个可以编 译通过的 lex 和 yacc 源程序。本来也想给出一个计算器 的源程序作为例子的,但是这样 的资料已经很多了。这些资料往往不能够让自 己说清楚问题,在自己的开发中还是会 遇到千奇百怪的问题,因此为了让自己 能够有机会解决一个新手在开发新程序中可能 出现的问题,我也就找了一个我 没有开发过的程序来让自己一步一步的解决这些问题 。我想这种方式也许是比 较好的学习方式吧:) 本章完!
4、解析C/C+包含文件 摘要 在这 一章里面将要涉及到处理C/C+的包含宏的解析。也就是说要从一大申 C/C+艹包含文件的声明中提取出文件名,以及相互依赖关系等等。实际上在这 章里面使用的Lex和Yacc技术也是非常重要的,这些都会在本章中进行详细讲 解 我们知道对于C/C++包含文件声明是为程序提供了一些库存的功能,因此存 在 中依赖关系,如果把这种依赖关系表达成为Makefile的形式, 那么就可以 自动生成Makefile。.在这一章里面并不会实现自动生成Makefile的功能,而是 仅仅解析出所有的包含文件名,并记录下来。 1.分析 我们知道C/C+中存在两种形式的包含文件,一种是用“<◇”包含的头文件, 是“”包含的头文件, 这两种不同的形 头文件的不同的搜索方式 另外还需要注意的是:这两种方式包含的都是磁盘上存在的文件名。也就是说 只要是磁盘上存在的文件名都可以包含的,都是合法的,因而C/C+里面存在 的有扩展名的头文件和没有扩展名的头文件都是合法的。并且还需要注意的是 C/C+包含的头文件是可以续行的。 因而总结起来需要做到如下的几件事情: 1.处理“◇”和“”两种包含方式 2.处理文件名 3.处理续行 2.Lex文件 #include"main.pp/在其中保存了记录头文件所需要的所有数据结构 #include"frame.tab.h"/∥由Yacc自动生成的所有标记声明,实际上都是C宏 extern "C"{ int yywrap(void) int yylex(void): x INCLUDE %xINCLUDE_FILE
4、解析 C/C++包含文件 摘要 在这一章里面将要涉及到处理 C/C++的包含宏的解析。也就是说要从一大串 C/C++ 包含文件的声明中提取出文件名,以及相互依赖关系等等。实际上在这一 章里面 使用的 Lex 和 Yacc 技术也是非常重要的,这些都会在本章中进行详细讲 解。 我们知道对于 C/C++包含文件声明是为程序提供了一些库存的功能,因此存 在一种依赖关系,如果把这种依赖关系表达成为 Makefile 的形式,那么就可以 自动生成 Makefile。在这一章里面并不会实现自动生成 Makefile 的功能,而是 仅仅解析出所有的包含文件名,并记录下来。 1. 分析 我 们知道 C/C++中存在两种形式的包含文件,一种是用“<>”包含的头文件, 一 种是“""”包含的头文件,这两种不同的形式表达了头文件的不同的搜索方式。 另外还需要注意的是:这两种方式包含的都是磁盘上存在的文件名。也就是说, 只 要是磁盘上存在的文件名都可以包含的,都是合法的,因而 C/C++里面存在 的有扩展 名的头文件和没有扩展名的头文件都是合法的。并且还需要注意的是 C/C++包含的头 文件是可以续行的。 因而总结起来需要做到如下的几件事情: 1. 处理“<>”和“""”两种包含方式 2. 处理文件名 3. 处理续行 2. Lex 文件 %{ #include "main.hpp"// 在其中保存了记录头文件所需要的所有数据结构 #include "frame.tab.h"// 由Yacc自动生成的所有标记声明,实际上都是C宏 extern "C"{ int yywrap(void); int yylex(void); } %} %x _INCLUDE_ %x _INCLUDE_FILE_
"#"[1t]*"include” BEGIN INCLUDE:/进入INCLUDE状态 yylval.clear0:/∥需要将所有的Include 值初始化 return INCLUDE:/∥返回INCLUDE标记 <INCLUDE >[\< BEGIN_INCLUDE_FILE;/进入 INCLUDE_FILE状态 return *yytext:/∥返回引号或者尖括号 <INCLUDE FILE_>[\>]*( yylval.headerfile+=yytext:/记录头文 件字符串 return HEADERFILE://返回头文件标i记 <_INCLUDE_FILE_>[\>]{ BEGIN INITIAL:/∥恢复到初始状态,默认 状态 return *yytext:/∥返回引号或者尖括号 \t\n] :/∥对于额外的空白都不处理直接扔掉 int yywrap(void) return l:/∥只处理一个输入文件 3.Yacc文件 #include <iostream> #include "main.hpp" #define YYDEBUG0/将这个变量设置为I则表示启动Yacc的调试功能 extern "C void yyerror(const char *s) extern int yylex(void): std:vector<Include>g_Includes;/用来记录所有的包含声明 Include g_pInclude;/用来保存新增的包含声明信,息的指针 %token INCLUDE
%% "#"[ \t]*"include" { BEGIN _INCLUDE_;// 进入_INCLUDE_状态 yylval.clear();// 需要将所有的Include 值初始化 return INCLUDE;// 返回INCLUDE标记 } <_INCLUDE_>[\"|<] { BEGIN _INCLUDE_FILE_;// 进入 _INCLUDE_FILE_状态 return *yytext; // 返回引号或者尖括号 } <_INCLUDE_FILE_>[^\">]* { yylval.headerfile+=yytext;// 记录头文 件字符串 return HEADERFILE;// 返回头文件标记 } <_INCLUDE_FILE_>[\"|>] { BEGIN INITIAL;// 恢复到初始状态,默认 状态 return *yytext;// 返回引号或者尖括号 } [ \t\n] ;// 对于额外的空白都不处理直接扔掉 %% int yywrap(void) { return 1;// 只处理一个输入文件 } 3. Yacc 文件 %{ #include <iostream> #include "main.hpp" #define YYDEBUG 0 // 将这个变量设置为 1 则表示启动Yacc的调试功能 extern "C"{ void yyerror(const char *s); extern int yylex(void); } std::vector<Include> g_Includes;// 用来记录所有的包含声明 Include *g_pInclude;// 用来保存新增的包含声明信息的指针 %} %token INCLUDE
%token HEADERFILE program:/empty*/ |program include_preprocess∥用这种递归的方式从有限的标记表 达出无限的内容 include preprocess INCLUDE'<'HEADERFILE'>' /注意这里的$3,实际上就是上面的标记的第三个的意思 /因为yylval被声明为1 nclude结构,参见main.hpp文件 /因而每个标记都是Include结构类型。 g_Includes push back (Include g_pInclude &g_Includes.back(); g_pInclude-->clear(:/∥初始化 g_pInclude-headerfile=s3.headerfile:/∥可以证明s3 的类型就是Include类型 g_pInclude->is_angle =true;/∥是尖括号 g_pInclude->is_quotation=false:/∥不是双引号 INCLUDE HEADERFILE /∥值得说明的是:上面的include_preprocess用表示, /而不是用$0表示。从左向右依次为 preprocess I∥INCLUDE //1 $2 /HEADERFILE 83 //11# $4 g_Includes.push_back(Include() g pInclude &g Includes.back ( g_pInclude->clear0:/初始化 g_pInclude->headerfile $3.headerfile: g_pInclude->is_angle =fa1se://不是尖括号 g_pInclude-->is_quotation=true:/是双g引号 void yyerror(const char *s) std:cerr<<s<<std:endl: int main()
%token HEADERFILE %% program:/* empty */ | program include_preprocess // 用这种递归的方式从有限的标记表 达出无限的内容 ; include_preprocess: INCLUDE '<' HEADERFILE '>' { // 注意这里的$3,实际上就是上面的标记的第三个的意思 // 因为yylval被声明为Include结构,参见main.hpp文件 // 因而每个标记都是Include结构类型。 g_Includes.push_back(Include()); g_pInclude = &g_Includes.back(); g_pInclude->clear();// 初始化 g_pInclude->headerfile = $3.headerfile;// 可以证明$3 的类型就是Include类型 g_pInclude->is_angle = true;// 是尖括号 g_pInclude->is_quotation = false;// 不是双引号 } | INCLUDE '\"' HEADERFILE '\"' { // 值得说明的是:上面的include_preprocess用$表示, // 而不是用$0 表示。从左向右依次为: // include_preprocess $ // INCLUDE $1 // '\"' $2 // HEADERFILE $3 // '\"' $4 g_Includes.push_back(Include()); g_pInclude = &g_Includes.back(); g_pInclude->clear();// 初始化 g_pInclude->headerfile = $3.headerfile; g_pInclude->is_angle = false;// 不是尖括号 g_pInclude->is_quotation = true;// 是双引号 } ; %% void yyerror(const char *s) { std::cerr<< s << std::endl; } int main() {