hust编译原理实验报告2.docx

上传人:b****2 文档编号:2009044 上传时间:2022-10-26 格式:DOCX 页数:33 大小:454.56KB
下载 相关 举报
hust编译原理实验报告2.docx_第1页
第1页 / 共33页
hust编译原理实验报告2.docx_第2页
第2页 / 共33页
hust编译原理实验报告2.docx_第3页
第3页 / 共33页
hust编译原理实验报告2.docx_第4页
第4页 / 共33页
hust编译原理实验报告2.docx_第5页
第5页 / 共33页
点击查看更多>>
下载资源
资源描述

hust编译原理实验报告2.docx

《hust编译原理实验报告2.docx》由会员分享,可在线阅读,更多相关《hust编译原理实验报告2.docx(33页珍藏版)》请在冰豆网上搜索。

hust编译原理实验报告2.docx

hust编译原理实验报告2

 

《编译原理》实验报告

 

院系计算机科学与技术

专业计算机科学与技术

班级CS1101

学号

姓名

指导教师周时阳

2014年7月2日

实验一

一、问题描述

选择计算机高级程序语言之一——C语言,运用恰当的词法分析技术线路,设计和实现其对应的词法分析器。

对C语言的词法按照正则文法或正则式→NFA→DFA→minDFA→程序设计;能够跳过程序中的注释和空白、换行符等没有意义的部分;遇到无法确认的符合能够报告错误,然后跳过这个错误,继续分解剩下的部分;分析后的结果存放到磁盘文件中。

二、数据结构及算法设计

1、C语言简单词法

(1)关键字:

mainintcharlongifelseforwhilevoid

所有的关键字都是小写。

在设计测试程序时所涉及的关键字应该在以上范围内才能被识别,否则将作为标识符处理。

(2)运算符和界符:

:

=+-*/<<=>>==;()++!

!

=

上面所包含的运算符与界符不是很全面,因此无法识别一些复杂的运算符,例如&&、||、%等。

(3)其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义:

ID=letter(letter|digit)*

NUM=digitdigit*

标识符由字母和数字组成,且首字符必须为字母;这里只对整型常数进行识别,浮点数数据在识别过程中会对小数点进行报错。

(4)空格由空白、制表符和换行符组成。

空格一般用来分隔ID、NUM、运算符、界符和关键字,制表符和换行符等都只是对程序起到一个可读性的作用,在词法分析中没有实际意义,因此词法分析阶段通常被忽略。

(5)注释由/**/及其内部内容组成,在词法分析阶段也对程序的分析没有实际意义,因此也要被忽略。

关键字的判定规则是先进行标识符的识别,识别到最终状态后,在关键字数组中查找,看此标识符是否为关键字,如果是则将其划为关键字一类,否则作为标识符。

因此,此正规式的主要分析在标识符ID中会详细分析,在分析完后只要通过一个strcmp函数就可以分辨出是否关键字。

2、标识符与运算符

各种单词符号对应的种别码

单词种别是语法分析需要的信息,而单词自身的值则是编译其他阶段需要的

信息。

因此词法分析器的输出结果表示为:

(单词种别,单词自身的值)

单词的种别采用整数编码表示,下面给出了词法分析器的各个单词符号的

种别码。

Start:

1

If:

2

Then:

3

Int:

4

While:

5

Do:

6

For:

7

End:

8

字母:

10

数字:

11

*:

13

/:

14

+:

15

-:

16

:

:

17

>:

20

<:

23

=:

25

;:

26

(:

27

):

28

!

:

0

2.1标识符ID:

标识符ID的正则式表示如下:

ID=letter(letter|digit)*

letter=a~z

digit=0~9

根据以上正则式画出NFA如下图。

图1ID的NFA表示

下面将这个NFA转换为DFA。

利用子集构造法,构造上述NFA的DFA步骤如下:

(1)首先计算ԑ—closure

(1),令0=ԑ—closure

(1)={1},0未被标记,它现在是子集族C的唯一成员。

(2)标记0;令1=ԑ—closure(move(0,letter))={2,3,4,6,9},将1加入到C中,1未被标记。

令2=ԑ—closure(move(0,digit))=Ф,不再计算。

(3)标记1;计算3=ԑ—closure(move(1,letter))={3,4,5,6,8,9},将3加入到C中,它未被标记。

计算4=ԑ—closure(move(1,digit))={3,4,6,7,8,9},将4加入到C中,它未被标记。

(4)标记3;计算ԑ—closure(move(3,letter))={3,4,5,6,8,9},即3,它已经在C中了。

计算ԑ—closure(move(3,digit))={3,4,6,7,8,9},即4,它已经在C中了。

(5)标记4;计算ԑ—closure(move(4,letter))={3,4,5,6,8,9},即3,它已经在C中了。

计算ԑ—closure(move(4,digit))={3,4,6,7,8,9},即4,它已经在C中了。

至此,算法终止共构造了4个子集:

0={1}1={2,3,4,6,9}

3={3,4,5,6,8,9}4={3,4,6,7,8,9}

那么上述NFA构造的DFA为:

(1)S={[0],[1],[3],[4]}

(2)Σ={letter,digit}

(3)D([0],letter)=[1]

D([1],letter)=[3]D([1],digit)=[4]

D([3],letter)=[3]D([3],digit)=[4]

D([4],letter)=[3]D([4],digit)=[4]

(4)0=[0]

(5)=[4]

为了方便书写,将[0],[1],[3],[4]分别重命名为A,B,C,D。

则重新

得到的DFA如下图所示。

图2ID的DFA表示

上面的DFA并不是最小DFA,如果要转换为最小DFA,则只需将B与D状态合并即可。

参考如下。

图3ID的mDFA

根据DFA进行辨别标识符的程序设计,算法分析如下:

(1)得到第一个首单词,确定为字母,记下当前位置为初始位置;

(2)向后取一个单词;

(3)如果该单词为字母或者数字,跳到

(2)继续执行;

(4)如果该单词为空格符或者换行符等界符,则停止执行;

(5)在停止处,去初始位置到当前位置的单词单元到缓冲区,作为整个标识符输出;

(6)程序结束。

该部分程序没有考虑关键字判别,因此,在上述算法中,在第5步之后要进行操作:

根据得到的缓冲区的数据,依次与关键字数组中的关键字进行比较,如果相同,那么这个标识符就是关键字,将它划为关键字一类输出。

含有关键字比较的程序流程图如图4。

图4ID与关键字程序流程

2.2运算符和界符:

运算符和界符的正则式表示为:

mark=+|-|*|/|=|==|(|)|[|]|{|}|!

|!

=|++|<|<=|>|>=|,|;|:

运算符的检索也比较简单,因为大多只有一个符号,对于双目运算符则按如下方式识别:

+→++(后一位为+)|+(后一位不为+)

-→负号(在=或者(之后)|减号(不在=或者(之后)

<→<=(后一位为=)|<(后一位不为=)

>→>=(后一位为=)|>(后一位不为=)

!

→!

=(后一位为=)|!

(后一位不为=)

/→注释(后一位为/或者*)|除号(后一位不为/或者*)

按照以上思想,如果碰到上述运算符,则相应的查看后一位的值,按条件识别不同的单目运算符和双目运算符。

2.3整型常数:

整型常数NUM的正则式表示如下:

NUM=digitdigit*

digit=0~9

根据正则式画出相应的NFA如图4。

同样,根据子集构造法,如同ID的转换方法类似可以得到相应的DFA。

NUM的DFA表示如图5。

图5NUM的NFA表示

DFA表示如下:

0={1},1={2,3,5},2={3,5}

(1)S={[0],[1],[2]}

(2)Σ={digit}

(3)D([0],digit)=[1]

D([1],digit)=[2]

D([2],digit)=[2]

(4)0=[0]

(5)=[2]

将[0],[1],[2]分别用A,B,C代替,则可以得到NUM的DFA表示。

图6NUM的DFA表示

同理,根据DFA可以将B状态合并,这样得到的mDFA如图7。

图7NUM的mDFA

因此,整型数据的递归方法与标识符类似,这里不列举其算法思想与流程图了。

在递归过程中的计数方法为依次往后取数的过程中,原值乘十后与最低位相加,算式为:

uWord.value.T2=uWord.value.T2*10+strSource[gnLocate]-'0'。

其中,等式左边uWord.value.T2为存放整型数据的缓冲区,strSource[gnLocate]为当前位置的数值的ascii码字符值,因此需减去字符0的ascii码值。

另外,还有负数的问题。

负数问题的解决方式是一样的。

在运算符判断过程中如果已经发现“-”为负号,那么其后的整型常数仍旧按照上述方法取指,但是计算数值的公式为:

uWord.value.T2=uWord.value.T2*10-strSource[gnLocate]-'0'。

这样得到的整个数据即为正确的负数值。

三程序实现

源代码

#include

#include

#include

charprog[80],token[8];

charch;

intsyn,p,m=0,n,row,sum=0;

char*rwtab[8]={"start","if","then","int","while","do","for","end"};

usingnamespacestd;

voidscaner()

{

for(n=0;n<8;n++)token[n]=NULL;

ch=prog[p++];

while(ch==''){

ch=prog[p];

p++;

}

if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){//标示符或者变量名

m=0;

while((ch>='0'&&ch<='9')||(ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){

token[m++]=ch;

ch=prog[p++];

}

token[m++]='\0';

p--;

syn=10;

for(n=0;n<8;n++)//将识别出来的字符和已定义的标示符作比较,

if(strcmp(token,rwtab[n])==0){

syn=n+1;

break;

}

}elseif((ch>='0'&&ch<='9')){//数字

{

sum=0;

while((ch>='0'&&ch<='9')){

sum=sum*10+ch-'0';

ch=prog[p++];

}

}

p--;

syn=11;

if(sum>32767)

syn=-1;

}elseswitch(ch){//其他字符

case'<':

m=0;

token[m++]=ch;

ch=prog[p++];

if(ch=='>'){

syn=21;

token[m++]=ch;

}elseif(ch=='='){

syn=22;

token[m++]=ch;

}else{

syn=23;

p--;

}

break;

case'>':

m=0;

token[m++]=ch;

ch=prog[p++];

if(ch=='='){

syn=24;

token[m++]=ch;

}else{

syn=20;

p--;

}

break;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1