词法分析器
词法分析器原理

词法分析器原理词法分析器(Lexical Analyzer)是编译器中的重要组成部分,用于将输入的源代码分解为一个个词法单元(Token),为语法分析器(Syntax Analyzer)提供分析的基础。
本文将介绍词法分析器的原理和工作流程。
一、概述词法分析器通过扫描源代码字符流,并识别出其中的合法词法单元。
它将源代码转化为一个个标识符、关键字、常数、运算符等基本构件,以供后续阶段进行进一步的处理和分析。
二、工作原理1. 自动机词法分析器通常使用有限自动机(Finite Automaton)来实现。
有限自动机由一系列状态组成,每个状态所接受的输入决定了自动机的状态转移。
利用状态转移规则,自动机可以根据输入字符逐步分析源代码并产生相应的词法单元。
2. 正则表达式为了方便描述词法分析器对输入的词法单元进行匹配,可以使用正则表达式。
正则表达式是一种描述字符模式的工具,它可以定义一类字符串的集合。
词法分析器将正则表达式与状态机相结合,通过模式匹配的方式识别输入字符流中的词法单元。
3. 词法规则词法分析器通过预先定义的词法规则来描述源代码中的不同词法单元。
例如,某个编程语言的词法规则可能包含关键字、标识符、数字、字符串等。
词法规则的定义中常常使用正则表达式来指定某个词法单元的模式。
4. 符号表为了方便后续的语义处理和编译过程,词法分析器通常会维护一个符号表(Symbol Table)。
符号表记录了源代码中出现的标识符、常量等信息,以供后续的语法分析和语义分析使用。
三、工作流程词法分析器的工作流程可以分为以下几个步骤:1. 读取源代码字符流,并初始化状态机。
2. 通过状态转移规则,逐个输入字符进行状态转移,直到达到某个终止状态。
3. 判断当前状态是否为某个词法单元的终止状态,如果是,产生相应的词法单元,并将其记录在符号表中。
4. 继续读取源代码字符流,重复以上过程,直到扫描完整个源代码。
五、总结词法分析器作为编译器的重要组成部分,负责将源代码分解为一个个词法单元,并提供给语法分析器进行进一步的处理。
编译原理课程设计—词法分析器

编译原理课程设计(一)——词法分析器1、题目编写程序实现一个简易的词法分析器。
2、实验目的对一段程序代码进行词法分析,将程序段中的关键字、标识符、常数、运算符、界符按照一定的种别编码分析出来。
3、环境及工具操作系统:windows XP ;使用工具:Microsoft Visual C++ 6.0; 编程语言:C 语言;4、分析程序输入:从文件中读入程序段;程序输出:由单词种别和单词符号的属性值组成的二元式;单词种别通常使用整数编码,编码方式可以有多种,在设计词法分析器之前应确定一种程序处理起来较方便的编码方式。
当一个种别中含有多个单词符号时,在分析出其属于哪个种别的时候应同时给出其单词符号属性,本程序为方便起见,采用单词符号本身来作为其属性,以标识同种别种的不同单词符号。
标识符及关键字的识别:字母开头的字母和数字组成的串是多数编程语言的标识符,所以我们的简易词法分析器中,将标识符定义为这种字母数字串。
当第一个字母为字母且紧接着的字符为数字或字母时,应将其串接在一起为一个单词,直到紧跟着的不在是字母数字时。
由于关键字通常为一个单词,则这样得到的串可能是标识符也可能是关键字,又因为一种语言的关键字通常是有限个,则我们可以构造一个存放所有关键字的表,查询关键字表,可以判断得到的串是否为关键字。
界符和运算符的识别:它们多为当个字符,建立两个分别存放界符合运算符的表,读取字符后,进行查表便可以得出它们的类型。
为方便词法分析器的设计,可以使用状态转换图,根据一种特定的编程语言先设计出其状态转换图才能更好将其用代码实现。
典型状态转换图结构如下:(a)有不含回路含分支的状态节点:对应if …else if …else …语句;(b)有含回路的状态节点:对应while …if …语句。
(b )5、状态转换图6、程序框架描述程序中编写了以下函数,各个函数实现的作用如下:1. GetChar():将下一输入的字符读入到全局变量ch中,搜素指示器前移一个字符的位置。
词法分析器功能

➢词法记号及属性➢词法记号的描述与识别➢有限自动机➢DFA构建➢DFA化简➢词法模式的识别过程➢子集构造法词法分析器语法分析器语义分析器源程序中间代码生成器代码优化器代码生成器出错管理器符号表管理器源代码词法分析器记号(token)流词法分析器语法分析器符号表记号取下一个记号源程序本章内容➢词法分析器:把构成源程序的字符流翻译成记号流,还完成和用户接口的一些任务。
➢介绍正规式、状态转换图和有限自动机概念。
➢Lex 与词法分析器的自动生成。
词法记号词法模式词法单元词法记号的属性词法错误➢看一个中文的句子你们是优秀的大工学子代词动词形容词名词(短语)通过分词操作,把句子以单词或者词组为单位进行划分,得到一个句型。
L1:x =ID COLON ID ASSGNy2+12;ID PLUS INT SEMI-COL 编译的词法分析做的工作类似于分词,把原始的字符串流形式的程序文本转换为词法记号流的形式。
例子中哪些是词法单元?➢词法单元又称单词,是编程语言中合法的字符串➢词法记号满足某种规则的词法单元,采用同一种记法。
➢满足一个给定规则的词法单元,被记为一个词法记号。
模式词法单元词法记号➢C语言的标识符?➢x2, 12, _12, _abc哪些是合法的C标识符?C语言标识符的规则(模式):首字符必须是_或者字母,由_、字母或数字组成的字符串词法记号词法单元例举模式的非形式描述STRUCT struct struct FOR for for RELOP <,<=,=,IDsum,_12,_x NUM 3.1,10,2.8e12LITERAL “seg.error”➢常见记号及模式的例子:简单的一对一模式相对复杂一点的模式引号“和”之间的任意字符串,但引号本身除外_或字母开头的由_、字母和数字组成的串<或<=或=或…任何数值常数词法记号词法单元例举模式的非形式描述relation < , < = , = , …< 或<= 或= 或…idsum, count, D5 由字母开头的字母数字串名词大连软件大黑山表示名称的词连词和与或和与或….词法记号词法单元例举模式的非形式化描述词法记号词法单元例举模式的非形式描述中国人胡锦涛毛泽东具有中国国籍的人美国人奥巴马克林顿具有美国国籍的人存在的意义?词法分析器语法分析器符号表记号取下一个记号源程序➢如果简单地把词法记号流传给语法分析器,会产生什么后果?-语义被完全摒弃,只剩下一个语法结构。
词法分析器

词法分析器词法分析器是一种重要的编译器技术,用于将输入的源代码按照词法规则进行分割、识别和标记。
它是编译过程中的第一个阶段,也是编译器的基础组成部分之一。
在本文中,我将详细介绍词法分析器的基本原理、应用场景和实现方法。
首先,让我们来了解一下词法分析器的基本原理。
词法分析器主要通过扫描源代码中的字符流,按照一定的词法规则将其分割成一个个的词法单元(token)。
词法单元是语言中的最小语法单位,可以是关键字、标识符、运算符、常量等。
词法分析器通常使用有限状态自动机(DFA)来实现,通过状态转换和模式匹配的方式来识别每个词法单元的类型和属性。
词法分析器在编译器中的作用非常重要。
它能够为后续的语法分析器提供正确的输入,为编译器的错误检查和优化提供基础。
通过词法分析器,我们可以将源代码转换为一系列的词法单元序列,为语法分析器和语义分析器提供正确的输入。
同时,词法分析器还可以检测源代码中的词法错误,并通过错误处理机制进行相应的处理和提示。
词法分析器的应用场景非常广泛。
除了编译器中的使用外,它还可以应用于其他领域,如代码编辑器、语法高亮、代码自动补全等。
在代码编辑器中,词法分析器可以实时地对用户输入的源代码进行分析和高亮显示,提供友好的编辑环境。
在代码自动补全场景中,词法分析器可以根据当前的上下文信息,自动提示用户可能的词法单元选项,提高编码效率和准确性。
实现一个词法分析器的方法有很多种,常用的包括手写词法分析器和使用词法分析器生成器。
手写词法分析器是通过编写代码的方式来实现,根据词法规则和状态转换表逐个字符地进行识别和匹配。
这种方式的优点是灵活性高,可以随时根据需求进行修改和调整。
然而,手写词法分析器的实现相对复杂,对开发者的要求较高。
而使用词法分析器生成器,如Lex和Flex,可以根据给定的词法规则自动生成词法分析器的代码。
这种方式的优点是简化代码编写过程,提高开发效率。
综上所述,词法分析器作为编译器中的重要组成部分,具有非常重要的作用。
词法分析器实验报告

词法分析器实验报告词法分析器实验报告一、引言词法分析器是编译器中的重要组成部分,它负责将源代码分解成一个个的词法单元,为之后的语法分析提供基础。
本实验旨在设计和实现一个简单的词法分析器,以深入理解其工作原理和实现过程。
二、实验目标本实验的目标是设计和实现一个能够对C语言代码进行词法分析的程序。
该程序能够将源代码分解成关键字、标识符、常量、运算符等各种词法单元,并输出其对应的词法类别。
三、实验方法1. 设计词法规则:根据C语言的词法规则,设计相应的正则表达式来描述各种词法单元的模式。
2. 实现词法分析器:利用编程语言(如Python)实现词法分析器,将源代码作为输入,根据词法规则将其分解成各种词法单元,并输出其类别。
3. 测试和调试:编写测试用例,对词法分析器进行测试和调试,确保其能够正确地识别和输出各种词法单元。
四、实验过程1. 设计词法规则:根据C语言的词法规则,我们需要设计正则表达式来描述各种词法单元的模式。
例如,关键字可以使用'|'操作符将所有关键字列举出来,标识符可以使用[a-zA-Z_][a-zA-Z0-9_]*的模式来匹配,常量可以使用[0-9]+的模式来匹配等等。
2. 实现词法分析器:我们选择使用Python来实现词法分析器。
首先,我们需要读取源代码文件,并将其按行分解。
然后,针对每一行的代码,我们使用正则表达式进行匹配,以识别各种词法单元。
最后,我们将识别出的词法单元输出到一个结果文件中。
3. 测试和调试:我们编写了一系列的测试用例,包括各种不同的C语言代码片段,以测试词法分析器的正确性和鲁棒性。
通过逐个测试用例的运行结果,我们可以发现和解决词法分析器中的问题,并进行相应的调试。
五、实验结果经过多次测试和调试,我们的词法分析器能够正确地将C语言代码分解成各种词法单元,并输出其对应的类别。
例如,对于输入的代码片段:```cint main() {int a = 10;printf("Hello, world!\n");return 0;}```我们的词法分析器将输出以下结果:```关键字:int标识符:main运算符:(运算符:)运算符:{关键字:int标识符:a运算符:=常量:10运算符:;标识符:printf运算符:(常量:"Hello, world!\n"运算符:)运算符:;关键字:return常量:0运算符:;```可以看到,词法分析器能够正确地将代码分解成各种词法单元,并输出其对应的类别。
实验一、词法分析器(含源代码)

词法分析器实验报告一、实验目的及要求本次实验通过用C语言设计、编制、调试一个词法分析子程序,识别单词,实现一个C语言词法分析器,经过此过程可以加深对编译器解析单词流的过程的了解。
运行环境:硬件:windows xp软件:visual c++6.0二、实验步骤1.查询资料,了解词法分析器的工作过程与原理。
2.分析题目,整理出基本设计思路。
3.实践编码,将设计思想转换用c语言编码实现,编译运行。
4.测试功能,多次设置包含不同字符,关键字的待解析文件,仔细察看运行结果,检测该分析器的分析结果是否正确。
通过最终的测试发现问题,逐渐完善代码中设置的分析对象与关键字表,拓宽分析范围提高分析能力。
三、实验内容本实验中将c语言单词符号分成了四类:关键字key(特别的将main说明为主函数)、普通标示符、常数和界符。
将关键字初始化在一个字符型指针数组*key[]中,将界符分别由程序中的case列出。
在词法分析过程中,关键字表和case列出的界符的内容是固定不变的(由程序中的初始化确定),因此,从源文件字符串中识别出现的关键字,界符只能从其中选取。
标识符、常数是在分析过程中不断形成的。
对于一个具体源程序而言,在扫描字符串时识别出一个单词,若这个单词的类型是关键字、普通标示符、常数或界符中之一,那么就将此单词以文字说明的形式输出.每次调用词法分析程序,它均能自动继续扫描下去,形成下一个单词,直到整个源程序全部扫描完毕,从而形成相应的单词串。
输出形式例如:void $关键字流程图、程序流程图:程序:#include<string.h>#include<stdio.h>#include<stdlib.h>#include<ctype.h>//定义关键字char*Key[10]={"main","void","int","char","printf","scanf","else","if","return"}; char Word[20],ch; // 存储识别出的单词流int IsAlpha(char c) { //判断是否为字母if(((c<='z')&&(c>='a'))||((c<='Z')&&(c>='A'))) return 1;else return 0;}int IsNum(char c){ //判断是否为数字if(c>='0'&&c<='9') return 1;else return 0;}int IsKey(char *Word){ //识别关键字函数int m,i;for(i=0;i<9;i++){if((m=strcmp(Word,Key[i]))==0){if(i==0)return 2;return 1;}}return 0;}void scanner(FILE *fp){ //扫描函数char Word[20]={'\0'};char ch;int i,c;ch=fgetc(fp); //获取字符,指针fp并自动指向下一个字符if(IsAlpha(ch)){ //判断该字符是否是字母Word[0]=ch;ch=fgetc(fp);i=1;while(IsNum(ch)||IsAlpha(ch)){ //判断该字符是否是字母或数字Word[i]=ch;i++;ch=fgetc(fp);}Word[i]='\0'; //'\0' 代表字符结束(空格)fseek(fp,-1,1); //回退一个字符c=IsKey(Word); //判断是否是关键字if(c==0) printf("%s\t$普通标识符\n\n",Word);//不是关键字else if(c==2) printf("%s\t$主函数\n\n",Word);else printf("%s\t$关键字\n\n",Word); //输出关键字 }else //开始判断的字符不是字母if(IsNum(ch)){ //判断是否是数字Word[0]=ch;ch=fgetc(fp);i=1;while(IsNum(ch)){Word[i]=ch;i++;ch=fgetc(fp);}Word[i]='\0';fseek(fp,-1,1); //回退printf("%s\t$无符号实数\n\n",Word);}else //开始判断的字符不是字母也不是数字{Word[0]=ch;switch(ch){case'[':case']':case'(':case')':case'{':case'}':case',':case'"':case';':printf("%s\t$界符\n\n",Word); break;case'+':ch=fgetc(fp);Word[1]=ch;if(ch=='='){printf("%s\t$运算符\n\n",Word);//运算符“+=”}else if(ch=='+'){printf("%s\t$运算符\n\n",Word); //判断结果为“++”}else {fseek(fp,-1,1);printf("%s\t$运算符\n\n",Word); //判断结果为“+”}break;case'-':ch=fgetc(fp);Word[1]=ch;if(ch=='='){printf("%s\t$运算符\n\n",Word); }else if(ch=='-'){printf("%s\t$运算符\n\n",Word); //判断结果为“--”}else {fseek(fp,-1,1);printf("%s\t$运算符\n\n",Word); //判断结果为“-”}break;case'*':case'/':case'!':case'=':ch=fgetc(fp);if(ch=='='){printf("%s\t$运算符\n\n",Word);}else {fseek(fp,-1,1);printf("%s\t$运算符\n\n",Word);}break;case'<':ch=fgetc(fp);Word[1]=ch;if(ch=='='){printf("%s\t$运算符\n\n",Word); //判断结果为运算符“<=”}else if(ch=='<'){printf("%s\t$运算符\n\n",Word); //判断结果为“<<”}else {fseek(fp,-1,1);printf("%s\t$运算符\n\n",Word); //判断结果为“<”}break;case'>':ch=fgetc(fp);Word[1]=ch;if(ch=='=') printf("%s\t$运算符\n\n",Word);else {fseek(fp,-1,1);printf("%s\t$运算符\n\n",Word);}break;case'%':ch=fgetc(fp);Word[1]=ch;if(ch=='='){printf("%s\t$运算符\n\n",Word);}if(IsAlpha(ch)) printf("%s\t$类型标识符\n\n",Word);else {fseek(fp,-1,1);printf("%s\t$取余运算符\n\n",Word);}break;default:printf("无法识别字符!\n\n"); break;}}}main(){char in_fn[30]; //文件路径FILE *fp;printf("\n请输入源文件名(包括路径和后缀名):");while(1){gets(in_fn);//scanf("%s",in_fn);if((fp=fopen(in_fn,"r"))!=NULL) break; //读取文件内容,并返回文件指针,该指针指向文件的第一个字符else printf("文件路径错误!请重新输入:");}printf("\n******************* 词法分析结果如下 *******************\n");do{ch=fgetc(fp);if(ch=='#') break; //文件以#结尾,作为扫描结束条件else if(ch==' '||ch=='\t'||ch=='\n'){} //忽略空格,空白,和换行else{fseek(fp,-1,1); //回退一个字节开始识别单词流scanner(fp);}}while(ch!='#');return(0);}4.实验结果解析源文件:void main(){int a=3;a+=b;printf("%d",a);return;}#解析结果:5.实验总结分析通过本次实验,让再次浏览了有关c语言的一些基本知识,特别是对文件,字符串进行基本操作的方法。
词法分析器的实验报告
词法分析器的实验报告词法分析器的实验报告引言:词法分析器是编译原理中的重要组成部分,它负责将源代码中的字符序列转换为有意义的词法单元,为后续的语法分析提供基础。
本实验旨在设计和实现一个简单的词法分析器,并对其进行测试和评估。
实验设计:1. 词法规则设计:在开始实验之前,我们首先需要设计词法规则,即定义源代码中的合法词法单元。
例如,对于一门类C的语言,我们可以定义关键字(如if、while、int等)、标识符、运算符(如+、-、*等)、分隔符(如()、{}等)等。
2. 有限自动机(DFA)的设计:基于词法规则,我们可以设计一个有限自动机,用于识别和分析源代码中的词法单元。
有限自动机是一个状态转换图,其中每个状态代表一种词法单元,而边表示输入字符的转换关系。
3. 实现代码:根据有限自动机的设计,我们可以使用编程语言(如Python、C++等)实现词法分析器的代码。
代码的主要功能包括读取源代码文件、逐个字符进行词法分析、识别和输出词法单元。
实验过程:1. 词法规则设计:我们以一门简单的算术表达式语言为例,设计了以下词法规则:- 数字:由0-9组成的整数或浮点数。
- 运算符:包括+、-、*、/等。
- 分隔符:包括括号()和逗号,。
- 标识符:以字母开头,由字母和数字组成的字符串。
2. 有限自动机(DFA)的设计:我们基于词法规则,设计了一个简单的有限自动机。
该自动机包含以下状态:- 初始状态:用于读取和识别源代码中的字符。
- 数字状态:用于识别和输出数字。
- 运算符状态:用于识别和输出运算符。
- 分隔符状态:用于识别和输出分隔符。
- 标识符状态:用于识别和输出标识符。
3. 实现代码:我们使用Python编程语言实现了词法分析器的代码。
代码主要包括以下功能:- 读取源代码文件。
- 逐个字符进行词法分析,根据有限自动机的设计进行状态转换。
- 识别和输出词法单元。
实验结果:我们对几个测试样例进行了词法分析,并对结果进行了评估。
词法分析器
词法分析器词法分析器又称扫描器。
词法分析是指将我们编写的文本代码流解析为一个一个的记号,分析得到的记号以供后续语法分析使用。
词法分析器的工作是低级别的分析:将字符或者字符序列转化成记号.。
在谈论词法分析时,使用术语“词法记号”(简称记号)、“模式”和“词法单元”表示特定的含义。
在分析时,一是把词法分析器当成语法分析的一部分,另一种是把词法分析器当成编译程序的独立部分。
在前一种情况下,词法分析器不断地被语法分析器调用,每调用一次词法分析器将从源程序的字符序列拼出一个单词,并将其Token值返回给语法分析器。
后一种情况则不同,词法分析器不是被语法分析器不断地调用,而是一次扫描全部单词完成编译器的独立一遍任务。
词法分析器主要特点是不依靠语法,而只依靠词法,即处理一个单词时不依赖于外部单词的信息,因此词法分析器一般都很简单。
当然,对某些语言在作词法分析时,在有些情况下不得不往前查看多个字符,有时还要做一些特殊处理,还有一些在词法分析中处理不了的,要留到语法分析中进行处理。
本算法主要利用状态转换图生成一个词法分析器,对输入的程序进行词法分析,并将分析得到的单词造表。
其中关键字表和界限符表的大小是由高级语言的子集决定的,可以用数组装载;而标识符表和常数表的大小取决于输入的待分析程序中的变量、过程名、常数的个数,所以要用动态数组(即vector)来装载。
当然为了方便,我们也把它定义成数组处理。
语法分析时,调用词法分析器,根据已知文法利用递归向下分析,检查语法错误。
源代码如下// LexicalAnalyzer.cpp : 定义控制台应用程序的入口点。
#include"stdafx.h"#include"iostream"#include"fstream"#include"string"#include"vector"using namespace std;struct Node{string state;string word;};class LA{private:vector<Node> list;string keyword[32];public:void BinarySearch(void);bool BinaryFind(string str);bool BinaryFindchar(char ch);char get_word(ifstream &infile,char str);char get_constant(ifstream &infile,char str);char get_operatorcharacter(ifstream &infile,char str);char get_list(ifstream &infile,char str);void printall();};void LA::BinarySearch(void){ifstream infile("keyword.txt",ios::in);if(! infile){cerr<<"open error!"<<endl;exit(1);}for(int i=0;i<32;i++){infile>>keyword[i];}infile.close();}bool LA::BinaryFind(string str){int low=0,high=31;while(low<=high){int mid=(low+high)/2;if(pare(keyword[mid])==0)return true;elseif(pare(keyword[mid])>0)low=mid+1;else high=mid-1;}return false;}bool LA::BinaryFindchar(char ch){intlc[20]={33,37,38,40,41,42,43,45,46,47,58,60,61,62,63,91,93,94,124,126};int low=0,high=19;while(low<=high){int mid=(low+high)/2;if(lc[mid]==ch)return true;elseif(ch>lc[mid])low=mid+1;else high=mid-1;}return false;}char LA::get_word(ifstream &infile,char str){BinarySearch();string ss;list.resize(list.size()+1);list[list.size()-1].word="";list[list.size()-1].word+=ss.assign(1,str);infile.get(str);while(str==95 || (str>=48 && str<=57)||(str >= 65 && str <= 90) || (str >=97 && str<= 122)){list[list.size()-1].word+=ss.assign(1,str);infile.get(str);}if (BinaryFind(list[list.size()-1].word))list[list.size()-1].state="保留字";elselist[list.size()-1].state="标示符";return str;}void LA::printall(){int i=0;ofstream outfile("result.txt",ios::out);if(! outfile.is_open()){cerr<<"open error!"<<endl;exit(1);}while (i < list.size()){outfile<<list[i].state<<": "<<list[i].word<<endl;i++;}outfile.flush();outfile.close();}char LA::get_constant(ifstream &infile,char str){string ss;list.resize(list.size()+1);list[list.size()-1].word="";list[list.size()-1].word+=ss.assign(1,str);if (str>=48 && str<=57){infile.get(str);while(str==46 || (str>=48 && str<=57)){list[list.size()-1].word+=ss.assign(1,str);infile.get(str);}}elseif (str==34){infile.get(str);while (str != 34){list[list.size()-1].word+=ss.assign(1,str);infile.get(str);}list[list.size()-1].word+=ss.assign(1,str);infile.get(str);}else{infile.get(str);while (str != 39){list[list.size()-1].word+=ss.assign(1,str);infile.get(str);}list[list.size()-1].word+=ss.assign(1,str);infile.get(str);}list[list.size()-1].state="常量";return str;}char LA::get_operatorcharacter(ifstream &infile,char str) {string ss;list.resize(list.size()+1);list[list.size()-1].word="";list[list.size()-1].word+=ss.assign(1,str);list[list.size()-1].state="运算符";char temp=str;infile.get(str);if(temp=='/'){if(str=='/'){while(str !='\n'){list[list.size()-1].word+=ss.assign(1,str);infile. get(str);}list[list.size()-1].word+=ss.assign(1,str);infile.get(str);list[list.size()-1].state="注释段";}if(str =='*'){char ch;list[list.size()-1].word+=ss.assign(1,str);infile.get(str);list[list.size()-1].word+=ss.assign(1,str);while((ch=infile.get())!='/' || str!='*'){str=ch;list[list.size()-1].word+=ss.assign(1,str);}list[list.size()-1].word+=ss.assign(1,ch);infile.get(str);list[list.size()-1].state="注释段";}}if (temp==45 && str==62){list[list.size()-1].word+=ss.assign(1,str);infile.get(str);list[list.size()-1].state="运算符";}if((temp==38 || temp==43 || temp==45 || temp==60 || temp==61 || temp==62 || temp==124)&&(temp==str || str==61)){list[list.size()-1].word+=ss.assign(1,str);char temp1=str;infile.get(str);if (temp==temp1 && str==61){list[list.size()-1].word+=ss.assign(1,str);infile.get(str);}list[list.size()-1].state="运算符";}if((temp==33 || temp==37 || temp==42 || temp==47 || temp==94) && str==61) {list[list.size()-1].word+=ss.assign(1,str);infile.get(str);list[list.size()-1].state="运算符";}if (temp==58 && str==58){list[list.size()-1].word+=ss.assign(1,str);infile.get(str);list[list.size()-1].state="运算符";}return str;}char LA::get_list(ifstream &infile,char str){string ss;list.resize(list.size()+1);list[list.size()-1].word="";list[list.size()-1].word+=ss.assign(1,str);infile.get(str);list[list.size()-1].state="界限符";return str;}int main(){LA la;char filename[30];char str;cout<<"请输入原文件名:";cin>>filename;ifstream infile(filename,ios::in);if(! infile.is_open()){cerr<<"open error!"<<endl;exit(1);}infile.get(str);while (! infile.eof()){if(str==95 || (str >= 65 && str <= 90) || (str >=97 && str<= 122)) str=la.get_word(infile,str);elseif((str>=48 && str<=57)||str==34 || str==39)str=la.get_constant(infile,str);elseswitch (str){case 35: {str=la.get_list(infile,str); break;}case 36: {str=la.get_list(infile,str); break;}case 44: {str=la.get_list(infile,str); break;}case 59: {str=la.get_list(infile,str); break;}case 64: {str=la.get_list(infile,str); break;}case 92: {str=la.get_list(infile,str); break;}case 96: {str=la.get_list(infile,str); break;}case 123: {str=la.get_list(infile,str); break;}case 125: {str=la.get_list(infile,str); break;}default: {if(la.BinaryFindchar(str))str=la.get_operatorcharacter(infile,str);elseinfile.get(str);break;}}}infile.close();la.printall();cout<<endl<<"词法分析结束,结果保存在当前目录的result.txt文件里!"<<endl;return 0;}。
词法分析器的设计与实现
词法分析器的设计与实现
1.定义词法规则:根据编程语言的语法规范,定义不同的词法规则,
如关键字、标识符、操作符、常量等。
每个词法规则由一个正则表达式或
有限自动机来描述。
2.构建有限自动机:根据词法规则,构建一个有限自动机(DFA)来
识别词法单元。
有限自动机是一种形式化模型,用于在输入字符序列上进
行状态转换。
3.实现状态转换函数:根据有限自动机的定义,实现状态转换函数。
状态转换函数接受一个输入字符,并返回当前状态和输出的词法单元。
4.实现输入缓冲区:为了方便词法分析器的实现,通常需要实现一个
输入缓冲区,用于存储源代码,并提供一些读取字符的函数。
5. 实现词法分析器:将前面实现的状态转换函数和输入缓冲区结合
起来,实现一个完整的词法分析器。
词法分析器可以使用迭代器模式,每
次调用next(函数来获取下一个词法单元。
6.处理错误情况:在词法分析过程中,可能会遇到一些错误情况,如
未定义的词法单元、不符合语法规范的词法单元等。
词法分析器需要能够
检测并处理这些错误情况。
7.构建测试用例:为了验证词法分析器的正确性,需要构建测试用例,包括各种不同的源代码片段,并验证分析结果是否符合预期。
8.进行性能优化:词法分析是编译器中的一个耗时操作,因此可以进
行一些性能优化,如使用缓存机制、减少状态转换次数等。
以上是词法分析器的设计与实现的一般步骤,具体实现过程可能因编程语言和编译器的不同而有所差异。
词法分析器文档
}
else if(chf==',')
{printf("48,%c\n",chf);
fprintf(fpout,"%d\t%s\n",48,",");
}
else if(chf==':')
{printf("49,%c\n",chf);
fprintf(fpout,"%d\t%s\n",49,":");
}
}
}
scan(FILE *fp)
{ char chf;
while((chf=fgetc(fp))!=EOF&&chf!='#') /*文件未结束就执行循环判断输入的字符串*/
{
if(isalpha(chf)) /*标示符和关键字的判断*/
{ int p=0;
char str1[20];
do
{str1[p++]=chf;
}
else if(chf=='=')
{chf=getc(fp);
if(chf=='=')
{printf("56,%c\n",chf);
fprintf(fpout,"%d\t%s\n",56,"==");
}
else{printf("47,%c\n",chf);
fprintf(fpout,"%d\t%s\n",47,"=");
}
str2[--j]='\0';
fseek(fp,-2L,1);
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
词法分析器报告
一、任务与目的
·任务:
1、使用C/C++程序设计语言和递归下降子程序的方法编写该函数绘图语言的词法分析器。
并要求设计一个词法分析器的测试小程序来调用自己编写的词法分析器测试各种不同的输入。
2、词法分析的任务是对输入的字符串形式的源程序按顺序进行扫描,在扫描的同时,根据源语言的词法规则识别具有独立意义的单词(符号),并产生与其等价的属性字流(内部编码)作为输出。
通常属性字流即是对识别的单词给出的标记符号的集合。
·目的:
通过自己动手编写词法分析器,掌握记号、模式与单词,掌握正规式与正规集,掌握有限自动机,掌握如何从正规式到词法分析器的各种算法。
理解如何理论联系实际以及明白理论与实际的差别。
二、分析与设计
词法分析器的本质:基本任务是进行模式匹配,其关键在于分析过程中的模式说明和模式识别方法,在编译分析中即正规表达式和有限自动机。
构造词法分析器方法:1、手工构造;2、利用自动生成工具LEX。
但是无论用那种方法,其内在工作原理都是相同的,都要经过正规式到最小状态DFA的转换。
词法分析器可有两种:一种是把词法分析器作为语法分析的一个子程序,一种是把词法分析器作为编
译程序的独立一遍.在前一种情况下,词法分析器不断地被语法分析器调用,每调用一次词法分析器将从源程序的字符序列拼出一个单词,并将其Token值返回给语法分析器.后一种情况则不同,词法分析器不是被语法分析器不断地调用,而是一次扫描全部单词完成编译器的独立一遍任务。
词法分析程序一般具有如下功能:读入字符串形式的源程序;识别出具有独立意义的最小语法单位:单词。
事实上,由正规表达式到最小化DFA的转换源程序中的测试生成串部分就是对所输入的单词进行判断,看其是否能被生成的DFA接受(也就是这个单词是否符合正规式定义的要求)。
这本质上就是一个简单的词法分析。
定义某种语言的单词,并给出编号。
该语言单词包括:保留字、运算符、标识符、常量、格式符等。
根据给定的语言子集构造词法分析器。
输出为中间文件。
在设计时为了便于理解,不使用内部编码而用枚举对同类型的单词进行标识。
例如所有的常量统一用“CONST_ID”对其进行标识,当扫描时遇到常量就输出该常量的值和“CONST_ID”标识。
这里给出词法分析程序大概的设计方法:
1、根据要求写出词法分析的正规文法G;
2、根据正规文法G,写出正则式RE;
3、根据正则式RE,画出NFA;
4、将NFA转化为DFA;
5、将DFA转化为mininum state DFA;
6、mininum state DFA就是词法分析程序的流程图,根据此流程图编写相应的词法分析程序。
以下是较为详细的设计:
①总体结构与模块划分
测试模块(scannermain.cpp)
词法分析器模块(scanner.h & scanner.cpp)
②重要数据结构
·枚举记号种类
·记号与符号表结构
·符号表
static Token TokenTab[] = {
{CONST_ID, "PI", 3.1415926, 0 },
{CONST_ID, "E", 2.71828, 0 },
{T, "T", 0.0, 0 },
{FUNC, "SIN", 0.0, sin },
{FUNC, "COS", 0.0, cos },
{FUNC, "TAN", 0.0, tan },
{FUNC, "LN", 0.0, log },
{FUNC, "EXP", 0.0, exp },
{FUNC, "SQRT", 0.0, sqrt},
{ORIGIN, "ORIGIN", 0.0, 0 },
{SCALE, "SCALE", 0.0, 0 },
{ROT, "ROT", 0.0, 0 },
{IS, "IS", 0.0, 0 },
{FOR, "FOR", 0.0, 0 },
{FROM, "FROM", 0.0, 0 },
{TO, "TO", 0.0, 0 },
{STEP, "STEP", 0.0, 0 },
{DRAW, "DRAW", 0.0, 0 }
};
③关键思想与算法
·构造NFA的Thompson算法
·模拟NFA的“并行”算法
·从NFA构造DFA:构造DFA的子集法,smove(S, a)函数和 _闭包(T)的计算
·DFA的最小化:利用可区分的概念,将所有不可区分的状态看作是一个状态
三、测试例程设计·测试程序(scannermain.cpp)
·测试数据(test.txt)
四、测试结果及分析
·结果分析
该词法分析器的输出为一堆记号流,这些记号流正确的反映出了绘图语言源程序中的各个单词的类型。
例如:“FOR”被识别为“关键字”类别;“SUYANG”被识别为错误的TOKEN等等。
并且对注释性语句也正确的识别了。
在测试过程中需要说明三点出现的问题及错误:
1、“**”POWER的正确识别
2、和开发环境有关的错误
这个问题只针对我的机器,在别的机器上不一定会出现该问题,我想是由于硬件平台的问题(可能和我的CPU是双核有关)。
3、在VS2005中生成工程时会产生两个警告(C4996, C4313)
1>------ 已启动全部重新生成: 项目: 词法分析器, 配置: Debug Win32 ------
1>正在编译...
1>scanner.cpp
1>d:"函数绘图语言编译器构造"词法分析器"scanner.cpp(15) : warning C4996: “fopen”被声明为否决的1>d:"program files"microsoft visual studio 8"vc"include"stdio.h(234) : 参见“fopen”的声明
1>消息:“This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE. See online help for details.”
1>scannermain.cpp
1>d:"函数绘图语言编译器构造"词法分析器"scannermain.cpp(19) : warning C4313: “printf”: 格式字符串中的“%x”与参数4 (属于“MathFuncPtr”类型)冲突
1>正在生成代码...
1>正在编译资源清单...
1>正在链接...
1>LINK : 没有找到D:"函数绘图语言编译器构造"Debug"词法分析器.exe 或上一个增量链接没有生成它;正在执行完全链接
1>正在嵌入清单...
1>生成日志保存在“file://d:"函数绘图语言编译器构造"词法分析器"Debug"BuildLog.htm”
1>词法分析器- 0 个错误,2个警告
这个问题比较容易解决,只需简单的屏蔽掉这两个警告即可。
在scanner.h文件中加上以下两条语句:
五、总结与体会
主要学习和体会了基于编译器构造技术中的由正规表达式到最小化DFA的算法设计和实现技术;主要包括由正规表达式构造NFA所用到的Thompson构造法、把NFA转化为与其等价的DFA所使用的子集构造算法以及把DFA最小化的算法,最后实现词法分析。
Thompson构造法根据读入的正规表达式的不同字符进入相应的转换处理。
NFA转化为与其等价的DFA需分两步进行:a、构造NFA N的状态K的子集的算法;b、计算 -closure。
完成这些子模块的设计后,再通过某一中间模块的总控程序对其调用,最后再由主程序合并调用。
在算法实现过程中,主要使用visual C++进行编程。
正规式与自动机理论在词法构造乃至整个编译器构造过程中起着至关重要的作用,同时它们被广泛应用于计算机科学的各个领域,它们与计算机其它学科之间也有着很大的联系。