编译原理实战——使用Lex/Flex进行编写一个有一定词汇量的词法分析器

Posted littlepage

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译原理实战——使用Lex/Flex进行编写一个有一定词汇量的词法分析器相关的知识,希望对你有一定的参考价值。

编译原理实战——使用Lex/Flex进行编写一个有一定词汇量的词法分析器

by steve yu 2019.9.30

参考文档:1.https://blog.csdn.net/mist14/article/details/48641349
2.https://wenku.baidu.com/view/1c6398903868011ca300a6c30c2259010202f3a4.html

1.Flex工具的概述

Flex工具是生成C语言的工具,我们在日常生活中,如果直接使用C语言进行编写词法分析器,会嵌套太多的if语句,或者switch语句,那么会使我们的代码出现混乱(可读性较差),于是,Flex工具,可以让我们直接使用正规式,进行词法分析。

2.Flex的安装与使用(CentOS 系统为例)

yum intsall flex -y

3.“.l”文件的概述

我们安装完flex工具,可以编写一个文件,这个文件可以通过flex编译成C程序,进行词法分析。

flex的语法被分为3个部分:

definitions
%%
rules
%%
user subroutines

defnitions

这部分由正则式组成,我们在正规式的编写中,有这样语法规则。

1. INT [1-9][0-9]*|[0]  /*整数类型,0或不以0开头的由0-9组成的字符串*/
2. FLOAT [0-9]*[.][0-9]+([eE][+-]?[0-9]*|[0])?f?    /*浮点数格式*/
3. LP \(    /*一个左圆括号*/

rules

rules规则部分语法如下:

LABEL1 |
LABLE2 |
...
 
/*TODO*/

TODO部分是告诉编译器在匹配到字符串后需要做什么。

INT 
    printf("Pick up an integer, value is %d", atoi(yytext));
    printf("Pick up an integer, value is %s", yytext);

user subroutines

此处主要是放置用户需要执行的c语言代码。他们会被原封不动地加入到lex.yy.c文件的末尾。
这里一般用来存放main函数,详细会在后面说明。

4.文档(这边以C程序的关键字为例)

数据类型关键字(12个)

char 1
double 2
enum 3
float 4
int 5
long 6
short 7
signed 8
struct 9
union 10
unsigned 11
void 12

控制语句关键字(12个)

for 13
do 14
while 15
break 16
continue 17
if 18
else 19
goto 20
switch 21
case 22
default 23
return 24

存储类型关键字(4个)

auto 25
extern 26
register 27
static 28

其他关键字

const 29
sizeof 30
typedef 31
volatile 32

**非关键字其他*

INT_DEX 33 [1-9][0-9]*|[0]
INT_HEX 34 [0][Xx][1-9a-fA-F][0-9a-fA-F]*|[0]
INT_OCT 35 [0][1-7][0-7]*|[0]
SEMI 36 ;
COMMA 37 ,
QUO 38
DOUQUO 39 "
EQ 40 =
PLUS 41 +
MIN 42 -
MULTI 43 *
DIV 44 /
AND 45 &&
OR 46 ||
SIAND 47 &
SIOR 48 |
LP 49 (
RP 50 )
LB 51 [
RB 52 ]
LC 53
RC 54
SPACE 55 \n\r\t
ID 56 [a-zA-Z][a-zA-Z0-9]*
DOU 57 INT_DEX[.][0-9]+

由于中文是根据编码不同的二进制安全,所以等同非关键字即可处理。(应该全了,不全可以再提醒我补齐)

5.编写.l文件分析代码(这边手写l文件调试了一下午)

/**
analyse_c.l
@author steve yu
@date 2019/9/30
*/
%
#include "stdio.h"
#include "stdlib.h"
%

CHAR char 
DOUBLE double
ENUM enum
FLOAT float
INT int
LONG long
SHORT short
SIGNED signed
STRUCT struct
UNION union
UNSIGNED unsigned
VOID void
FOR for
DO do
WHILE while
BREAK break
CONTINUE continue
IF if
ELSE else
GOTO goto
SWITCH switch
CASE case
DEFAULT default
RETURN return
AUTO auto
EXTERN extern
REGISTER register
STATIC static
CONST const
SIZEOF sizeof
TYPEDEF typedef
VOLATILE volatile
INT_DEX [1-9][0-9]*|[0]
INT_HEX [0][Xx][1-9a-fA-F][0-9a-fA-F]*|[0]
INT_OCT [0][1-7][0-7]*|[0]
SEMI ;
COMMA ,
QUO '
DOUQUO \"
EQ =
PLUS \+
MIN -
MULTI \*
DIV \/
AND &&
OR \|\|
SLAND &
SLOR \|
LP \(
RP \)
LB \[
RB \]
LC \
RC \
SPACE [ \n\r\t]
ID [a-zA-Z][a-zA-Z0-9]*
DOU INT_DEX[.][0-9]+

%%
CHAR 
  printf("%s  1\n",yytext);

DOUBLE 
  printf("%s  2\n",yytext);

ENUM 
  printf("%s  3\n",yytext);

FLOAT 
  printf("%s  4\n",yytext);

INT 
  printf("%s  5\n",yytext);

LONG 
  printf("%s  6\n",yytext);

SHORT 
  printf("%s  7\n",yytext);

SIGNED 
  printf("%s  8\n",yytext);

STRUCT 
  printf("%s  9\n",yytext);

UNION 
  printf("%s  10\n",yytext);

UNSIGNED 
  printf("%s  11\n",yytext);

VOID 
  printf("%s  12\n",yytext);

FOR 
  printf("%s  13\n",yytext);

DO 
  printf("%s  14\n",yytext);

WHILE 
  printf("%s  15\n",yytext);

BREAK 
  printf("%s  16\n",yytext);

CONTINUE 
  printf("%s  17\n",yytext);

IF 
  printf("%s  18\n",yytext);

ELSE 
  printf("%s  19\n",yytext);

GOTO 
  printf("%s  20\n",yytext);

SWITCH 
  printf("%s  21\n",yytext);

CASE 
  printf("%s  22\n",yytext);

DEFAULT 
  printf("%s  23\n",yytext);

RETURN 
  printf("%s  24\n",yytext);

AUTO 
  printf("%s  25\n",yytext);

EXTERN 
  printf("%s  26\n",yytext);

REGISTER 
  printf("%s  27\n",yytext);

STATIC 
  printf("%s  28\n",yytext);

CONST 
  printf("%s  29\n",yytext);

SIZEOF 
  printf("%s  30\n",yytext);

TYPEDEF 
  printf("%s  31\n",yytext);

VOLATILE 
  printf("%s  32\n",yytext);

INT_DEX 
  printf("%s  33\n",yytext);

INT_HEX 
  printf("%s  34\n",yytext);

INT_OCT 
  printf("%s  35\n",yytext);

SEMI 
  printf("%s  36\n",yytext);

COMMA 
  printf("%s  37\n",yytext);

QUO 
  printf("%s  38\n",yytext);

DOUQUO 
  printf("%s  39\n",yytext);

EQ 
  printf("%s  40\n",yytext);

PLUS 
  printf("%s  41\n",yytext);

MIN 
  printf("%s  42\n",yytext);

MULTI 
  printf("%s  43\n",yytext);

DIV 
  printf("%s  44\n",yytext);

AND 
  printf("%s  45\n",yytext);

OR 
  printf("%s  46\n",yytext);

SLAND 
  printf("%s  47\n",yytext);

SLOR 
  printf("%s  48\n",yytext);

LP 
  printf("%s  49\n",yytext);

RP 
  printf("%s  50\n",yytext);

LB 
  printf("%s  51\n",yytext);

RB 
  printf("%s  52\n",yytext);

LC 
  printf("%s  53\n",yytext);

RC 
  printf("%s  54\n",yytext);

SPACE 

ID 
  printf("%s  56\n",yytext);

DOU 
  printf("%s  57\n",yytext);

%%
/*necessary func*/
int yywrap()
  return 1;

/*main func*/
int main(int argc,char** argv)
  if(argc>1)
    if(!(yyin=fopen(argv[1],"r")))
      perror(argv[1]);
      return 1; 
    
  
  while(yylex());
  return 0;

6.编译并测试

flex analyse.l
gcc lex.yy.c
./a.out test

在这个test文本文件代码如下

int main()
 int a=0;
 if(a==6) a=a+1;
 else return -1;
 return 0;

分析结果如下

int  5
main  56
(  49
)  50
  53
int  5
a  56
=  40
0  33
;  36
if  18
(  49
a  56
=  40
=  40
6  33
)  50
a  56
=  40
a  56
+  41
1  33
;  36
else  19
return  24
-  42
1  33
;  36
return  24
0  33
;  36
  54

以上是关于编译原理实战——使用Lex/Flex进行编写一个有一定词汇量的词法分析器的主要内容,如果未能解决你的问题,请参考以下文章

lex / flex规则的格式-模式和动作应该在同一行吗?

JavaCC

编译原理实战入门:用 JavaScript 写一个简单的四则运算编译器(修订版)

编译原理实战入门:用 JavaScript 写一个简单的四则运算编译器词法分析

编译原理实战入门:用 JavaScript 写一个简单的四则运算编译器(修订版)

对编译原理的认识