PL0 语言功能简单、结构清晰、可读性强,而又具备了一般高级程序设计语言的必须部分,因而 PL0 语言的编译程序能充分体现一个高级语言编译程序实现的基本方法和技术。
分析对象〈算术表达式〉的 BNF 定义如下:
<表达式> ::= [+|-]<项>{<加法运算符> <项>}
<项> ::= <因子>{<乘法运算符> <因子>}
<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’
<加法运算符> ::= +|-
<乘法运算符> ::= *|/
本次实验我采用的递归下降分析器的设计。递归下降分析法的原理是利用函数之间的递归调用来模拟语法树自上而下的构建过程。本次实验基于实验2的递归下降分析程序来实现两个功能,计算算术表达式的值和计算中间代码四元式,下面是递归下降分析器设计思想:
(1)对每个非终结符A构造一个函数过程,对A的每个继承属性设置一个形式参数,函数的返回值为A的综合属性。
(2)对于每个产生式按照从左到右的次序,对于单词符号做以下的工作:
a.对于带有综合属性x的终结符X,把x的值存入为X.x设置的变量中。然后产生一个匹配X的调用,并继续读入下一个输入符号。
b.对于每个非终结符号B,产生一个右边带有函数调用的赋值语句c=B(b1,b2,…,bk)
c.对于语义动作,把动作的代码抄进分析器中,用代表属性的变量来代替对应属性的每一次引用。
在写代码之前要先想好自己的翻译模式,不然就不够条理了。由于本实验都是表达式,虽然算术表达式和四元式的输出处理不一样,但是识别的过程还是一样的,所以可以简单的把每个非终结符设置为2个综合属性,即第一个运算数和第二个运算数,对于巴科斯范式{}用循环不断更新第一个参数即可。
这样算法在递归返回时就返回了对应的计算值或者参数名(四元式返回字符串,算术表达式返回计算值),最后输出即可。
算法的流程图就是在递归下降分析程序的基础上增添3块内容:一是词法分析程序,由于分析需要判断表达式是不是正确的,同时从词法分析结果分析更方便,所以要嵌入词法分析程序,但是需要做一点修改,将输出改为输出到全局数组中,方便语法分析调用。
二是计算算术表达式的代码,就是直接在之前的三个非终结符上添加内容,在识别的基础上计算出值,通过函数返回值传递结果。三是输出中间代码四元式,这个和计算算术表达式很像,但是得单独写出函数,因为返回值类型和输出都又所不同。
语言:c++
注:本算法由于调用了词法分析程序,为避免代码复杂,采用多文件方式组织,即有3个文件:main.cpp wordanalysis.h wordanalysis.cpp
main.cpp:
#include
#define plus 0 // +
#define minus 1 // -
#define times 2 // *
#define slash 3 // /
#define lparen 4 // (
#define rparen 5 // )
#define ident 6 // 标识符
#define number 7 // 无符号整数
#define finish 8 // 完成
#include
#include
#include "wordanalysis.h"
#include "wordanalysis.cpp"
using namespace std;
int sym;//当前的输入符号
int cou;//词法分析结果数组的大小
int coun=0;//当前分析到第几个编码
bool sign;//标志sym当前符号是否识别完毕
bool error;//判断分析成功与否
void advance();//读入下一单词符号
int expression();//表达式--递归下降子程序
int term();//项--递归下降子程序
int factor();//因子--递归下降子程序
string fexpression();//表达式--递归下降子程序--四元式
string fterm();//项--递归下降子程序--四元式
string ffactor();//因子--递归下降子程序--四元式
typedef struct Quaternary{//定义四元式
string op;
string arg1;
string arg2;
string result;
}quaternary;
quaternary quat[100];
int cquat;//指明上面数据添加到第几条
string newtemp(){//产生新变量名t1,t2等
static int tim=1;
string digits[10] = {"0","1","2","3","4","5","6","7","8","9"};
string s;
if(tim<10)s="t"+digits[tim];
else s="t"+digits[tim/10]+digits[tim%10];
//string str=to_string(tim);//codeblocks要在设置打开允许c++11操作
//string s="t"+str;
tim++;
return s;
}
void emit(string op,string arg1,string arg2,string result){//产生四元式用于显示
quat[cquat].op=op;
quat[cquat].arg1=arg1;
quat[cquat].arg2=arg2;
quat[cquat].result=result;
cquat++;
return;
}
void advance()//读入下一单词符号
{
sign=true;//上一个单词识别成功
if(coun<=cou)
{
sign=false;//表示当前读入的单词还未完成正确的识别
string keyword=word_result[coun][0];
if(keyword=="plus")sym=plus;
else if(keyword=="minus")sym=minus;
else if(keyword=="times")sym=times;
else if(keyword=="slash")sym=slash;
else if(keyword=="lparen")sym=lparen;
else if(keyword=="rparen")sym=rparen;
else if(keyword=="ident")sym=ident;
else if(keyword=="number")sym=number;
else sym=finish;//单词错误,分析结束
coun++;
}
else
{
//cout<<"分析结束!"<
sym=finish;
}
}
int expression()//表达式--递归下降子程序
{
string op;
int arg1,arg2,result;
bool isminus=false;
if((sym==plus)||(sym==minus))//表达式前面的符号可有可无,有时需要读入下一符号
{
if(sym==minus)isminus=true;
advance();
}
arg1=term();//获取第一个参数
if(isminus)arg1=-arg1;
while((sym==plus)||(sym==minus))//后面一段可以重复多次
{
op=word_result[coun-1][1];//获取符号
advance();
arg2=term();//获取第二个参数
if(op=="+")//进行加法运算
{
result=arg1+arg2;
arg1=result;
}else{
result=arg1-arg2;
arg1=result;
}
}
return arg1;
}
int term()//项--递归下降子程序
{
string op;
int arg1,arg2,result;
arg1=factor();
while((sym==times)||(sym==slash))//后面一段可以重复多次
{
op=word_result[coun-1][1];//获取符号
advance();
arg2=factor();
if(op=="*")//进行乘法运算
{
result=arg1*arg2;
arg1=result;
}else{
if(arg2==0)
{
cout<<"除数为0错误!"<<endl;
error=false;//分析报错
return 0;
}
result=arg1/arg2;
arg1=result;
}
}
return arg1;
}
int factor()//因子--递归下降子程序
{
int arg=0;
if(sym==ident)//单词为标识符
{
cout<<"算术表达式含有字母错误!"<<endl;
error=false;//分析报错
return 0;
}
else if(sym==number)//单词为无符号整数
{
arg=atoi(word_result[coun-1][1].c_str());//获取数字
advance();
}
else if(sym==lparen)//单词为左括号
{
advance();
arg=expression();
if(sym==rparen)advance();//右括号匹配
else
{
error=false;//分析报错
cout<<"右括号不匹配"<<endl;
}
}else
{
error=false;//分析报错
cout<<"符号不属于因子!"<<endl;
}
return arg;
}
string fexpression()//表达式--递归下降子程序
{
string op,arg1,arg2,result;
bool isminus=false;
if((sym==plus)||(sym==minus))//表达式前面的符号可有可无,有时需要读入下一符号
{
if(sym==minus)isminus=true;
advance();
}
arg1=fterm();//获取第一个参数
if(isminus)//取负
{
result=newtemp();//产生中间变量名,相当于对结果进行存储
emit("-",arg1,"-",result);//产生四元式,相当于进行加法或减法运算
}
while((sym==plus)||(sym==minus))//后面一段可以重复多次
{
op=word_result[coun-1][1];//获取符号
advance();
arg2=fterm();//获取第二个参数
result=newtemp();
emit(op,arg1,arg2,result);
arg1=result;
}
return arg1;
}
string fterm()//项--递归下降子程序
{
string op,arg1,arg2,result;
arg1=ffactor();
while((sym==times)||(sym==slash))//后面一段可以重复多次
{
op=word_result[coun-1][1];//获取符号
advance();
arg2=ffactor();
result=newtemp();
emit(op,arg1,arg2,result);
arg1=result;
}
return arg1;
}
string ffactor()//因子--递归下降子程序
{
string arg;
if(sym==ident)//单词为标识符
{
arg=word_result[coun-1][1];
advance();
}
else if(sym==number)//单词为无符号整数
{
arg=word_result[coun-1][1];//获取数字
advance();
}
else if(sym==lparen)//单词为左括号
{
advance();
arg=fexpression();
if(sym==rparen)advance();//右括号匹配
else
{
error=false;//分析报错
cout<<"右括号不匹配"<<endl;
}
}else
{
error=false;//分析报错
cout<<"符号不属于因子!"<<endl;
}
return arg;
}
int main()
{
error=true;//默认分析成功
cou=word_analysis();//词法分析一个句子-(((as+23)*3-5)+iu)/(4-fg*7)
if(!success)
{
cout<<"表达式有误!"<<endl;
return 0;
}
// cout<<"con="<
// for(int i=0;i<=con;i++)
// cout<
// int u;
// cin>>u;
int pattern=iscaculate();//判断是算术表达式还是四元式,值为1表示算术表达式
if(pattern==1)
{
advance();
int result=expression();//表达式是起始分析点
if(error&&sign)cout<<result<<endl;
else cout<<"error!"<<endl;
}
else{
advance();
fexpression();//表达式是起始分析点
if(error&&sign)
for(int i=0;i<cquat;i++){
cout<<'('<<quat[i].op<<','<<quat[i].arg1<<','<<quat[i].arg2<<','<<quat[i].result<<')'<<endl;;
}
else cout<<"error!"<<endl;
}
return 0;
}
wordanalysis.h:
#ifndef WORDANALYSIS_H_INCLUDED
#define WORDANALYSIS_H_INCLUDED
#include
#include
#include
#include
using namespace std;
extern string word_result[200][2];//保存词法分析后的数据:编码-单词
int con;//计数上边的数据
extern bool success;//标志表达式是否正确
void initialization_map();
bool isNumber(char ch);//是否数字
bool isCase(char ch);//是否字母
bool isCaculationSymbol(char ch);//是否运算符
bool isBandSymbol(char ch);//是否边界符
void getcode(char *str);//从cin读取文件
void calulationString(char *str);//运算符处理
void bandString(char *str);//边界符处理
void analysis(const char *InputFileName, char *str);//词法分析程序
int word_analysis();//供外界调用的接口
int iscaculate();//判断是算术表达式还是四元式,值为1表示算术表达式
bool caculate=true;//默认是算术表达式
#endif // WORDANALYSIS_H_INCLUDED
wordanalysis.cpp:
#include "wordanalysis.h"
map<string,string> Keywords;//使用map来存储保留字和编码
void initialization_map()
{
Keywords["begin"]="beginsym";
Keywords["call"]="callsym";
Keywords["const"]="constsym";
Keywords["do"]="dosym";
Keywords["end"]="endsym";
Keywords["if"]="ifsym";
Keywords["odd"]="oddsym";
Keywords["procedure"]="proceduresym";
Keywords["read"]="readsym";
Keywords["then"]="thensym";
Keywords["var"]="varsym";
Keywords["while"]="whilesym";
Keywords["write"]="writesym";
}
string word_result[200][2]={"0"};//保存词法分析后的数据:编码-单词
bool success=false;//标志表达式是否正确
//判断该位置的符号是否是数字
bool isNumber(char ch) {
if(ch>='0' && ch<='9') {
return true;
} else {
return false;
}
}
//判断该位置的符号是否是字母
bool isCase(char ch) {
if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))return true;
else return false;
}
//判断该位置是否是运算符的基本单位
bool isCaculationSymbol(char ch)
{
if(ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='>'||ch=='<'||ch=='='||ch=='#'||ch==':')return true;
else return false;
}
//判断该位置是否是边界符
bool isBandSymbol(char ch)
{
if(ch=='('||ch==')'||ch==','||ch==';'||ch=='.')return true;
else return false;
}
//连续的运算符处理
void calulationString(char *str)
{
int a=strlen(str);
for(int i=0;i<a;i++)
{
if(str[i]=='+') {word_result[con][0]="plus";word_result[con][1]="+";con++;}
else if(str[i]=='-') {word_result[con][0]="minus";word_result[con][1]="-";con++;}
else if(str[i]=='*') {word_result[con][0]="times";word_result[con][1]="*";con++;}
else if(str[i]=='/') {word_result[con][0]="slash";word_result[con][1]="/";con++;}
else if(str[i]=='=') {word_result[con][0]="eql";word_result[con][1]="=";con++;}
else if(str[i]==':')//如果i位置是 ':',i+1位置不是 '=',那么该符号非法
{
if((i+1)<a&&str[i+1]=='=')
{
word_result[con][0]="becomes";word_result[con][1]=":=";con++;
i++;
} else {word_result[con][0]="error";word_result[con][1]=str[i];con++;success=false;}
}else if(str[i]=='>')
{
if((i+1)<a&&str[i+1]=='=')
{
word_result[con][0]="geq";word_result[con][1]=">=";con++;
i++;
}else {word_result[con][0]="gtr";word_result[con][1]=">";con++;}
}else if(str[i]=='<')
{
if((i+1)<a&&str[i+1]=='=')
{
word_result[con][0]="leq";word_result[con][1]="<=";con++;
i++;
}else {word_result[con][0]="lss";word_result[con][1]="<";con++;}
}else {word_result[con][0]="error";word_result[con][1]=str[i];con++;success=false;}
}
}
//获取一段连续的边界符号后,依次将其分解开
void bandString(char *str)
{
int i,k=strlen(str);
for( i=0;i<k;i++)
{
switch(str[i])
{
case '(':
word_result[con][0]="lparen";word_result[con][1]="(";con++;
break;
case ')':
word_result[con][0]="rparen";word_result[con][1]=")";con++;
break;
case ',':
word_result[con][0]="comma";word_result[con][1]=",";con++;
break;
case ';':
word_result[con][0]="semicolon";word_result[con][1]=";";con++;
break;
case '.':
word_result[con][0]="period";word_result[con][1]=".";con++;
break;
default:
break;
}
}
}
//词法分析函数
void analysis(char *str)
{
int length=strlen(str),i=0,j,templength,c,d=0;
char tempStr[100],smallStr[100];//存储一个单词,每个单词的长度是有限的
while(i<length)
{
j=0;
while(str[i]==' '&&i<length)//去掉开头的空格符号
{
i++;
}
while(str[i]!=' '&&i<length)//获取两个空格之间的一段代码字符串
{
tempStr[j++]=str[i++];
}
tempStr[j]='\0';
templength=strlen(tempStr);
c=0;
while(c<templength)
{
if(isCase(tempStr[c]))//如果以字母开头
{
while((!isCaculationSymbol(tempStr[c]))&&(!isBandSymbol(tempStr[c]))&&c<templength)//获取全是字母和数字的一段串
{
smallStr[d++]=tempStr[c++];
}
smallStr[d]='\0';
map<string,string>::iterator iter;
iter=Keywords.find(string(smallStr));//find函数返回一个指向数据的迭代器,未找到时指向end
if(iter!=Keywords.end())//如果字符串是保留字
{
word_result[con][0]=iter->second;word_result[con][1]=iter->first;con++;
strcpy(smallStr,"");//将这两个临时存储结构清空
d=0;
}
else//字符串是标识符
{
word_result[con][0]="ident";word_result[con][1]=smallStr;con++;
strcpy(smallStr,"");
d=0;
}
}
else if(isNumber(tempStr[c]))//如果以数字开头
{
while(isNumber(tempStr[c])&&c<templength)//截取全是数字的一段字符串
{
smallStr[d++]=tempStr[c++];
}
smallStr[d]='\0';
word_result[con][0]="number";word_result[con][1]=smallStr;con++;
strcpy(smallStr,"");
d=0;
}
else if(isCaculationSymbol(tempStr[c]))//以运算符开头
{
while(isCaculationSymbol(tempStr[c])&&c<templength)
{
smallStr[d++]=tempStr[c++];
}
smallStr[d]='\0';
calulationString(smallStr);//处理连续的运算符
strcpy(smallStr,"");
d=0;
}
else if(isBandSymbol(tempStr[c]))//如果以边界符开头
{
while(isBandSymbol(tempStr[c])&&c<templength)
{
smallStr[d++]=tempStr[c++];
}
smallStr[d]='\0';
bandString(smallStr);//处理连续的界限符
strcpy(smallStr,"");
d=0;
}
else
{
success=false;
word_result[con][0]="error";word_result[con][1]=tempStr[c];con++;
c++;
}
}
strcpy(tempStr,"");
}
}
int word_analysis()
{
success=true;
initialization_map();//初始化
string word;//固定输入为一行的无空格表达式
cin>>word;
char *str=(char*)word.c_str();//以字符的方式存放文件的数据
for(int i=0;str[i]!='\0';i++)
{
if(isCase(str[i]))
{
caculate=false;
break;
}
}
analysis(str);
return con-1;//这个数是词法分析结果条数-1.可直接做下角标
}
int iscaculate()//判断是算术表达式还是四元式,值为1表示算术表达式
{
if(caculate)return 1;
else return 0;
}
说明:
1、在实际提交中发现c++不能使用多文件,系统一直报“重复定义”错误,必须合到一起才可以成功提交。
2、算法有输入只有一行,支持开头符号,大整数,长标识符。
3、算法会自动识别是算术表达式还是输出四元组。
输入输出示例1:-(((as+23)*3-5)+iu)/(4-fg*7)
输入输出示例2:-2*((45+23)/3-5)/0/(4-5*7)
还有一些其他检测错误的地方这里不展示了。
输入输出示例3:-2*((45+23)/3-5)-0/(4-5*7)