Pascal语言编译器的设计与实现.docx
《Pascal语言编译器的设计与实现.docx》由会员分享,可在线阅读,更多相关《Pascal语言编译器的设计与实现.docx(9页珍藏版)》请在冰豆网上搜索。
Pascal语言编译器的设计与实现
Pascal语言编译器的设计与实现
我们设计的编译程序涉及到编译五个阶段中的三个,即词法分析器、语法分析器和中间代码生成器。
编译程序的输出结果包括词法分析后的二元式序列、变量名表、状态栈分析过程显示及四元式序列程序,整个编译程序分为三部分:
(1)词法分析部分
(2)语法分析处理及四元式生成部分(3)输出显示部分
一.词法分析器设计
于我们规定的程序语句中涉及单词较少,故在词法分析阶段忽略了单词输入错误的检查,而将编译程序的重点放在中间代码生成阶段。
词法分析器的功能是输入源程序,输出单词符号。
我们规定输出的单词符号格式为如下的二元式:
(单词种别,单词自身的值)
#defineACC-2#definesy_if0#definesy_then1#definesy_else2#definesy_while3#definesy_begin4#definesy_do5#definesy_end6#definea 7#definesemicolon8#definee 9#definesharp10#defineS 11#defineL 12
#definetempsy 15#defineEA18//Eand#defineEO19//Eor
#defineplus34#definesubtract35#definetimes36#definedivide37#definebexxes38
#defineop_and39#defineop_or40#defineop_not41#definerop42
#definelparent48#definerparent49#defineident 56#defineintconst 57
函数说明1.读取函数readline()、readchar()
词法分析包含从源文件读取字符的操作,但频繁的读文件操作会影响程序执行效率,故实际上是从源程序文件””中读取一行到输入缓冲区,而词法分析过程中每次读取一个字符时则是通过执行readchar()从输入缓冲区获得的;若缓冲区已被读空,则再执行readline()从中读取下一行至输入缓冲区。
2.扫描函数scan()
扫描函数scan()的功能是滤除多余空格并对主要单词进行分析处理,将分析得到的二元式存入二元式结果缓冲区。
3.变量处理find
变量处理中首先把以字母开头的字母数字串存到spelling[]数组中,然后进行识别。
识别过程是先让它与保留关键字表中的所有关键字进行匹配,若获得成功则说明它为保留关键字,即将其内码值写入二元式结果缓冲区;否则说明其为变量,这时让它与变量名表中的变量进行匹配),如果成功,则说明该变量已存在并在二元式结果缓冲区中标记为此变量,否则将该变量登记到变量名表中,再将这个新变量存入二元式缓存数组中。
4.数字识别number()
数字识别将识别出的数字填入二元式结果缓存数组。
5.显示函数
显示函数的功能在屏幕上输出词法分析的结果,同时给出二元式个数及源程序行数统计。
二.语法分析器设计
语法分析器的核心是三张SLR分析表以及针对这三张SLR分析表进行语义加工的语义动作。
编译程序中语法分析处理及四元式生成部分主要是以二元式作为输入,并通过SLR分析表对语法分析处理过程进行控制,使四元式翻译的工作有条不紊的进行,同时识别语法分析中的语法错误。
在处理if和while语句时,需要进行真值或假值的拉链和返填工作,以便转移目标的正确填入。
1.控制语句的SLR分析表1设计过程如下:
将扩展文法G’
0)S’S
1)SifethenSelseS2)SwhileedoS3)SbeginLend4)Sa5)LS6)LS;L
用∈_CLOSURE方法构造LR(0)项目规范簇为:
I0:
S’·S
S·ifethenSelseSS·whileedoS S·beginLend
S·a;I1:
S’S·
I2:
Sif·ethenSelseSI3:
Swhile·edoSI4:
Sbegin·Lend L·S L·S;L
S·ifethenSelseS
S·whileedoS S·beginLendS·a
I5:
Sa·
I6:
Sife·thenSelseS I7:
Swhilee·doS I8:
SbeginL·end I9:
LS·
LS·;L
I10:
Sifethen·SelseS
S·ifethenSelseSS·whileedoSS·beginLend
I11:
Swhileedo·S
S·ifethenSelseSS·whileedoSS·beginLendS·a
I12:
SbeginLend·I13:
LS;·L
L·SL·S;L
S·ifethenSelseSS·whileedoSS·beginLendS·a
I14:
SifethenS·elseSI15:
SwhileedoS·I16:
LS;L·
I17:
SifethenS·elseS
S·ifethenSelseSS·whileedoS
S·beginLendS·a
I18:
SifethenSelseS·
构造文法G’中非终结符的FOLLOW集如下:
FOLLOW(L)={end}
FOLLOW(S)={else,;,end,#}
在LR项目规范簇中,只有I9有“移进――归约”冲突,LS·LS·L因为FOLLOW(L)∩FIRST(L)=∮所以可以用SLR方法解决以上冲突,最后我们得到的SLR分析表如下:
If0123456789101112131415161718S2 S2 S2S2S2 S2
staticint/*0*//*1*//*2*//*3*//*4*//*5*//*6*//*7*//*8*//*9*/
then S10 else R4 R3S17R2R1whilebeginS3 S3 S3S3S3 S3S4 S4 S4S4S4 S4ACTIONdo S11 end R3S12R5R3R2R6R1aS5 S5 S5S5S5 S5; R4 S13R3R2R1eS6S7 #ACC R4 R3R2R1S1 9 14159 18GOTOL 8 16 action[19][13]={{2,-1,-1,3,4,-1,-1,5,-1,-1,10,1,-1},{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,ACC,-1,-1},{-1,-1,-1,-1,-1,-1,-1,-1,-1,6,-1,-1,-1},{-1,-1,-1,-1,-1,-1,-1,-1,-1,7,-1,-1,-1},{2,-1,-1,3,4,-1,-1,5,-1,-1,-1,9,8},{-1,-1,104,-1,-1,-1,104,-1,104,-1,104,-1,-1},{-1,10,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1,11,-1,-1,-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1,-1,12,-1,-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1,-1,105,-1,13,-1,-1,-1,-1},
/*10*/{2,-1,-1,3,4,-1,-1,5,-1,-1,-1,14,-1},/*11*/{2,-1,-1,3,4,-1,-1,5,-1,-1,-1,15,-1},/*12*/{-1,-1,103,-1,-1,-1,103,-1,103,-1,103,-1,-1},/*13*/{2,-1,-1,3,4,-1,-1,5,-1,-1,-1,9,16},/*14*/{-1,-1,17,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},/*15*/{-1,-1,102,-1,-1,-1,102,-1,102,-1,102,-1,-1},/*16*/{-1,-1,-1,-1,-1,-1,106,-1,-1,-1,-1,-1,-1},/*17*/{2,-1,-1,3,4,-1,-1,5,-1,-1,-1,18,-1},/*18*/{-1,-1,101,-1,-1,-1,101,-1,101,-1,101,-1,-1}};
其中,前9列为action值,后2列为goto值;0~16表示17个移进状态;-1表示出错;ACC表示分析成功;而100~106对应归约产生式:
2.算术表达式的LR分析表2设计如下:
0)S’E1)EE+E2)EE*E3)E(E)
4)Ei I012345678910111213S3S3S3S3 S3S3+S4R4S4R1R2R3R6R5-S11R4S11R1R2R3R6R5*S5R4S5S5R2R3R6S5ACTION/S10R4S10S10R2R3R6S10(S2S2S2S2 S2S2) R4S9R1R2R3R6R5#ACCR4 R1R2R3R6R5GOTOE1678 1213
staticintaction1[14][9]=/*0*/{{3,-1,-1,-1,-1,2,-1,-1,1},/*1*/{-1,4,11,5,10,-1,-1,ACC,-1},/*2*/{3,-1,-1,-1,-1,2,-1,-1,6},/*3*/{104,104,104,104,104,104,104,104,-1},/*4*/{3,-1,-1,-1,-1,2,-1,-1,7},/*5*/{3,-1,-1,-1,-1,2,-1,-1,8},
/*6*/{-1,4,11,5,10,-1,9,-1,-1},/*7*/{101,101,101,5,10,101,101,101,-1},/*8*/{102,102,102,102,102,102,102,102,-1},/*9*/{103,103,103,103,103,103,103,103,-1},/*10*/{3,-1,-1,-1,-1,2,-1,-1,12},/*11*/{3,-1,-1,-1,-1,2,-1,-1,13},/*12*/{106,106,106,106,106,106,106,106,-1},/*13*/{105,105,105,5,10,105,105,105,-1}};
3.布尔表达式的SLR分析表3设计如下:
1)S’B2)Bi
3)Biropi4)B(B)5)BNOTB6)ABAND7)BAB8)OBOR9)BOBi0123456789101112131415S1S3S1S1S1S1R5R7 RopS2 (S4 S4S4S4S4R5R7 ACTION)R1R2R4 S12R3R6R8NOTS5 S5S5S5S5R5R7 ANDR1R2S9 S9R3S9S9S9ORR1R2S10 S10R3S10S10S10#R1R2R4 R3ACCR6R8B13 1161415 GOTOA7 7777 O8 8888 staticintaction2[16][11]=/*0*/{{1,-1,4,-1,5,-1,-1,-1,13,7,8},/*1*/{-1,2,-1,101,-1,101,101,101,-1,-1,-1},/*2*/{3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},/*3*/{-1,-1,-1,102,-1,102,102,102,-1,-1,-1},/*4*/{1,-1,4,-1,5,-1,-1,-1,11,7,8},
/*5*/{1,-1,4,-1,5,-1,-1,-1,6,7,8},/*6*/{-1,-1,-1,104,-1,9,10,104,-1,-1,-1},/*7*/{1,-1,4,-1,5,-1,-1,-1,14,7,8},/*8*/{1,-1,4,-1,5,-1,-1,-1,15,7,8},/*9*/{105,-1,105,-1,105,-1,-1,105,-1,-1,-1},/*10*/{107,-1,107,-1,107,-1,-1,107,-1,-1,-1},/*11*/{-1,-1,-1,12,-1,9,10,-1,-1,-1,-1},/*12*/{-1,103,-1,103,-1,103,103,103,-1,-1,-1},/*13*/{-1,-1,-1,-1,-1,9,10,ACC,-1,-1,-1},/*14*/{-1,-1,-1,106,-1,9,10,106,-1,-1,-1},/*15*/{-1,-1,-1,108,-1,9,10,108,-1,-1,-1}};
LR分析表控制语义加工的实现:
当扫描LR分析表的当前状态为归约状态时,则在调用与该状态对应的产生式进行归约的同时,调用相应的语义子程序进行有关的翻译工作。
现在对LR分析器的分析栈加以扩充,使得每个文法符号之后都跟着它的语义值。
为了清晰起见,我们把这个栈的每一项看成三部分组成:
状态state,文法符号syl和语义值val。
编译程序实现算术表达式、布尔表达式及程序语句的语义加工时,都是按这种状态栈加工方式进行的。
例如:
(5+3)*6的分析过程序号123456789101112131415002023026026402643026470260269010150153015801ACCSTATE--------5--5---5----5-3--8--8--8-8--8---8-6-48Val##(#(5#(E#(E+#(E+3#(E+E#(E#(E)#E#E*#E*6#E*E#Esylinput(5+3)*6#5+3)*6#+3)*6#+3)*6#3)*6#)*6#)*6#)*6#*6#*6#*6####在分析过程中,第步操作后的状态栈为023,根据栈顶状态“3”和现行输入符号“+”查分析表ACTION[3,+]=R4,即按第个产生式E→n来进行归约;于产生式右部仅含一项,故去掉状态栈栈顶“3”;此时2变为新的栈顶状态,再查的下一状态s′:
GOTO[2,E]=6,即将状态6和文法符号E压栈,最后得到第步的状态。
第步操作后也是如此,当前状态栈为02647,根据栈顶状态7和现行输入符号“)”查分析表ACTION[7,)]=R1,即按第个产生式E→E1+E2进行归约;于产生式右部有三项,故去掉状态栈栈顶的647三项;此时2变为新的栈顶状态,再查的下一状态s′:
GOTO[2,E]=6,即将状态6和文法符号E压栈,最后得到第步的状态。
三.中间代码生成器设计:
1.布尔表达式
布尔表达式在程序语言中有两个基本作用:
一是用作控制语句的条件式;二是用于逻辑演算,计算逻辑值。
布尔表达式是布尔算符作用于布尔变量或关系表达式而形成的。
关系表达式的形式是E1ropE2,其中rop是关系符,E1和E2是算术式。
在这里,我们只考虑前面给定文法所产生的布尔表达式:
B→B&&B|B||B|!
B|(B)|iropi|i
遵照我们的约定,布尔算符的优先顺序为:
!
、&&、||,并假定&&和||都服从左结合规则。
所有关系符的优先级都是相同的,而且高于任何布尔算符,低于任何算术算符,关系算符不得结合。
表达式的真、假出口的确定:
考虑表达式B1||B2,若B1为真,则立即知道B也为真;因此,B1的真出口也就是整个B的真出口。
若B1为假,则B2必须被计值,B2的第一个四元式就是B1的假出口。
当然,B2的真、假出口也就是整个B的真、假出口。
类似的考虑适用于对B1&&B2的翻译,我们将B1||B2和B1&&B2的翻译用下图表示。
在自下而上的分析过程中,一个布尔式的真假出口往往不能在产生四元式的同时就填上。
我们只好把这种未完成的四元式的地址作为B的语义值暂存起来,待到整个表达式的四元式产生完毕之后再来回填这个未填入的转移目标。
2.条件语句
对条件语句ifeS1elseS2中的布尔表达式e,其作用仅在于控制对S1和S2的选择。
因此,作为转移条件的布尔式e,我们可以赋予它两种“出口”:
一是“真”出T口,出向S1;一是“假”出口,出向S2。
于是,e的代码F条件语句可以翻译成如图的一般形式。
非终结符e具有两项语义值e_TC和e_FC,它们分别指出了尚待回填真、S2的代码假出口的四元式串。
e的“真”出口只有在往回扫描到if时才能知道,而它图3-2 条件语句的代码结构的“假”出口则需到处理过S1并且到达else才能明确。
这就是说,必须把e_FC的值传下去,以便到达相应的else时才进行回填。
另外,当S1语句执行完时意味着整个if-else语句也已执行完毕;因此,在S1的编码之后应产生一条无条件转移指令。
这条转移指令将导致程序控制离开整个if-else语句。
但是,在完成S2的翻译之前,这条无条件转移指令的转移目标是不知道的。
甚至,在翻译完S2之后,这条转移指令的转移目标仍无法确定。
这种情形是于语句的嵌套性所引起的。
例如下面的语句:
ife1ife2S1elseS2elseS3在S1的代码之后的那条无条件转移指令不仅应跨越S2而且应跨越
S3。
这也就是说,转移目标的确定和语句所处的环境密切相关。
3.条件循环语句
条件循环语句whileeS通常被翻译成图的代码结构。
布尔式e的“真”出口出向S代码段的第一个四元式。
紧接S代码段之后应产生一条转向测试e的无条件转移指令。
e的“假”出口将导致程序控制离开整个while语句。
e的“假”出口目标即使在整个while语句翻译完之后也未必明确。
例如:
ife1whilee2S1elseS2这种情况仍是于语句的嵌套性引起的。
所以,我们只好把它作为语句的语义值S·CHAIN暂留下来,以便在处理外层语句时再伺机回填。
语法翻译实现方法
将上述语法翻译付诸实现过程中,我们仅保留了算术表达式和布尔表达式翻译的文法和语义动作;面对程序语句的翻译,于改造后含有较多的非终结符且语义动作又相对简单,故仍恢复为改造之前的程序语句文法。
于总体上构造一个SLR分析表来实现语法分析及语义加工将使得所构造的SLR分析表过大,所以将其分为下面三部分处理:
(1)对算术表达式单独处理,即为算术表达式构造一个SLR分析表,并将赋值语句A=E与算术表达
式归为一类处理,处理之后的赋值语句仅看作为程序语句文法中的一个终结符a。
(2)对布尔表达式也单独处理,并为其构造一个SLR分析表,经SLR分析表处理后的布尔表达式看
作为程序语句文法中的一个终结符e。
(3)程序语句文法此时变为:
SifethenSelseS|whileedoS|beginLend|a;LSL|S
此时为程序语句构造相应的SLR分析表就简单多了。
前面的程序语句文法中所添加的非终结符是为了能及时回填有关四元式转移目标而引入的,在取消了这些非终结符后又如何解决及时回填转移目标的问题呢?
我们采取的解决方法是增加两个数组labelmark和labeltemp来分别记录语句嵌套中每一层布尔表达式e的首地址以及每一层else之前的四元式地址。
也即,对程序语句的翻译来说:
①在处理完布尔表达式e后,回填if或while语句的真值链;②在归约完每一个语句S之后检查符号栈,看在S之前的文法符号是否if或while,若是则
回填假值链;③在if语句中,else前面要加入一个无条件转移的四元式转向if语句末尾;在while语句尾
要有一个无条件转移四元式转向while语句开头。
四.数据结构说明
编译程序中涉及到的数据结构说明如下:
/******************************************/
charch='\\0';/*当前字符*/int count=0;staticcharspelling[10]={\存放识别的字*/staticcharline[81]={\一行字符缓冲区*/char*pline;/*字符缓冲区指针*/
staticcharntab1[100][10];structntab{inttc;intfc;}ntab2[200];intlabel=0;
/*存放临时变量的表的定义*/structrwords{charsp[10];intsy;};
/*存放文件的结构*/
structrwordsreswords[10]={{\{\{\{\{\{\{\{\{\{\structaa{intsy1;intpos;
}buf[1000],/*词法分析结果缓冲区*/g_nCurChar,/*当前字符*/
g_nExpCurChar,/*当前表达式中的字符*/E,/*非终结符*/
sstack[100],/*符号栈*/ibuf[100],stack[1000];structaaoth;s