一、实习目的
理解编译程序的构造原理,掌握编译程序的构造方法与技术。通过实习,使学生既加深对编译原理基础理论的理解,又提高动手能力,特别是提高软件设计能力。
二、实习要求
在理解编译原理基本思想的基础上,选择一个自己熟悉的程序设计语言,完成编译程序的设计和实现过程。
编译程序的设计可以采用自顶向下和自底向上两种不同的方法。由于许多高级语言(如PASCAL,C)中的语法成分都是递归定义的,所以本实习要求学生采用递归下降分析技术,这是一种自顶向下的的编译方法,其基本思想是对语言的每个(或若干个)语法成分编制一个处理子程序,从处理<程序>这个语法成分的子程序开始,在分析过程中调用一系列过程或函数,对源程序进行语法和语义分析,直到整个源程序处理完毕为止。
本上机实习是为C语言(子集)设计一个编译程序,完成词法分析、语法分析、语义分析等功能,并生成某种机器上的目标代码(汇编语言)或中间代码(四元式)。
三、实习步骤
1.阅读《上机实习指导书》。
2.根据设计要求写算法,画程序框图
3.根据框图编写编译程序
4.输入编译程序并上机调试
5.撰写上机实习报告
四、实习内容
1、题目:C语言小子集编译程序的实现
2、C语言小子集的文法规则:
1.<程序>::=main(){<分程序>}
2.<分程序>::=<变量说明部分>;<语句部分>
3.<变量说明部分>::=<变量说明><标识符表>
4.<变量说明>::=int
5.<标识符表>::=<标识符表>,<标识符>
6.<标识符表>::=<标识符>
7.<标识符>::=<字母>
8.<标识符>::=<标识符><字母>
9.<标识符>::=<标识符><数字>
10.<语句部分>::=<语句部分>;<语句>|<语句>
11.<语句>::=<赋值语句>|<条件语句>|<循环语句>|
12.<赋值语句>::=<标识符>=<表达式>
13.<条件>::=<表达式><关系运算符><表达式>
14.<表达式>::=<项>|<表达式><加法运算符><项>
15.<项>::=<因子>|<项><乘法运算符><因子>
16.<因子>::=<标识符>|<常量>|(<表达式>)
17.<常量>::=<无符号整数>
18.<无符号整数>::=<数字序列>
19.<数字序列>::=<数字序列><数字>
20.<数字序列>::=<数字>
21.<加法运算符>::=+|-
22.<乘法运算符>::=*|/
23.<关系运算符>::=<|>|!=|>=|<=|==
24.<复合语句>::={<语句部分>}
25.<语句1>::=<语句>|<复合语句>
26.<条件语句>::=if(<条件>)<语句1>else<语句1>
27.<循环语句>::=while(<条件>)do<语句1>
28.<字母>::=a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z
29.<数字>::=0|1|2|3|4|5|6|7|8|9
3、实现功能:
(1)词法分析
扫描源程序,根据词法规则,识别单词,填写相应的表。如果产生词法错误,则显示错误信息、位置,并试图从错误中恢复。简单的恢复方法是忽略该字符(或单词)继续扫描。
(2)语法分析
对源程序作语法分析,确定是否属于C语言小子集,同时揭示出程序的内在结构。
(3)语法错误检查
根据C语言小子集的文法规则设置检测手段,通过查错子程序或一些查错语句,报告源程序出错位置、性质等,直至整个程序结束为止。
(4)语义分析与目标代码生成
在语法分析的基础上,进行语义分析,生成输入源程序的目标代码。输入源程序的目标代码可以建立在一个假想的处理机(虚拟机)上,或者以所学的汇编语言为基础,也可以生成四元式序列。
输入源程序样本:(这只是一个例子,调试时可以任意修改或换其它程序)
main ()
{ int a,b,y,max;
a=10; b= ;
while (a>0)
{ b=a+b*a;
a=a-1
};
x=a+b; y=b+b;
if (x>y) max=x
else max=y
}
消除左递归后的文法:
1.<程序>::=main(){<分程序>}
2.<分程序>::=<变量说明部分>;<语句部分>
3.<变量说明部分>::=<变量说明><标识符表>
4.<变量说明>::=int
5.<标识符表>::=<标识符><标识符表1>
6. <标识符表1>::=,<标识符><标识符表1>|空串
7.<标识符>::=<字母><标识符1>
8.<标识符>::=<数字><标识符1>
9.<标识符1>::=<字母><标识符1>|空串
10.<标识符1>::=<数字><标识符1>|空串
11.<语句部分>::=<语句><语句部分1>
12.<语句部分1>::=;<语句><语句部分1>|空串
13.<语句>::=<赋值语句>|<条件语句>|<循环语句>
14.<赋值语句>::=<标识符>=<表达式>
15.<条件>::=<表达式><关系运算符><表达式>
16.<表达式>::=<项><表达式1>
17.<表达式1>::=<加法运算符><项><表达式1> | 空串
18.<项>::=<因子><项1>
19.<项1>::=<乘法运算符><因子><项1>|空串
20.<因子>::=<标识符>|<常量>|(<表达式>)
21.<常量>::=<无符号整数>
22.<无符号整数>::=<数字序列>
23.<数字序列>::=<数字><数字序列1>
24.<数字序列1>::=<数字><数字序列1>
25.<加法运算符>::=+|-
26.<乘法运算符>::=*|/
27. <关系运算符>::=<|>|!=|>=|<=|==
28. <复合语句>::={<语句部分>}
29.<语句1>::=<语句>|<复合语句>
30. <条件语句>::=if(<条件>)<语句1>else<语句1>
31. <循环语句>::=while(<条件>)do<语句1>
32. <字母>::=a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z
33. <数字>::=0|1|2|3|4|5|6|7|8|9
开始
1.词法分析
种别码对应表
单词/符号 | 种别码 | 单词/符号 | 种别码 |
---|---|---|---|
main | 1 | != | 16 |
int | 2 | >= | 17 |
if | 3 | <= | 18 |
else | 4 | == | 19 |
while | 5 | * | 20 |
do | 6 | / | 21 |
, | 7 | + | 22 |
; | 8 | - | 23 |
( | 9 | 整数 | 24 |
) | 10 | 标识符 | 25 |
{ | 11 | ||
} | 12 | ||
= | 13 | ||
< | 14 | ||
> | 15 |
我的思路是先判断是否要进行取词操作(flag_cw),紧接着判断是否有空格、换行符(有则行数line自增)、制表符,然后利用getc()取词。(这其中涉及到文件操作,具体的请朋友们自行度娘,资源应该很多)。紧接着就是对读入的字符利用typeOf()分类,并将字符分类成各个单词/符号。
代码
void getWord() //词法分析
{
int i = 0;
int j = 0;
int type;
if(flag_cw == 0)标识符
{
letter = getc(cfin);
}
while(letter == " " || letter == "\n" || letter == "\t")
{
if(letter == "\n")
line++;
letter = getc(cfin);
flag_cw = 1;
}
type = typeOf(letter);
switch(type)
{
case 1: //识别数字
{
for(i = 0;i < 20;i++)
word[i] = NULL;
i = 0;
do
{
strcpy(&word[i],letter.c_str()); //将从文件中获取的单个字符存入word数组中
i++;
letter = getc(cfin);
flag_cw = 1;
}while(typeOf(letter) == 1); //数字类型为1
strcpy(word1.content,word); //将word数组中的单词存入单词表,下面类似代码以此类推
word1.type = 24; //整数类型为24
word1.numOfLine = line; //单词所在行数
break;
}
case 2: //识别以字母开头的标识符
{
for(i = 0;i < 20;i++)
word[i] = NULL;
i = 0;
do
{
strcpy(&word[i],letter.c_str());
i++;
letter = getc(cfin);
flag_cw = 1;
}while(typeOf(letter) == 1 || typeOf(letter) == 2); //除开头外有数字或字母的标识符
strcpy(word1.content,word);
word1.numOfLine = line;
if(isKeyWord(word) != 0) //识别保留字
{
word1.type = isKeyWord(word); //将保留字对应的种别码写进单词表
break;
}
else
{
word1.type = 25; //标识符类型为25
break;
}
}
case 3: //识别符号
{
for(i = 0;i < 20;i++)
word[i] = NULL;
i = 0;
strcpy(&word[i],letter.c_str());
if(letter == "=" || letter == "<" || letter == ">" || letter == "!")
{
i++;
letter = getc(cfin);
if(letter == "=") //判断"!=","==",">=","<="
{
flag_cw = 1;
strcpy(&word[i],letter.c_str());
strcpy(word1.content,word);
word1.type = isToken(word1.content);
word1.numOfLine = line;
letter = getc(cfin);
}
else
{
flag_cw = 1;
strcpy(word1.content,word);
word1.type = isToken(word);
word1.numOfLine = line;
//printError("错误的符号");
}
}
else
{
i++;
letter = getc(cfin);
flag_cw = 1;
strcpy(word1.content,word);
word1.type = isToken(word);
word1.numOfLine = line;
}
break;
}
default:
{
word1.type = 0; //错误的单词
strcat(word1.content," ");
break;
}
}
}
2.语法分析
根据消除左递归后的文法,开始编写语法分析部分,由于文法数众多,这里选择两个作为范例说明。
以<程序>::=main(){<分程序>} 和 <分程序>::=<变量说明部分>;<语句部分> 为例
void chengXu() //<程序>::=main(){<分程序>}
{
getWord();
if(strcmp(word1.content,"main"))
{
printError("缺少关键字'main'");
}
getWord();
if(strcmp(word1.content,"(") != 0)
{
printError("缺少左括号'('");
}
getWord();
if(strcmp(word1.content,")") != 0)
{
printError("缺少右括号')'");
}
getWord();
if(strcmp(word1.content,"{") != 0)
{
printError("缺少左大括号'{'");
}
fenChengXu();
if(strcmp(word1.content,"}") != 0)
{
printError("缺少右大括号'}'");
}
op = "go";
str1 = "";
str2 = "";
result1 = "";
getQuadruple(op,str1,str2,result1);
cout << "语法分析完成,发现" << err << "个错误" << endl;
}
void fenChengXu() //<分程序>::=<变量说明部分>;<语句部分>
{
bianLiangShuoMingBF();//变量说明部分
if(strcmp(word1.content,";") != 0)
{
printError("缺少分界符';'");
}
getWord();
yuJuBuFen();
}
这里要解释下getQuadruple()。这个用于生成中间代码(四元式)。具体的定义如下:
struct Quadruple //四元式
{
public:
string optr; //运算符
string num1; //操作数1
string num2; //操作数2
string result; //结果
int tag; //标记
};
void getQuadruple(string optr,string n1,string n2,string r) //生成四元式
{
quadruple1[f_num].optr = optr;
quadruple1[f_num].num1 = n1;
quadruple1[f_num].num2 = n2;
quadruple1[f_num].result = r;
f_num++;
}
3.语义分析与中间代码生成
这部分也是本人依然不太熟悉的部分,借(chao)鉴(xi)了不少大佬的成果,大家将就看看。。。
template<typename A> string toString(const A& t) //把数字转换成字符串
{
ostringstream oss;
oss << t;
return oss.str();
}
string newtemp() //设置中间变量
{
char temp[15] = "A";
temp[1] = p_temp / 10 + '0';
temp[2] = p_temp++ % 10 + '0';
return temp;
}
void addJumpLine(int num,int jumpline,int jumpn) //插入跳转标号 第1个参数用来填go
{
if(quadruple1[num].optr == "go")
{
quadruple1[num].num1 = toString(jumpline);
}
else
{
for(int i = f_num - 1;i >= 0;i--)
{
if(quadruple1[i].tag == jumpn)
{
quadruple1[i].result = toString(jumpline);
quadruple1[i].tag = 0;
break;
}
}
}
}
4.运行结果
示例代码:
main ()
{
int a,c;
a=10;
b=12;
while(b != -b)
do
{
c =9+8- (a + b) * b / a + b);
};
if(a > b)
{
a = a + b;
}
else
{
b = a + b;
}
}
结果如下:
可生成四元式版本:
main ()
{
int a,b,c;
a=10;
b=12;
while(b != 0)
do
{
c =9+8- (a + b) * b / a + b;
};
if(a > b)
{
a = a + b;
}
else
{
b = a + b;
}
}
5.总结
这是小菜鸟第一次写博客,算作自己的学习笔记。此外,这个程序还有诸多不足之处,还请各位大佬多多指教。
来源:CSDN
作者:yh5116
链接:https://blog.csdn.net/yh5116/article/details/103744122