正则语言引擎:一个简单LEX和YACC结合运用的实例

本文先描述了LEXYACC的书写方法。然后利用LEXYACC编写了一个简单正则语言的引擎(暂时不支持闭包与或运算),生成的中间语言为C语言
正则引擎应直接生成NFA或DFA模拟器的输入文件,但在SDT的设计上就走的是增加,修改状态节点。而本文则采用类似语言编译器,翻译生产对应C语言的语句。

熟悉LEX和YACC工具

lex工具简介

lex格式

[第一部分:定义段]
%%
[第二部分:词法规则段]
%%
[第三部分:辅助函数段]
第一部分

Part1.1

  • 第一部分以符号%{和%}包裹,里面为以C语法写的一些定义和声明
  • 例如,文件包含,宏定义,常数定义,全局变量及外部变量定义,函数声明等。
  • 这一部分被Lex翻译器处理后会全部拷贝到文件lex.yy.c中。
  • 注意,特殊括号%{和%}都必须顶着行首写。
%{
    #include <ctype.h>
    char tokenString[MAXTOKENLEN+1];
%}

Part1.2

  • 第二部分是一组正规定义和状态定义。
  • 正规定义是为了简化后面的词法规则而给部分正规式定义了名字。
  • 每条正规定义也都要顶着行首写。
digit       [0-9]
number      {digit}+
letter      [a-zA-Z]
identifier  {letter}+
newline     \n
whitespace  [ \t]+
  • 状态定义也叫环境定义,它定义了匹配正规式时所处的状态的名字。
  • 状态定义以%s开始,后跟所定义的状态的名字,注意%s也要顶行首写。
  • 例如下面一行就定义了一个名为COMMENT的状态和一个名为BAD的状态,状态名之间用空白分隔:
%s COMMENT BAD
第二部分
  • 词法规则段列出的是词法分析器需要匹配的正规式,以及匹配该正规式后需要进行的相关动作。
"if"            {
  return IF;}
"then"          {
  return THEN;}
"else"          {
  return ELSE;}
  • 每行都是一条规则,该规则的前一部分是正规式,需要顶行首写。
  • 后一部分是匹配该正规式后需要进行的动作,这个动作是用C语法来写的,被包裹在{}之内,被Lex翻译器翻译后会被直接拷贝进lex.yy.c。
  • 正规式和语义动作之间要有空白隔开。其中用{}扩住的正规式表示正规定义的名字。
  • 也可以若干个正规式匹配同一条语义动作,此时正规式之间要用 | 分隔。
第三部分
  • 辅助函数段用C语言语法来写,辅助函数一般是在词法规则段中用到的函数。这一部分一般会被直接拷贝到lex.yy.c中。
LEX中的变量
  • yyin和yyout:这是Lex中本身已定义的输入和输出文件指针。这两个变量指明了lex生成的词法分析器从哪里获得输入和输出到哪里。默认:键盘输入,屏幕输出。
  • yytext和yyleng:这也是lex中已定义的变量,直接用就可以了。
  • yytext:指向当前识别的词法单元(词文)的指针
  • yyleng:当前词法单元的长度。
  • ECHO:Lex中预定义的宏,可以出现在动作中,相当于fprintf(yyout, “%s”,yytext),即输出当前匹配的词法单元。
  • yylex():词法分析器驱动程序,用Lex翻译器生成的lex.yy.c内必然含有这个函数。
  • yywrap():词法分析器遇到文件结尾时会调用yywrap()来决定下一步怎么做:
  • 若yywrap()返回0,则继续扫描
  • 若返回1,则返回报告文件结尾的0标记。
  • 由于词法分析器总会调用yywrap,因此辅助函数中最好提供yywrap,如果不提供,则在用C编译器编译lex.yy.c时,需要链接相应的库,库中会给出标准的yywrap函数(标准函数返回1)

一个简易的词法分析器

保留字有

int float main return while do if else for

符号有

< <= = <> > >= + - * / ( ) { } ;

双引号内容当作MSG整体。

书写lex分析程序如下:

%{
    #include <stdio.h>
    #include <stdlib.h> 
    #define LT                  1
    #define LE                  2
    #define GT                  3
    #define GE                  4
    #define EQ                  5
    #define NE                  6

    #define WHILE               18
    #define DO                  19
    #define ID          20
    #define NUMBER      21
    #define RELOP       22
    #define NEWLINE     23
    #define IF          24
    #define ELSE        25
    #define FOR         26

    #define PLUS        27
    #define MINUS       28
    #define TIMES       29
    #define OVER        30
    #define LPAREN      31
    #define RPAREN      32
    #define SEMI        33
    #define OP          34

    #define LBRACE      35
    #define RBRACE      36

    #define INT         37
    #define MAIN        38
    #define RETURN      39
    #define FLOAT       40
    #define MSG         41

    #define ERRORCHAR   42

    int yylval;
    int installID ();
    int installNum ();
%}

delim       [ \t \n]
ws          {delim}+
letter      [A-Za-z_]
digit       [0-9]
id          {letter}({letter}|{digit})*
number      {digit}+(\.{digit}+)?(E[+-]?{digit}+)?

%%

{ws}          {;}
int           {
  return (INT);}
float         {
  return (FLOAT);}
main          {
  return (MAIN);}
return        {
  return (RETURN);}
while         {
  return (WHILE);}
do            {
  return (DO);}
if            {
  return (IF);}
else          {
  return (ELSE);}
for           {
  return (FOR);}
{id}          {yylval = installID (); return (ID);}
{number}      {yylval = installNum (); return (NUMBER);}
"<"           {yylval = LT; return (RELOP);}
"<="          {yylval = LE; return (RELOP);}
"="           {yylval = EQ; return (RELOP);}
"<>"          {yylval = NE; return (RELOP);}
">"           {yylval = GT; return (RELOP);}
">="          {yylval = GE; return (RELOP);}
"+"           {yylval = PLUS; return (OP);}
"-"           {yylval = MINUS; return (OP);}
"*"           {yylval = TIMES; return (OP);}
"/"           {yylval = OVER; return (OP);}
"("           {
  return LPAREN;}
")"           {
  return RPAREN;}
";"           {
  return SEMI;}
"{"           {
  return LBRACE;}
"}"           {
  return RBRACE;}
"\""          { 
                  char c;
                  int i = 0;
                  do{
                        y
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作者: 胡彦 本框架是一个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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值