编译原理学习(三)

您所在的位置:网站首页 程序编写换行符号是什么 编译原理学习(三)

编译原理学习(三)

2024-07-06 12:02| 来源: 网络整理| 查看: 265

编译原理(三)——Flex实现词法分析器(附Flex使用简介) 词法分析器设计LEX源文件结构定义部分识别规则部分辅助函数部分 LEX文件及Linux环境下编译

词法分析器设计

词法分析器,又叫扫描器,其功能是从左往右逐个字符地对源程序进行扫描,然后按照源程序的构词规则识别出一个个单词符号,把作为字符串的源程序等价的转换为单词符号串的中间程序。单词符号是程序设计语言中的基本语法单元。我们尝试设计一个能扫描C语言子集的词法分析器,我们的单词分为以下5种:

关键字:程序设计语言中定义的具有固定意义的单词,比如C语言中的int,char,while,if,else,for等;标识符 :用来表示名字的字符串,如变量名、函数名、数组名等;常数 :各种类型的常数,在我的词法分析器中只支持整数;运算符 :算术运算符、关系运算符以及逻辑运算符;界符:如“,” “:” “(” “)” “{” “}” “:” 等。

引入一个概念:词法单元。表示单词类,常用整数进行编码,这个编码也叫种别码。 词法单元中每个单词符号也叫词素,每个词素应该具备不同的属性值,这个值是反映单词特征或者特性的值,是编译中其他阶段需要的信息。如果一个词法单元只含有一个词素,那么可以不必设置属性值,如果一个词法单元含有多个词素,那么就应该给每个词素一个属性值用以区分他们。 (刚刚这一段不懂暂时也没关系,如果想要详细了解请翻看龙书第三章)

LEX源文件结构

LEX的输入是用LEX源语言编写的程序,它是扩展名为.l或.lex的文件。LEX源程序经过LEX系统处理后输出一个C程序文件,此文件再经过C编译器的编译就能产生一个可执行程序。 一般而言,一个LEX源程序由“%%”分隔的三部分组成,其整体格式为: 定义部分 %% 识别规则部分 %% 辅助函数部分

定义部分

定义部分对规则部分要引用的文件和变量进行说明,通常可包含头文件、常数定义、全局变量定义、外部变量定义以及正规式定义等。正规式定义用来定义在规则部分引用的正规式,类似于C语言中的宏定义,所以也称宏定义。 除了宏定义外,定义部分的其他代码需要用“%{”和“%}”括起来,LEX会将这部分内容直接复制到C文件生成的lex.yy.c文件中。例:

%{ #define ID 68 //identifyer #define NUMBER 69 //number #define DEFAULT 70//default %} white [\t\n ] digit [0-9] letter [A-Za-z] id ({letter}|_)({letter}|{digit}|_)* number [1-9]{digit}*|0

如果你还没学过正则表达式,这里有一篇文章个人认为总结的比较到位可供参考正则表达式

识别规则部分

这部分用来定义对每一种词法单元的识别动作,也就是词法分析器要执行的操作。例如:

{id} {fprintf(yyout,"ID %d %s\n",ID,yytext);} {number} {fprintf(yyout,"NUMBER %d %s\n",NUMBER,yytext);} 辅助函数部分

在识别规则部分中如果用到的函数不是库函数,则需要在这部分给出函数定义,由用户用C语言编写,它们会由LEX系统直接复制到输出的C程序文件中。 下表列出了LEX中常用的一些变量和函数

函数名定义yyinFILE*类型,指向LEX输入文件,缺省情况下指向标准输入yyoutFILE*类型,指向LEX输出文件,缺省情况下指向标准输出yytextchar*类型,指向识别规则中的一个正规式匹配的单词的首字符yylengint类型,记录与识别规则中正规式匹配的单词的长度yylex()从该函数开始分析,可由lex自动生成yywrap()文件结束处理函数,如果返回值为1就停止解析。可以用来解析多个文件。echo将yytext打印到yyout LEX文件及Linux环境下编译

我的词法分析器只支持了C语言的一个子集,具体有哪些请自行浏览代码。 LEX文件的编译只需要两条命令,加入我们的LEX文件保存在C.lex中,我们只需在终端执行: lex C.lex(执行完后会生成lex.yy.c) gcc lex.yy.c -o MyC_analyze -ll(执行完后可执行文件即为MyC_analyze) 具体代码如下: 这是我根据作业要求修改后的版本,引入符号表存储标识符,并尝试对不同作用域的同名变量进行区分,按理说作用域其实是一个树的结构,但我尝试借助大括号,用某种算法去进行区分。目前的思路是一共三个int变量,其中两个分别记录左右大括号的数量,还有一个用来标记新的作用域产生。具体实现请参照我对id、“{”、“}”识别的动作。这个算法目前并不严谨,但由于测试用例比较简单,目前还没发现漏洞所在,但由于显然我现在这个算法并不能实现对树结构的层次区分,所以日后随着我的编译器的功能的加深可能会暴露出问题,到时候再来修改吧。

%top{ #include #include } %{ #define VOID 1 #define CHAR 2 #define INT 3 #define SIZEOF 4 #define CONST 5 #define RETURN 6 #define CONTINUE 7 #define BREAK 8 #define WHILE 9 #define IF 10 #define ELSE 11 #define SWITCH 12 #define CASE 13 #define FOR 14 #define DO 15 #define SCANF 16 #define PRINTF 17 #define LC 35 //{ #define RC 36 //} #define LB 37 //[ #define RB 38 //] #define LP 39 //( #define RP 40 //) #define LOGRE 41//~ #define INPLUS 42//++ #define INMINUS 43//-- #define LOCRE 44//! #define AND 45//& #define STAR 46//* #define DIVOP 47// / #define COMOP 71// % #define PLUS 48//+ #define MINUS 49//- #define RELG 50//> #define RELGEQ 51//>= #define RELL 52//


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3