通过设计、编制、调试一个算术表达式的语法及语义分析程序,加深对语法及语义分析原理的理解,并实现词法分析程序对单词序列的词法检查和分析。
对算数表达式的运行过程描述:算数表达式总体上服从从左至右的运行过程,但不同运算符的优先级不同,首先要对输入的算数表达式进行词法分析,该过程是编制一个读单词的过程,从输入的源程序中,识别出各个具有独立意义的单词。其次是对其进行语法分析,对输入的算术表达式进行判断,若其合法,则进行语义分析。算数表达式遵从一定的优先顺序,不同运算符的优先级可能不一致,例如:*和/的优先级要低于(),+和-的优先级要低于*和/,由于只考虑四则运算,我们将问题局限于+、-、*、/以及()之间的运算,这就需要我们构造出一个算符优先矩阵,对于输入的算术表达式,依据优先关系矩阵得到一定的运算顺序规则,依据规则一步一步生成三元式,到三元式显示结束后,即可得到算数表达式的结果。
具体设计步骤如下:
(1)写出算符优先分析方法要求的文法和属性文法描述。
(2)描述算符优先分析方法的思想。
(3)给出中间代码序列的结构设计。
(4)完成相应的词法分析、语法分析和语义分析程序设计。
(5)测试用例和测试结果。设计不同的测试用例以显示程序的各种功能,包括复杂四则算术表达式的计算过程。并记录测试结果。
词法分析师编译的第一个阶段,它的主要任务是从左至右逐个字符地对源程序进行扫描,产生一个个单词序列,用以语法分析。执行词法分析的程序成为词法分析程序或扫描程序。词法分析器的工作是低级别的分析:将字符或者字符序列转化成记号.。在谈论词法分析时,使用术语“词法记号”(简称记号)、“模式”和“词法单元”表示特定的含义。在分析时,一是把词法分析器当成语法分析的一部分,另一种是把词法分析器当成编译程序的独立部分。在前一种情况下,词法分析器不断地被语法分析器调用,每调用一次词法分析器将从源程序的字符序列拼出一个单词,并将其Token值返回给语法分析器。后一种情况则不同,词法分析器不是被语法分析器不断地调用,而是一次扫描全部单词完成编译器的独立一遍任务。
程序设计语言的大慈符号一般可分成下列五种:
(1)关键字,也称基本字,如C语言中的struct,if,else,int,double和for等。
(2)标识符,用来表示各种名字,如常量名,变量名等。
(3)常数,各种类型的常数,如10,3.14,FAULSE,‘CBA’等。
(4)运算符,如‘+’、‘-’、‘*’、‘/’等。
(5)界符,如‘,’,‘;’,‘{’,’}’等。
语法分析师编译程序的核心部分。语法分析的作用是识别有词法分析给出的单词符号序列是否是给定文法的正确句子(程序),目前语法分析常用的方法有自顶向下分析和自顶向上分析两大类。自顶向下分析包括确定分析和不确定分析,自底向上分析又包括算符优先分析和LR分析。
自底向上分析,也称移进-规约分析,粗略地说它的实现思想是对输入符号串自左向右进行扫描,并将输入符逐个移入一个后进先出栈中,边移入变分析,一旦栈顶符号串形成某个句型的句柄或可贵月串时(该句柄或可归约串对应某产生式的右部),就用该产生式的做不非终结符代替相应右部的文法符号串,这称为一步规约。重复这一过程直到归约到栈中只剩文法的开始符号时则为分析成功,也就确认输入串时文法的句子。
通常在算术表达式求值过程中,运算次序是先乘除后加减,这说明了乘除运算的优先级高于加减运算的优先级,乘除为同一优先级但运算符在前面的先做,这称为左结合,同样加减运算也是如此,这也说明了运算的次序只与运算符有关,而与运算对象无关,因而直观算符优先分析法的关键是对一个给定文法G,人为地规定其算符的优先顺序,即给出优先级别和同一个级别中的结合性质,算符间的优先关系表示与简单优先关系的笔试类似。
编译程序的任务是吧源程序翻译成目标程序,这个目标程序必须和源程序的语义等同,也就是说,尽管他们的语法分析结构完全不同,但它们所表达的结果应完全相同。通常,在词法分析程序和语法分析程序对源程序的语法结构进行分析之后,可以有语法分析程序直接调用相应的语义子程序进行语义分析,也可以首先生成语法书或该结构的某种表示,再进行语义处理。
编译中的语义处理是指两个功能:第一,审查每个语法结构的静态语义,即验证语法结构合法的程序是否真正哟意义。又是把这个工作成为静态语义分析或静态审查。第二,如果静态语义正确,语义处理的工作是要执行真正的翻译,即生成程序的一种中间表示形式(中间代码),或者生成实际的目标代码。
有的编译程序直接生成目标代码,有的编译程序采用中间代码。中间代码,也称中间语言,是复杂性介于源程序语言和机器语言的一种表示形式。一般,快速编译程序直接生成目标代码,没有将中间代码翻译成目标代码的额外开销。但是为了使编译程序结构在逻辑上更为简单明确,常采用中间代码,这样可以将与机器相关的某些实现细节置于代码生成阶段仔细处理,并且可以在中间代码一级进行优化工作是的代码优化比较容易实现。
属性,涉及的概念比较广泛,常用以描述十五或人的特征、性质、品质等。比如,谈到一个物体,可以用“颜色”描述它,谈起某人,可以使用“有幽默感”来形容他。对编译程序使用的语法树的特点,可以用“类型”、“值”或“存储位置”来描述它。
形式上讲,一个属性文法是一个三元组,A=(G,V,F),一个上下文无关文法G;一个属性的有穷集V关于属性的锻压或为此的有穷集F。每个属性与文法的某I个非终结符或终结符相联。每个断言与文法的某产生式相联。如果对G中的某一输入串而言(句子),A中的所有断言对该输入串的语法树节点的属性全为真,则该串也是A语言中的句子。编译程序的静态语义审查工作就是验证关于所便宜的程序的断言是否全部为真。
属性文法最早出自克努特(D.E.Knuth)笔下。他把属性分成两类:继承属性综合属性,对属性值得概念和计算都给予很多论述。我们不对属性文法进行理论上的研究而仅仅将它作为工具描述语义分析。在编译的许多实际应用中,属性和断言以多种形式出现,也就是说,与每个文法符号相联的可以是各种属性、断言、以及语义规则,或者某种程序设计语言的程序段等。
通常在算术表达式求值过程中,运算次序是先乘除后加减,这说明了乘除运算的优先级高于加减运算的优先级,乘除为同一优先级但运算符在前面的先做,这称为左结合,同样加减运算也是如此,这也说明了运算的次序只与运算符有关,而与运算对象无关,因而直观算符优先分析法的关键是对一个给定文法G,人为地规定其算符的优先顺序,即给出优先级别和同一个级别中的结合性质,算符间的优先关系表示与简单优先关系的笔试类似。
由于算符优先分析法去掉了单非终结符之间的归约,尽管在分析过程中,当决定是否为句柄时采取一些检查措施,但仍难完全避免把错误的句子得到正确的归约。
G[E]: (1)E→E+T∣E-T∣T|i
(2)T→T*F∣T/F∣F
(3)F→(E)∣i
说明:终结符号i 为用户定义的简单变量,即标识符的定义。
struct info{
char left; //文法左部
vector<string> right; //文法右部
vector<char> first; //FIRSTVT集
vector<char> last; //LASTVT集
};
算符优先矩阵采用二维字符数组表示的:
char mtr[9][9]; //算符优先矩阵
4.3.1获取文法
Void get()函数用于获取预先设定好的文法,若文法获取成功,则给予频幕显示。
4.3.2打印文法
Void print()函数用于打印出已成功获取的文法,在成功获取文法的前提下,基于文法的频幕显示。
4.3.3求FIRSTVT集和LASTVT集
Void fun()函数用于求解FIRSTVT集和LASTVT集,具体求解过程依照其定义,采用分类讨论的思想分情况一一求解后综合。
4.3.4求算符优先矩阵
Void matrix()函数用于求解算符优先矩阵,具体求解方法也遵从定义,对于终结符在前,非终结符在后的情形,对应填入《,反之,填入》,对于非终结符在两个终结符之间或是两个终结符紧挨的情形,对应填入=。
4.3.5测试文法
void test()函数用于测试用例,对于输入合法的算术表达式,给予其三元式的中间过程打印与频幕。对于输入非法的算术表达式,基于报错。
4.3.6比较两个运算符的优先级
Int cmp(char a,char b)函数用于比较两个运算符的优先级,对于两个运算符,在算符优先矩阵中查找为》,则前者优先级高于后者,返回1;若在算符优先矩阵中查找为《,则前者优先级低于后者,返回-1;若为=,则两运算符优先级相同,返回0。
4.3.7打印出三元式
void out(char now,int avg1,int avg2)函数用于打印出每一步计算的三元式,格式为:运算符,操作数,操作数。
4.3.8定义四元式计算方法
int ope(char op,int a,int b)函数用于定义四元式的计算方法,具体方法为,操作数a与操作数b进行op运算。
4.4.1文法载入运行结果
4.4.2文法打印结果
4.4.3求解FIRSTVT集和LASTVT集
4.4.4求解算符优先矩阵
4.4.5算术表达式1+(4-2)*5+6/3的测试
本次实验完成了算符优先分析方法生成三元式的中间代码,从测试用例来看,实验较为成功,对于输入的算术表达式(甚至是较长较复杂的算术表达式),都能正确显示其三元式。其FIRSTVT集和LASTVT集的求解和算符优先分析矩阵的求解均是正确的,且较为严谨。但程序还是存在着一些不足,如程序对词法的分析结果还较为欠缺,输入不当有时会导致编译器死机。也就是说,程序的容错性还有待加强。
这次实验的程序代码有接近500行,这对于自身来说还算是一个较大的挑战,因为之前很少有机会接触到这么庞大的工程,整个设计过程就用时不少,好在对编译原理的理论知识学习较为扎实,在此前提下,用C++语言逐一实现各个功能,这个过程中,出现过很多的问题,有点问题的解决费时不少,但当解决之后也便是豁然开朗,在对三元式的求解过程中,遇到的麻烦更是棘手,但当运行通过后,对这些知识点的理解便更进一步了,知道了中间代码的具体生成过程。
在设计之初,我原本打算使用C语言编写该程序,实施一段时间过后才觉得实在是复杂至极,变连忙更换策略,用C++语言中的STL辅助帮忙,给设计过程带来了便捷。虽然之前对STL了解不是很深刻,但在经过这次的课程设计后,我深深感受到STL的强大,在这方面的提升也多了不少。
通过这次的课程设计,加强了我对编译原理的认识,理解了理论背后的实际问题,知道了理论知识在计算机学科中的重要性,没有理论支撑,就不会有各种各样夺人眼球的软件产品,没有扎实的理论知识作为支撑,就只能一直效仿,更谈不上什么创新。
这次的课程设计以及这学期对编译原理这门课的学习,使我真正领会到这门课程对于一个软件开发人员的重要性,曾经看过一句话:没有经历过归约编程的编程者是遗憾的。这也从另一面说明了编译原理的重要性,这是一门计算机科学的专业基础课程,也是软件开发者的必要先修课程。我们有理由用最真诚的心态去面对,在以后的科学研究和学习生活中,做到一丝不苟,要做到像编译器一样,对发现的任何语法错误,都能做出提醒,不断改进,不断进步!
[1]张素琴 吕映芝 蒋维杜 戴桂兰. 《编译原理》. 清华大学出版社. 2005年2月.
[2]阿霍. 《编译原理》. 机械工业出版社. 2009年1月.
[3]劳顿. 《编译原理及实践》. 机械工业出版社. 2004年2月.
[4]Peter Van Der Linden. 《C专家编程》. 人民邮电出版社. 2008年2月.
[5]Stephen Prata. 《C++ Primer Plus》. 人民邮电出版社. 2012年7月.
#include <iostream>
#include <string>
#include <VECTOR>
#include <stack>
using namespace std;
struct info{
char left;
vector<string> right;
vector<char> first;
vector<char> last;
};
vector<info> lang;
char mtr[9][9]; //算符优先矩阵
stack<char> sta;
void get(); //获取文法
void print(); //打印文法
void fun(); //求FirstVT 和 LastVT
void matrix(); //求算符优先矩阵
void test(); //测试文法
int cmp(char a,char b); //比较两个运算符的优先级
void out(char now,int avg1,int avg2); //打印三元式
int ope(char op,int a,int b); //定义三元式计算方法
int main(){
int choose;
cout<<"载入文法请按:1:"<<endl;
cin>>choose;
while(choose!=0){
switch(choose){
case 1: get(); break;
case 2: print(); break;
case 3: fun(); break;
case 4: matrix(); break;
case 5: test(); break;
default:break;
}
cin>>choose;
}
return 0;
}
void get(){
info temp,temp1,temp2;
temp.left = 'E';
temp.right.push_back("E+T");
temp.right.push_back("E-T");
temp.right.push_back("T");
temp.right.push_back("i");
temp1.left = 'T';
temp1.right.push_back("T*F");
temp1.right.push_back("T/F");
temp1.right.push_back("F");
temp2.left = 'F';
temp2.right.push_back("(E)");
temp2.right.push_back("i");
lang.push_back(temp);
lang.push_back(temp1);
lang.push_back(temp2);
cout << "————————————————————" << endl;
cout << " 文法获取完成" << endl;
cout << "————————————————————" << endl;
cout << endl;
cout<<"显示文法请按:2"<<endl;
}
void print(){
cout << "****************************************" << endl;
for(int i = 0;i < lang.size();i ++){
for(int j = 0;j < lang[i].right.size();j ++){
cout << lang[i].left << " --> ";
cout << lang[i].right[j] << endl;
}
}
cout << "****************************************" << endl;
cout << endl;
cout<<"构造FIRSTVT集和FOLLOWVT集合请按:3"<<endl;
}
//FIRSTVT:B=>b......
void fun(){
int i,j,sign = 0,sign1 = 0;
for(i = 0;i < lang.size();i ++){
for(j = 0;j < lang[i].right.size();j ++){
string temp = lang[i].right[j]; //获取右部
if(temp[0] > 'Z' || temp[0] < 'A'){ //终结符
lang[i].first.push_back(temp[0]);
}
else if(temp.length() >= 2){ //终结符
if(temp[1] > 'Z' || temp[1] < 'A'){
lang[i].first.push_back(temp[1]);
}
}
}
}
//LASTVT:B=>.....a
for(i = 0;i < lang.size();i ++){
for(j = 0;j < lang[i].right.size();j ++){
string temp = lang[i].right[j]; //获取右部
if((temp[0] > 'Z' || temp[0] < 'A') && temp.length() == 1){ //终结符
lang[i].last.push_back(temp[0]);
}
else if(temp.length() >= 3){ //终结符
if(temp[1] > 'Z' || temp[1] < 'A')
lang[i].last.push_back(temp[1]);
else if(temp[2] > 'Z' || temp[2] < 'A') //终结符
lang[i].last.push_back(temp[2]);
}
}
}
//FIRSTVT:B=>Cb......
while(sign == 0){ //迭代FirstVT
sign = 1;
for(i = 0;i < lang.size();i ++){
for(j = 0;j < lang[i].right.size();j ++){
string temp = lang[i].right[j]; //获取右部
if(temp.length() == 1 && (temp[0] <= 'Z' && temp[0] >= 'A')){//可以迭代
for(int k = 0;k < lang.size();k ++){
if(lang[k].left == temp[0]){ //找到了,添加元素
for(int p = 0;p < lang[k].first.size();p ++){
sign1 = 0;
char ch = lang[k].first[p];
for(int q = 0;q < lang[i].first.size();q ++){
if(lang[i].first[q] == ch){ //包含了
sign1 = 1;
}
}
if(sign1 == 0){
lang[i].first.push_back(ch);
sign = 0;
}
}
}
}
}
}
}
}
//LASTVT:B=>......aC
sign = 0;
while(sign == 0){ //迭代LastVT
sign = 1;
for(i = 0;i < lang.size();i ++){
for(j = 0;j < lang[i].right.size();j ++){
string temp = lang[i].right[j]; //获取右部
if(temp.length() == 1 && (temp[0] <= 'Z' && temp[0] >= 'A')){//可以迭代
for(int k = 0;k < lang.size();k ++){
if(lang[k].left == temp[0]){ //找到了,添加元素
for(int p = 0;p < lang[k].last.size();p ++){
sign1 = 0;
char ch = lang[k].last[p];
for(int q = 0;q < lang[i].last.size();q ++){
if(lang[i].last[q] == ch){ //包含了
sign1 = 1;
}
}
if(sign1 == 0){
lang[i].last.push_back(ch);
sign = 0;
}
}
}
}
}
}
}
}
cout << "……………………………………………………" << endl;
cout << "FirstVT:" << endl;
for(i = 0;i < lang.size();i ++){
cout << lang[i].left << " : ";
for(j = 0;j < lang[i].first.size();j ++){
cout << lang[i].first[j] << " ";
}
cout << endl;
}
cout << endl;
cout << "LasttVT:" << endl;
for(i = 0;i < lang.size();i ++){
cout << lang[i].left << " : ";
for(j = 0;j < lang[i].last.size();j ++){
cout << lang[i].last[j] << " ";
}
cout << endl;
}
cout << "……………………………………………………" << endl;
cout << endl;
cout<<"构造优先关系矩阵请按:4"<<endl;
}
void matrix(){
int i,j;
for(i = 0;i < 9;i ++){ //初始化
for(j = 0;j < 9;j ++){
mtr[i][j] = 'n';
}
}
string temp = "+-*/()i#";
for(i = 1;i < 9;i ++){
mtr[i][0] = temp[i - 1];
mtr[0][i] = temp[i - 1];
}
vector<string> str;
//aU==>a < FirstVT(U)
for(i = 0;i < lang.size();i ++){ //aU a < FirstVT(U)
for(j = 0;j < lang[i].right.size();j ++){
string ss = lang[i].right[j];
string ok = "";
if(ss.length() > 2){
if((ss[0] > 'Z' || ss[0] < 'A') && (ss[1] <= 'Z' && ss[1] >= 'A')){ //aU
ok = "";
ok += ss[0];
ok += ss[1];
str.push_back(ok);
}
if((ss[1] > 'Z' || ss[1] < 'A') && (ss[2] <= 'Z' && ss[2] >= 'A')){ //aU
ok = "";
ok += ss[1];
ok += ss[2];
str.push_back(ok);
}
}
}
}
//找到a和FIRSTVT(U)
for(i = 0;i < str.size();i ++){
for(j = 1;j < 9;j ++){
if(mtr[j][0] == str[i][0]){
for(int k = 0;k < lang.size();k ++){
if(lang[k].left == str[i][1]){
for(int p = 0;p < lang[k].first.size();p ++){
for(int q = 1;q < 9;q ++){
if(mtr[q][0] == lang[k].first[p]){
mtr[j][q] = '<';
}
}
}
}
}
}
}
}
//Ua==>LastVT(U) > a
str.clear();
for(i = 0;i < lang.size();i ++){
for(j = 0;j < lang[i].right.size();j ++){
string ss = lang[i].right[j];
string ok = "";
if(ss.length() > 2){
if((ss[1] > 'Z' || ss[1] < 'A') && (ss[0] <= 'Z' && ss[0] >= 'A')){ //Ua
ok = "";
ok += ss[0];
ok += ss[1];
str.push_back(ok);
}
if((ss[2] > 'Z' || ss[2] < 'A') && (ss[1] <= 'Z' && ss[1] >= 'A')){ //Ua
ok = "";
ok += ss[1];
ok += ss[2];
str.push_back(ok);
}
}
}
}
//找到a和LASTVT(U)
for(i = 0;i < str.size();i ++){
for(j = 1;j < 9;j ++){
if(mtr[0][j] == str[i][1]){ //Find a Then Find LastVt(U)
for(int k = 0;k < lang.size();k ++){
if(lang[k].left == str[i][0]){ //Find U
for(int p = 0;p < lang[k].last.size();p ++){
for(int q = 1;q < 9;q ++){
if(mtr[0][q] == lang[k].last[p]){
mtr[q][j] = '>';
}
}
}
}
}
}
}
}
//aUb || ...ab... ==> a = b
str.clear();
for(i = 0;i < lang.size();i ++){ //ab aUb a = b
for(j = 0;j < lang[i].right.size();j ++){
string ss = lang[i].right[j];
string ok = "";
if(ss.length() > 2){
if((ss[1] > 'Z' || ss[1] < 'A') && (ss[0] > 'Z' || ss[0] < 'A')){ //aa
ok = "";
ok += ss[0];
ok += ss[1];
str.push_back(ok);
}
if((ss[2] > 'Z' || ss[2] < 'A') && (ss[1] > 'Z' || ss[1] < 'A')){ //aa
ok = "";
ok += ss[1];
ok += ss[2];
str.push_back(ok);
}
if((ss[2] > 'Z' || ss[2] < 'A') && (ss[0] > 'Z' || ss[0] < 'A')){ //aUa
ok = "";
ok += ss[0];
ok += ss[2];
str.push_back(ok);
}
}
}
}
for(i = 0;i < str.size();i ++){
for(j = 1;j < 9;j ++){
if(str[i][0] == mtr[j][0]){
for(int k = 1;k < 9;k ++){
if(mtr[0][k] == str[i][1]){
mtr[j][k] = '=';
}
}
}
}
}
for(i = 0;i < lang[0].first.size();i ++){ //#
for(j = 1;j < 9;j ++){
if(lang[0].first[i] == mtr[0][j]){
mtr[8][j] = '<';
}
}
}
for(i = 0;i < lang[0].first.size();i ++){ //#
for(j = 1;j < 9;j ++){
if(lang[0].first[i] == mtr[j][0]){
mtr[j][8] = '>';
}
}
}
mtr[8][8] = '=';
cout << "========================================" << endl;
for(i = 0;i < 9;i ++){
for(j = 0;j < 9;j ++){
if(mtr[i][j] != 'n')
cout << mtr[i][j] << " ";
else
cout << " ";
}
cout << endl;
}
cout << "========================================" << endl;
cout << endl;
cout<<"文法测试请按:5"<<endl;
}
void test(){
cout << "----------------------------------------" << endl;
cout << "请输入算术表达式:" << endl;
string str;
cin >> str;
str += '#';
int i,j,k;
stack<int> data;
stack<char> op;
op.push('#');
char now = 'n'; //记录当前栈顶操作符
int sign = 0;
for(i = 0;i < str.length();i ++){
sign = 0;
if(str[i] >= '0' && str[i] <= '9'){ //操作数
int temp = str[i] - '0';
data.push(temp);
}
else{ //运算符
op.push(str[i]);
sign = 1;
}
if(now != 'n' && sign == 1){ //有可比性,并且操作符栈有更新
if(!op.empty()){
char top = op.top(); //栈顶元素
while(cmp(now,top) == 1){ //需要规约
int avg1 = data.top();
data.pop();
int avg2 = data.top();
data.pop();
out(now,avg2,avg1); //打印三元式
data.push(ope(now,avg2,avg1));
op.pop();
op.pop();
if(!op.empty()){
now = op.top();
}
else{
now = 'n';
}
op.push(top);
}
if(cmp(now,top) == 0){
op.pop();
op.pop();
if(!op.empty()){
now = op.top();
}
else{
char temp = '=';
if(!data.empty()){
int da = data.top();
out(temp,da,0);
}
}
}
}
}
else{ //不需要比较
if(!op.empty()){
now = op.top();
}
}
}
}
int cmp(char a,char b){
int i,j;
for(i = 1;i < 9;i ++){
if(mtr[i][0] == a){
for(j = 1;j < 9;j ++){
if(mtr[0][j] == b){
if(mtr[i][j] == '>'){
return 1;
}
else if(mtr[i][j] == '='){
return 0;
}
else if(mtr[i][j] == '<'){
return -1;
}
}
}
}
}
return 2;
}
void out(char now,int avg1,int avg2){
cout << "----------------------------------------" << endl;
cout << now << " ," << avg1 << " ," << avg2 << endl;
cout << endl;
}
int ope(char op,int a,int b){
if(op == '+'){
return a + b;
}
if(op == '-'){
return a - b;
}
if(op == '*'){
return a * b;
}
if(op == '/'){
return a / b;
}
if(op == '='){
return a;
}
return 0;
}