Lex和Yacc从入门到精通(7)-筛选信息(容错处理)

 
#if 0
 
    在通常的情况下,我们只关心文本中的一部分信息,但是为了编写词法和语法分析程
序,又不得不将所有的结构信息全部描写出来,例如:我们仅仅关心C++源文档中的类名字
信息,而不关心类是否有成员变量,是否有成员函数以及是否有其它的一些C++内容。将结
构信息全部描述出来的做法是费时费力的,通常的情况往往导致项目的不可完成或者延期
完成。另外,作为程序设计者和代码编写者,都希望将功能局域化而不扩散难度,也非常
希望编写的代码能够简单的不予理睬还没有理解的内容,专心处理自己关心的内容。本篇
文档就以着重考虑处理C/C++类名称信息为例,忽略其它的一切没有进行语法描述的C/C++
信息。这就是Lex和Yacc的错误(error)处理的一个应用:)我非常喜欢:)
 
    下面给出词法和语法分析器的源代码,因为这么简单的程序,看源代码是学习的最好
方法:)
 
#endif
 
// 词法扫描器文件:lex.l
%{
#include <string>
 
// 将yylval的值类型由默认的int修改为std::string类型,实际上可以修改为你认为的任
// 何类型,仅仅只是需要定一个这样YYSTYPE宏即可,特别注意,这个宏定义必须在后面
// 的标记文件yacc.tab.h之前定义,并且在yacc文件中也要有这个YYSTYPE定义,并且必
// 须和这里的保持一致。实际上YYSTYPE的定义在生成的标记文件yacc.tab.h中有一个宏
// 判断,如果用户也就是我们定义了YYSTYPE宏,那么就用我们定义的YYSTYPE,否则就用
// 默认的YYSTYPE,也就是int类型:)
 
#define YYSTYPE std::string
#include "yacc.tab.h"
 
#define LEX_RETURN(arg) yylval=yytext;return arg
%}
d   [0-9]
l   [a-z]
u   [A-Z]
a   {l}|{u}
%%
[;{}]                   {LEX_RETURN(yytext[0]);}
"class"                 {LEX_RETURN(CLASS);}
 
(_|{a})(_|{a}|{d})*     {LEX_RETURN(IDENTIFIER);}
 
[ /t/n]                 /* 忽略空白 */
.                       /* 忽略其它一切没有被处理的文本 */
%%
int yywrap()
{
    return 1;
}
 
 
// 语法分析器文件:yacc.y
%{
#include <iostream>
#define YYSTYPE std::string
extern int yylex();
void yyerror(const char*msg);
%}
%token CLASS IDENTIFIER
%%
program:/* 空 */
       | program class //处理C++ 类
       | program error ';' // 一旦出现错误直接跳到最近的分号处,回复正常的扫描过程
                           // 特别注意这里的标记符号error,它是由yacc自动生成的标记
                           // 和上面的CLASS和IDENTIFIER标记一样都可以直接应用到语法
                           // 描述中
       ;
class:// 特别注意一下下面的class语法描述又调用了program,这是一种嵌套结构的常见做法
       CLASS IDENTIFIER '{' program '}' ';' {std::cout<<"发现类名:"<<$2<<std::endl;}
     ;
%%
void yyerror(const char*msg)
{
    // 错误处理,仅仅是简单的输出一个错误标记,在具体应用中应当能够分析出这种错
    // 误是否已经被处理了,这里为了说明上面的错误信息过滤没有进行这种识别
    std::cerr<< "发现错误" << std::endl;
}
int main()
{
    yyparse();
    return 0;
}
 
// Makefile文件
CC=g++
CFLAGS=
LEX=flex
YACC=bison
YACCFLAGS=-d
TARGET=lexyacc
 
$(TARGET):lex.yy.c yacc.tab.h yacc.tab.c
    $(CC) $(CFLAGS) lex.yy.c yacc.tab.c -o $(TARGET)
lex.yy.c:lex.l
    $(LEX) lex.l
yacc.tab.c yacc.tab.h:yacc.y
    $(YACC) $(YACCFLAGS) yacc.y
 
clean:
    rm -f lex.yy.c yacc.tab.h yacc.tab.c
 
// 从上面的代码中可以看出,通过容错处理之后,我们就可以专心于特定的功能代码编写
// 而不需要考虑其它的信息,这样就可以极大的降低解决问题的难度。在后续的文档中都
// 会采用这种技巧来实现特定的功能。如果对上面的一些描述还不是很清晰的话,可以参
// 见我之前已经写出来的系列文档,在本章中值得说明的只有两点:
// 1:yacc自动生成的error标记的使用
// 2:改变默认的yylval的int类型为std::string类型
// 其实我是在尽可能的使用C++库,目的当然是降低编写代码的难度,减少代码,便于说
// 明问题;)
//
// 好了,本篇文档到此就已经说明了本文开始所提出的问题:D,后续的文档正在努力给出
// 。其实编写Lex和Yacc程序非常简单,只需要注意几个常见错误就可以完成一般的任务
// 了,在下一篇里面将会讲解常见的错误及其处理方法:)敬请关注:)
 
// 下面是实例应用
// 测试文件:sample.cpp
class Point
{
    int x;
    int y;
    int GetX();
    int GetY();
};
 
class Rect
{
    int x;
    int y;
    int w;
    int h;
    int GetX();
    int GetY();
    int GetW();
    int GetH();
};
 
class Wrapper
{
    class Inner1{};
    class Inner2{
        class InnerInner1{float f;};
        class InnerInner2{};
        std::string name;
    };
    bool sex;
};
 
// 编译并运行过程
D:/home/blog/lexyacc>make
flex lex.l
bison -d yacc.y
g++ lex.yy.c yacc.tab.c -o lexyacc
 
D:/home/blog/lexyacc>lexyacc.exe < sample.cpp
发现错误
发现类名:Point
发现错误
发现类名:Rect
发现类名:Inner1
发现错误
发现类名:InnerInner1
发现类名:InnerInner2
发现错误
发现类名:Inner2
发现错误
发现类名:Wrapper
 
D:/home/blog/lexyacc>
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
作者: 胡彦 本框架是一个lex/yacc完整的示例,用于学习lex/yacc程序基本的搭建方法,在linux/cygwin下敲入make就可以编译和执行。 本例子虽小却演示了lex/yacc程序最常见和重要的特征: * lex/yacc文件格式、程序结构。 * 如何在lex/yacc中使用C++和STL库,用extern "C"声明那些lex/yacc生成的、要链接的C函数,如yylex(), yywrap(), yyerror()。 * 重定义YYSTYPE/yylval为复杂类型。 * 用%token方式声明yacc记号。 * 用%type方式声明非终结符的类型。 * lex里正则表达式的定义、识别方式。 * lex里用yylval向yacc返回属性值。 * 在yacc嵌入的C代码动作里,对记号属性($1, $2等)、和非终结符属性($$)的正确引用方法。 * 对yyin/yyout重赋值,以改变yacc默认的输入/输出目标。 * 如何开始解析(yyparse函数),结束或继续解析(yywrap函数)。 本例子功能是,对当前目录下的file.txt文件,解析出其中的标识符、数字、其它符号,显示在屏幕上。linux调试环境是Ubuntu 10.04。 总之,大部分框架已经搭好了,你只要稍加扩展就可以成为一个计算器之类的程序,用于《编译原理》的课程设计。 文件列表: lex.l: lex程序文件。 yacc.y: yacc程序文件。 main.hpp: 共同使用的头文件。 Makefile: makefile文件。 file.txt: 给程序解析的文本文件。 使用方法: 1-把lex_yacc_example.rar解压到linux/cygwin下。 2-命令行进入lex_yacc_example目录。 3-敲入make,这时会自动执行以下操作: (1) 自动调用flex编译.l文件,生成lex.yy.c文件。 (2) 自动调用bison编译.y文件,生成yacc.tab.c和yacc.tab.h文件。 (3) 自动调用g++编译、链接出可执行文件main。 (4) 自动执行main,得到如下结果:。 bison -d yacc.y g++ -c lex.yy.c g++ -c yacc.tab.c g++ lex.yy.o yacc.tab.o -o main id: abc id: defghi int: 123 int: 45678 op: ! op: @ op: # op: $ AllId: abc defghi 参考资料:《LexYacc从入门到精通(6)-解析C-C++包含文件》, http://blog.csdn.net/pandaxcl/article/details/1321552 其它文章和代码请留意我的blog: http://blog.csdn.net/huyansoft 2013-4-27

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值