武汉理工大学《编译原理》课程设计
E->TE'
E'->+TE' | -TE' | e
T->FT'
T'->*FT' | /FT' | e
F->(E) | id
P->E rop id
rop-> > | < | >= | <= | != | ==
2.2 WHILE 循环语句的属性文法
产生式
语义规则
S→while P{A}
S.addr:=newlabel;
E.true:=newlabel;
E.false:=S.next;
S1.next:=S.begin;
S.code:=gen(S.begin’:’‖E。code‖
gen(E.true’:’) ‖S1.code‖
gen(‘goto’ S.begin)
3 语法分析方法及中间代码形式的描述
3.1 语法分析方法
3.1.1 简单优先法
简单优先分析法是按照文法符号(终结符和非终结)的优先关系确定句柄的,因此我
们首先介绍任意两个文法符号之间的优先关系是怎样确定的,及如何构造优先关系表。
首先定义优先关系的表示:
X=Y 表示 X 和 Y 的优先关系相等
X>Y 表示 X 的优先性比 Y 的优先性大
X
武汉理工大学《编译原理》课程设计
邻关系确定他们的优先关系。
3.1.2 简单优先法的缺点
对文法的要求高,而且也要把文法符号的关系首先表示出来,这普遍性不是太好。
+
}a
S
w
h
e
E
d
o
{
B
=
(
N,
N,
c
N,
b
N,
1 },
)
N,
l
**/
i
#
N,
N,
N,
N,
N,
;
>
N,
N,
N,
N,
N, -1,
{/**
1
/* S */ { N,
N,
N,
/* w/ { N,
N,
N,
/* h*/ { N,
N,
N,
/* i/ { N,
N,
/* l/ { N,
N,
N,
/* e */ { N,
N,
N,
/* E*/ { N,
N,
N,
/* d*/ { N,
N,
N,
/* o*/ { N,
N,
N,
/* { { N,
N,
/* A/ { N,
0,
/*}*/ { N,
N,
N,
/* a */ { N,
N,
N,
/* = */ { N,
N,
N,
/* + */ { N,
N,
N,
/* 1 */ { N,
N,
N,
/* ; */ { N,
N,
N,
/* > */ { N,
N,
N,
/* ( */ { N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N },
N,
N },
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N },
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N },
N,
N },
N,
N },
N,
N },
N,
N },
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N },
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N },
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N },
N,
N },
N,
N },
N,
N },
N,
N },
N,
N },
N,
N },
N,
N },
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
1,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N, N,
N,
N,
N,
N,
0, -1,
N,
N,
N,
N,
N,
3
武汉理工大学《编译原理》课程设计
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
0,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
N,
1,
N,
N,
N,
N,
/* b */ { N,
N,
N,
/* c */ { N,
N,
N,
/* ) */ { N,
N,
N,
/* # */ { -1, -1, N,
N,
N,
N,
N,
N,
N },
N,
N },
N,
1 },
N,
0 }};
其中 N 表示的是没有优先关系, -1 表示优先级小于,0 表示优先级相同,1 表示
优先级大于
3.2 中间代码形式描述
3.2.1 三元式
三元式是一种普遍采用的中间代码形式。它由三个部分组成:算符 op,第一和第二
运算对象 ARG1 和 ARG2。运算对象有时候指用户自己定义的变量。
3.2.2 赋值语句的四元式
对 c=a+1 翻译结果如下
1)(+,a,1)
2),(=, c, (1))
3.2.3 while(a>b)语句翻译
为方便和直观,将 while 语句翻译为 if a> b goto do.addr
4 编译系统的概要设计
本程序开始,需先对输入字符串进行词法分析,将其识别为一个个独立的单词序列,
得到的字符单词要和关键字比较,看是否是关键字,根据比较结果进行返回相应的单词类
型。单词类型主要包括标识符,关键字,常量,运算符和分界符。在语法分析程序中,根
据词法分析得到的结果,进行判断是否是当前需要的单词类型,如果不是就说明输入字符
串不能由该文法推导出来;如果是当前需要的类型,就相应得做该单词类型分支程序。根
据简单优先法分析的字符串,看是否能得到接受状态。此外,在进行语法分析的同时进行
语义分析,审查程序有无语义错误,每个算符是否具有语言规范允许的运算对象,当不符
合语言规范时,编译程序应报告错误。在进行了上述的语法分析和语义分析阶段的工作之
后,将源文件变成三元式表示。
源程序目录下建立“input.txt”文本文档,在文档中输入 while(P){A}格式的 WHILE
4
武汉理工大学《编译原理》课程设计
语句,再通过调用程序中的 get_word(string word)函数进行对文本文档中语句的读取操作。
4.1 词法分析
词法分析是编译的第一阶段,它的主要任务是从左至右逐个字符地对源程序进行扫
描,产生一个单词序列,用于语法分析。
编程中,建立操作数栈和运算符栈,设定运算符优先级。对于读取的字符进行判定,
若是运算符,就与栈顶符号比较优先级,高则入栈,否则栈内运算符出栈;若是非运算符,
则送入操作数栈。
4.2 语法制导翻译
在语法分析过程中,随着分析的步步进展,根据每个产生式所对应的语义子程序(或
语义规则描述的语义动作)进行翻译。属性文法的每个符号有属性,所以每个符号入栈
时,必须连属性一起入栈,这样,栈符号就由文法符号及存放该符号属性的域所组成。
由于属性类型不同,属性域存放的内容就要根据属性的类型来定。有的可能直接存放属
性值,也有的存放的是指向属性值的指针。对于综合属性,其属性域不存放其属性值,
而是存放一个指针,指向存贮该属性值的单元。对于继承属性,其属性域直接保存其属
性值。继承属性的属性域刚入栈时为空,但是在该栈符号变成栈顶符号之前的某一时刻,
它们必须接受相应的属性值,即在成为栈顶时,继承属性的属性域必须有值。
5 详细的算法描述
5.1 文法设计
为顺利完成本次课程设计,实现要求的功能,设计测试文法 G(S)如下:
S-> while(P) {A};
A->id=E;
E->TE'
E'->+TE' | -TE' | e
T->FT'
T'->*FT' | /FT' | e
F->(E) | id
P->E rop id
5
武汉理工大学《编译原理》课程设计
rop-> > | < | >= | <= | != | ==
5.2 算法描述
//rop-> > | < | >= | <= | != | ==
//F->(E) | id
(1)word_node * word_link(word_node,word_node,string)//给结点赋值,并设置 type 值。
(2)void get_word(string word)函数设计:从文本文档中读取字符,存入 word_array
(3)简单优先子程序如下:
void rop(word_node *&p)
void F(word_node *&p)
void extend_T(word_node *&p)
void T(word_node *&p)
void extend_E(word_node *&p)
void E(word_node *&p)
void P(word_node *&p)
void A(word_node *&p)
bool S(word_node *&p)
//T->FT'
//E'->+TE' | -TE' | e
//E->TE'
//P->E rop b
//A->id=E;
// while(p) {doA};
//T'->*FT' | /FT' | e
5.3 源程序如下
//文件输入
//定义栈,便于归约时运用。
//关键字的个数
//数字的最大位数
//符号的最大长度
#include"Stack.h"
#include
#include
#include
using namespace std;
#define norw 13
#define nmax 14
#define al 10
#define N -10
#define getchdo if(getch()==-1)return -1
#define getsymdo if(getsym()==-1)return -1
ifstream *fin;
SeqStack Sym,Opt,Opr;
char *p,ch;
char str[20];
char inputstr[100]; //保存从文件读入的所有字符
char buf[5][20];
int count1=0,count2=0;
int ip=0,row,line;
6
武汉理工大学《编译原理》课程设计
//当前的符号
//当前 number
//临时符号
//当前 ident
//保留字
enum symbol sym;
int num;
char a[al+1];
char id[al+1];
char word[norw][al];
enum symbol wsym[norw];
enum symbol ssym[256];
enum symbol{
//关键字的符号值
//单字符的符号值
nul,
ident,
number,
plus,
minus,
oddsym,
geq,
semicolon,
elsesym,
eql,
lparen,
period,
neq,
rparen,
becomes,
lss,
lbrace,
beginsym,
times,
leq,
rbrace,
endsym,
whilesym,
writesym, readsym,
dosym,
constsym,
varsym,
procsym ,greater, less
slash,
gtr,
comma,
ifsym,
callsym,
};
void ThreeAddr();
void init()
{
//设置单字符符号
for(int i=0;i<=255;i++)
{
ssym[i]=nul;
}
ssym['+']=plus;
ssym['-']=minus;
ssym['*']=times;
ssym['/']=slash;
ssym['(']=lparen;
ssym[')']=rparen;
ssym['{']=lbrace;
ssym['}']=rbrace;
ssym['=']=eql;
ssym[',']=comma;
ssym['.']=period;
ssym['#']=neq;
ssym[';']=semicolon;
ssym[ > ]=greater;
ssym[ < ]=less;
//设置保留字名字,
strcpy(&(word[0][0]),"begin");
strcpy(&(word[1][0]),"call");
strcpy(&(word[2][0]),"const");
strcpy(&(word[3][0]),"do");
7