学习使用flex

2023-11-06

会对flex做词法分析了解很多。

下面我通过一个例子来详细说明如何使用flex

根据所学的词法分析内容,利用flex构造PL/0语言的词法分析器。

既然是构造PL/0的词法分析器,那么我们有必要看一下pl0语言的简介和相应文法:

2        PL/0语言

Ⅰ.PL/0语言概述.

    PL/0语言是PASCAL语言的子集,它具备一般高级程序设计语言的典型特点。PL/0语言编译程序结构比较清晰,可读性强,充分体现了一个高级语言编译程序实现的基本组织、技术和步骤,是一个非常合适的小型编译程序的教学模型。

 

Ⅱ.PL/0语言文法描述.

   由于只是做PL/0的词法分析,因此这里只列出词法有关的内容,其余略过。

   下面用扩充的EBNF来进行表示:

   元符号说明:

‘< >’:用左右尖括号括起来的中文字表示语法构造成分,或称语法单位,是PL/0的非终结符

   ‘::=’:该符号的左部由右部定义。

   ‘|’:表示“或”,即作不可由多个右部定义。

   ‘{ }’:表示花括号内的语法成分可以重复。在不加上下界时可以重复0到任意次数,有上下界时为可重复次数的限制。

   ‘[ ]’:表示方括号内的成分为任选项。

   ‘()’:表示圆括号内的成分优先。

  

   PL/0语言文法的EBNF表示

   <常量定义>::=<标识符>=<无符号整数>

   <无符号数字>::=<数字>{<数字>}

   <标识符>::=<字母>{<字母>|<数字>}

   <加法运算符>::=+|-

   <乘法运算符>::=*|/

   <关系运算符>::==|#|<|<=|>|>=

   <分界符>::=(|)|,|;|.

 

接下来我们就要来看看lex究竟是什么东西了。

 

2        flex词法生成器

Ⅰ.flex概要.

flex是一个用于生成扫描器的工具,扫描器可识别文本中的词法模式。flex从给定的文件中读取,或者从标准输入中读取(当没有给定文件时)有关要生成的扫描器的说明。这种说明的格式是一对正规表达式和C代码,称之为规则。flex的输出是名为lexyy.c的C源程序,在lexyy.c中定义了一个名为yylex()的函数。lexyy.c可以被编译,并使用-lfl链接选项同flex库链接,以生成可执行文件。执行该文件,它会分析它的输入,察看是否满足正规表达式,只要它发现一个,就会执行相应的C代码。

 

Ⅱ. Flex运行与应用过程.

     Flex和lex的功能、结构几乎完全相同。Lex编译器接收lex源程序(该源程序是对要产生的词法分析器的说明和描述),由lex编译器处理lex源程序后产生一个词法分析器作为输出。一般说来,lex源程序经过Lex编译器处理后产生一个lexyy.c的程序,这就是对应的词法分析器程序,经过C编译器编译后就可以产生一个可执行程序。通过这个可执行程序我们就可以对相应的程序语言做词法分析

 

Ⅲ.flex语言及结构.

Flex源语言结构

说明部分      /* 包含模式宏定义和C语言的说明信息*/

%%

规则部分      /*  转换规则(一般还包含规则对应的动作)*/

%%

用户代码      /*  规则动作部分所需的辅助过程的C代码 */

Flex语言是对表示语言单词集的正规式的描述,以解决正规式规则输入问题。由上图可知Flex语言作为词法分析器自动构造的专用语言,其程序结构由三部分组成。其中,第一部分说明部分包含C代码、模式宏定义等。模式宏定义实际是对识别规则中出现的正规式的辅助的影。如语言的字母可定义为:

Letter [a-zA-Z]

数字可以定义为:

digit[0-9]

   除宏定义外,定义部分的其余代码必须用符号%{ 和 %} 括起来。另外,flex使用的C语言库文件和外部变量以及部分声明的函数,也应分别置于%{ 和 %} 之内。例如下面是一个flex语言的说明部分:

%{ 

#include<stdio.h>

#include<stdlib.h>

int flag;

void function( );

#define err -1

%}

digit[0-9]

letter[a-zA-Z]

newline [/n]

%%

注意:这里其标识符作用的%%和%{ 、%}必须要顶行写,另外,在其中也可以随意添加C语言的注释。

    第二部分,识别的规则部分是flex程序的主体部分。其一般形式是

             模式1   动作1

             模式2   动作2

                   ……

             模式n   模式n

其中模式是对要分析语言的单词的描述,用正规表达式表示。动作是与匹配的模式对应的,一般用C代码(这要看相应的支持平台)表示对模式处理的动作。当识别出某个模式的单词后,词法分析器(flex)要做的动作就是执行相应的程序。详细的模式这里不再给出,flex手册会有比较详细的定义。

    第三部分,即用户代码定义部分,它是对模式进行处理的C函数、主函数等。作为辅助过程它是支持规则动作部分所需要的处理过程,是对规则部分中动作的补充。这些过程如果不是C的库函数,必须给出具体的定义,然后分别编译且与生成的词法分析器装配在一起。

    需要说明的一点是,定义部分和用户代码部分是任选的,规则部分则是必须的。

 

 Ⅳ.lexyy.c的全局变量和函数.

     需要说明的是在flex生成的lexyy.c中,里面是有一些是本身就定义好的一些变量和函数,这些变量和函数对我们完成词法分析的操作有很大的帮助,这里主要介绍这次实验要用到的和常用的一些函数和变量。

     File*yyin   /* 指向词法分析器要接收的待分析程序的指针。如果不指定则默认指向标准输入终端(键盘)。如果我们待分析的程序是文件形式我们可以将这个指针指向该文件的地址指针。 */

File*yyout  /* 同上,唯一不同是该指针指向输出的文件。默认指向标准输出终端(屏幕)。我们可通过重定向该指针改变输出流方向。 */

char*yytext  /* 指向识别的单词的地址;用来保存扫描一次匹配的字符串。*/

int yyleng    /* 匹配的字符串中字符的个数。*/

 

函数

ECHO  /* flex的默认动作,一般来说是输出字符串 */

yywrap() /* 扫描一次完后要调用的函数,返回一个值,当这个值为1的时候分flex就不再继续扫描。*/

yyrestart() /* 重新定向flex的输入 */

当然还有其它很多的函数,比如yymore()、相容分组、条件模式、宏等,这里由于本次实验没有用到,因此不再详细给出。具体可以查看flex手册。

 

Ⅴ.flex词法分析产生器实现概述。

   词法分析器自动生成器的核心是lex编译器,lex编译器的功能是对某语言单词集描述的lex源程序,将其变换为一个能识别该语言单词的词法分析器。而该词法分析器像有限自动机一样取识别处理单词。

   基于lex源程序,lex编译器的实现步骤大致是:

   ⑴对lex源程序识别规则中的每个pi构造一个相应的NFA  Ni。

     ⑵引入唯一初态S,从初态S通过ε弧将所有NFA  Ni(i=1,…,n)连接成新的NFA N’。⑴、⑵两步实际是完成从正规表达式到非确定有限自动机的构造。

   ⑶对NFA  N’确定化,产生DFA  N。

   ⑷DFA  N 最小化。

   ⑸给出控制程序。控制程序的作用是激活有限自动机,即控制输入字符串在有限自动机上运行,一旦达到终态,即识别出lex源程序模式描述的某个单词,转去调用相应的动作部分就可以了。

 

到这里我想应该对flex都有了一定了解了吧,现在我们就开始着手编写flex分析pl/0的输入文件lex.l文件,实际上这个文件的作用就是如何识别pl/0的词法,也就是识别pl/0词法规则的说明文件。从上面讲的我们知道这样的一个文件分为3部分,下面我来详细说明每一部分:


 ㈠说明部分.

      由于词法分析最后的结果是一个二元组,并且本次实验只是给出一个形式化的显示,但是为了更加的形象,因此我用了一个struct结构来描述这个二元组,并且当分析完后将token存放到这个二元组中。另外还有一个函数print()的声明,该函数的主要作用是输出识别到的token。另外还定义一些辅助操作的变量。例如用于读写文件的指针,保存行号、token数目、错误操作符数目的变量。

      接下来是PL/0词法的描述,用正规表达式书写:

      digit         [0-9]     /*数字从0-9*/

letter        [a-zA-Z]  /*字母接受a-z的大小写,同时本程序定义PL/0语言大小写敏感;*/

number        {digit}+  /*无符号整数*/

identifier    {letter}({letter}|{digit})*  /*标识符*/

wrongid       ({digit}+){letter}({letter}|{digit})*   /*错误的id,形如123a等*/

newline       [/n]  /*新行*/

whitespace    [/t]+ /*制表符*/

    

    ㈡规则部分.

      由于说明部分我们已经定义好无符号整数和标识符的正规表达时,并且用了一个别名,而保留字和运算符、分界符比较少,因此规则部分我们就直接根据每个模式写出要执行的对应的动作。在这里我定义的动作就是先将每种单词分种类,然后同一种给一个整数最为代号,然后在print()函数中根据这个代号输出相应的属性。因此动作部分的主要处理工作就是赋值,调用print()函数。

例如: {identifier}       {value=1;print();}

      详细的PL/0词法的保留字、分界符、运算符见后面的附录。

  

 ㈢用户代码.

   用户代码部分主要包括三个函数:

   主函数main()  //给出源文件输入输出的方向,调用扫描器等;

   输出处理函数print()//主要功能就是输出二元组;

   yywrap()   //自带函数,用来结束扫描;

 

此外,要注意:

要注意空格和制表符,因为在源程序中为了排版等不可避免的存在空格和制表符,但是在实际的词法分析中它们是没有具体意义的,因此读到空格或指标符的时候要“吃掉”他们。

注意newline。newline就是换行符,但我们读到换行符的时候,应当对其执行空格和制表符的操作(“吃掉”),但是我们要统计行数,应此我们设立一个变量每当读到一个换行符的时候就递增1。

另外,lex在读到匹配模式的单词后就执行模式对应的动作,但是它会默认地将不匹配的单词输出,因此我们要对分析语言没有的符号进行另外的处理(例如打印出错等)。

 

 

下面是我写的一个pl/0用flex分析生成词法分析器的文件:

%{
#include<stdio.h>
#include<stdlib.h>
void print();                            //输出token序列;
void main(int argc,char*argv[]);         //主函数;
struct token{                            //二元组;
      char*idproperty;    //token属性值;
      char*idname;    //识别的token名字;
}entity[1000];     //定义1000个这样的token,大小可改变;
char*filename;                           //保存结果的文件名;
int errnum=0;     //错误token的数目;
int value;     //属性值int型;
int linenum=1;     //行数;
int count=0;     //token的个数;
int flag=0;
FILE*fpin;     //测试文件指针;
FILE*fpout;     //结果文件指针;
%}
digit         [0-9]
letter        [a-zA-Z]
number        {digit}+
identifier    {letter}({letter}|{digit})*
wrongid       ({digit}+){letter}({letter}|{digit})*
newline       [/n]
whitespace    [/t]+
%%
"procedure" |
"call"  |        
"begin"  | 
"end"  | 
"var"  | 
"const"  | 
"if"  | 
"then"  | 
"while"  | 
"do"  | 
"read"  | 
"write"  | 
"odd"    {value=0;print();}
{identifier}   {value=1;print();}
{wrongid}   {value=6;print();}
{number}   {value=2;print();}
"+"|"-"|"*"|"/"          {value=3;print();}
"<>"  | 
">="  | 
"<="  | 
":="  | 
"="|"#"|"<"|">"          {value=4;print();}
"("|")"|","|";" |
"."                             {value=5;print();}
{newline}   {linenum+=1;}
{whitespace}   {;}
" "    {;}
.    {value=7;print();}
%%
int yywrap()
{  
    fclose(fpin);
    return 1;
}

void print()
{
    count+=1;
    if(flag!=1){
       if((fpout=fopen(filename,"a"))==NULL){
           printf("cannot write the file /n");
           exit(0);
       }
    }
    if(value<=5){
       switch(value){
             case 0:entity[count-1].idproperty="BasicKey";break;
             case 1:entity[count-1].idproperty="identifier";break;
             case 2:entity[count-1].idproperty="number";break;
             case 3:entity[count-1].idproperty="arithmetic-op";break;
             case 4:entity[count-1].idproperty="relation-op";break;
             case 5:entity[count-1].idproperty="boundary-op";break;
       }
       entity[count-1].idname=yytext;
       fprintf(fpout,"%d < %s , %s > /n",count,entity[count-1].idname,entity[count-1].idproperty);
    }else{
         errnum+=1;
         switch(value){
               case 6:entity[count-1].idproperty="Mixed number and letter:";break;
               case 7:entity[count-1].idproperty="Unkown operator:";break;
         }
         entity[count-1].idname=yytext;
         fprintf(fpout,"%d [line:%d]:%s/"%s/" /n",count,linenum,entity[count-1].idproperty,entity[count-1].idname);
    }
    if(flag!=1)fclose(fpout);
}

void main(int argc,char*argv[])
{
    if(argc==1){
      printf("please input the PL//0 program(ctrl+z to end) /n");
      flag=1;
      fpin=stdin;
      fpout=stdout;
    }
    if(argc==2)argv[2]="defresult.txt";
    filename=argv[2];
    if(flag!=1){
       if((fpin=fopen(argv[1],"r"))==NULL){
           printf("cannot open the file /n");
           exit(0);
       }
    }
    yyin=fpin;
    yylex();
    if(flag!=1){
       if((fpout=fopen(filename,"a"))==NULL){
           printf("cannot write the file /n");
           exit(0);
       }
    }
    fprintf(fpout,"/n");
    fprintf(fpout,"%d symbol(s) found. /n %d error(s) found. /n",count,errnum);
    fprintf(fpout,"======================================================================= /n");
    if(flag!=1)fclose(fpout);
    yywrap();


}

 


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/litchh/archive/2004/07/14/40983.aspx

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

学习使用flex 的相关文章

随机推荐

  • 7-52 两个有序链表序列的交集 (20 分)(思路加详解尾插法)come Boby!

    一 题目 已知两个非降序链表序列S1与S2 设计函数构造出S1与S2的交集新链表S3 输入格式 输入分两行 分别在每行给出由若干个正整数构成的非降序序列 用 1表示序列的结尾 1不属于这个序列 数字用空格间隔 输出格式 在一行中输出两个输入
  • 浅谈5G 与4G的区别

    5G 顺势而生 应用广泛 包含诸多的进步 但未来依然可期 期望6G 7G 8G等等 前提 了解5G 技术 有必要了解一下 1G 2G 2 5G 3G 4G技术 1G 到4G之间的技术我们称之为蜂窝移动网路系统 正所谓长江后浪推前浪 一代更比
  • sql-labs 29 waf 绕过参数污染

    HTTP参数污染 HTTP Parameter Pollution 攻击者通过在HTTP请求中插入特定的参数来发起攻击 如果Web应用中存在这样的漏洞 可以被攻击者利用来进行客户端或者服务器端的攻击 waf服务器 tomcat 只解析重复参
  • 前端自适应布局

    在前端开发中 我们不可避免要面临适配问题 本文将介绍几种适配方式 一 px和em 1 1 px 1 2 em 二 rem 2 1 rem原理 2 2 rem如何计算的 2 3 rem使用 三 使用插件px2rem转换 3 1 原理和优点 3
  • MySQL笔记(五)使用python调用数据库进行操作

    python 访问数据库流程 在pycharm中下载pymysql 打开数据库视图 相当去navicat 设置数据库 使用python对数据库进行操作 Python2中使用的是MySQLdb模块 from pymysql import de
  • 图结构与图算法综述

    图结构与图算法综述 图结构以及图算法 无向图 有向图和网络能运用很多常用的图算法 这些算法包括 各种遍历算法 这些遍历类似于树的遍历 寻找最短路径的算法 寻找网络中最低代价路径的算法 回答一些简单相关问题 例如 图是否是连通的 图中两个顶点
  • Oracle数据库基础知识

    1 Oracle 数据库服务器体系所包含的三种主要结构是 内存结构 进程结构 存储结构 2 安装 11gR2 数据库要经过哪几个主要阶段 Grid基础架构安装 数据库软件安装 DBCA创建数据库 3 数据库实例所必须的后台进程包括 DBWn
  • QT——实战动态链接库调用

    如何在Debug模式下调用外部的动态链接库 首先在工程文件夹下 通过右键可以选择添加后 进入下图界面 选中外部库 点击下一步 选择所要调用的外部库文件debug生成的buliding文件里的libxxxxx a文件 如下图所示 点击下一步
  • 移动端中的坑和 vue中事件修饰符详解(stop, prevent, self, once, capture, passive)

    stop 是阻止冒泡行为 不让当前元素的事件继续往外触发 如阻止点击div内部事件 触发div事件 prevent 是阻止事件本身行为 如阻止超链接的点击跳转 form表单的点击提交 self 是只有是自己触发的自己才会执行 如果接受到内部
  • Eclipse 运行web项目 HTTP404错误

    Eclipse 引入web项目后 run as on server tomcat启动成功 但网页提示404 问题排查 404 服务器找不到资源 首先检查Eclipse部署路径 是否部署了资源文件 查找部署路径 发现该路经下只有一个WEB I
  • QT 计算两个日期时间差?(时间转时间戳)

    时间戳时间转换工具 时间换算工具 1 得出的结果单位是 天 不足一天为0 没有半天的说法 QDateTime time1 QDateTime fromString 2022 4 25 16 40 02 yyyy MM dd HH mm ss
  • CMake中option和cmake_dependent_option的使用

    CMake中的option命令为用户提供可以选择的布尔选项 boolean option 其格式如下 option
  • centos7.4中安装Apache服务

    安装Apache服务 大家好 今天我们在cenots7 4中安装一个web服务Apache 接下来我们先来简单了解一下Apache服务吧 Apache Http server是开源软件项目的杰出代表 基于标准的http网络协议提供网页浏览服
  • 黄鱼车

    本文转载至 http www zynews com news 2010 12 19 content 788498 htm 文 佘建民 有交关外地朋友问我迭个老上海 为啥上海人拿三轮脚踏货车叫作 黄鱼车 对这个疑问 现借 上海闲话 一角 讲讲
  • 【C++】内存管理初阶

    1 C C 内存管理 1 C C 内存分布 int globalVar 1 static int staticGlobalVar 1 void Test static int staticVar 1 int localVar 1 int n
  • 游戏数据库设计经验

    一 游戏模板数据库设计特点 软件行业一般数据库设计原则 保持数据的完整性一致性 避免数据冗余 范式设计 但游戏领域的游戏模板表设计上还需要考虑这些特点 1 1 对游戏程序只读 游戏程序只需要考虑读取性能 不需要过多考虑修改性能 1 2 数据
  • 【代码审计】模板注入

    0x00 介绍 这里主要学习下 FreeMarker 模板注入 FreeMarker 是一款模板引擎 FreeMarker 模板文件与 HTML 一样都是静态页面 当用户访问页面时 FreeMarker 引擎会进行解析并动态替换模板中的内容
  • 学一点Ceph知识:初识Ceph

    Ceph是什么 Ceph是一个开源的分布式存储系统 可大规模扩展 高性能 无单点故障 在普通的服务器上可以支持到PB级容量 商用机器上支持的容量可以达到EB级别 Ceph的竞争力 市面上提供云存储的分布式系统如阿里云的OSS 底层存储框架为
  • 深度学习语义分割(二)SegNet论文解读

    SegNet是是第一次在语义分割中应用编码器 解码器 encoder decoder 的结构 其中 编码器使用池化层逐渐缩减输入数据的空间维度 而解码器通过反卷积层等网络层逐步恢复目标的细节和相应的空间维度 从编码器到解码器之间 通常存在直
  • 学习使用flex

    会对flex做词法分析了解很多 下面我通过一个例子来详细说明如何使用flex 根据所学的词法分析内容 利用flex构造PL 0语言的词法分析器 既然是构造PL 0的词法分析器 那么我们有必要看一下pl0语言的简介和相应文法 2 PL 0语言