实验三. 自下而上语法分析
1. 实验目的
(1) 给出 PL/0 文法规范,要求编写 PL/0 语言的语法分析程序。
(2) 通过设计、编制、调试一个典型的自下而上语法分析程序,实现对 词法分析程序所提供的单词序列进行语法检查和结构分析,进一步 掌握常用的语法分析方法。
(3) 选择最有代表性的语法分析方法,如算符优先分析法、LR 分析法; 或者调研语法分析器的自动生成工具 YACC 的功能与工作原理,使用 YACC 生成一个自底向上的语法分析器。
2. 实验准备
微机安装好 C 语言,或 C++,或 Visual C++,或自己需要用的语言.
3. 实验内容
已给 PL/0 语言文法,构造表达式部分的语法分析器。
4. 实验要求
(1) 将实验一“词法分析”的输出结果,作为表达式语法分析器的输入,进行语法解析,对于语法正确的表达式,报告“语法正确”;对于语法错误的表达式,报告“语法错误”,指出错误原因。
(2) 把语法分析器设计成一个独立一遍的过程。
(3) 采用递归下降分析法或者采用预测分析法实现语法分析。
5. 设计思想
(1)算符优先分析算法
进行算符优先分析算法时需要构造优先关系表,构造优先关系表至为关键的一步便是构造FIRSTVT和LASTVT。
(2)G(<表达式>):
<表达式> ::= [+|-]<项>{<加法运算符> <项>}
<项> ::= <因子>{<乘法运算符> <因子>}
<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’
<加法运算符> ::= +|-
<乘法运算符> ::= |/
<关系运算符> ::= =|#|<|<=|>|>=
<标识符> ::=<字母>{<字母>|<数字>}
<无符号整数> ::= <数字>{<数字>}
<字母> ::= a|b|…|X|Y|Z
<数字> ::= 0|1|…|8|9
常见的文法表示方式:
G(E):
E-> +TK|-TK|TK
K-> AT|ε
T-> FN
N-> MF|ε
A -> +|-
M -> |/
R -> =|#|<|<=|>|>=
D -> 0|1|2|3|4|5|6|7|8|9
Z -> a|b|c|d|e|f|…|x|y|z
P -> DP|D
F -> Z|ZF|FD
由于用词法分析的结果作为输入,为编程方便,没有细分标识符和无符号整数。
a.此文法无公共左因子,也无需消除左递归
b.FIRSTVT集合:
FIRSTVT (表达式)={+,-, 标识符,无符号整数,( ,,/}
FIRSTVT (项)= {标识符,无符号整数,(,,/ }
FIRSTVT (因子)= {标识符,无符号整数,( }
FIRSTVT (加法运算符)= {+,-}
FIRSTVT (乘法运算符)={ ,/}
FIRSTVT (关系运算符)={=,#,<,<=,>,>= }
LASTVT集合:
LASTVT (表达式)={+,-, 标识符,无符号整数,),,/ }
LASTVT (项)= {标识符,无符号整数,),*,/}
LASTVT (因子)= {标识符,无符号整数,)}
LASTVT (加法运算符)= {}
LASTVT (乘法运算符)={}
LASTVT (关系运算符)={}
由b.可构造算符优先表如下:
(3)子程序算法:(本算法没有细致的划分标识符和无符号整数,直接用上一次实验的词法分析所得结果提取单词编码进行输出判断,在itc上用的cin作为输入)
//FIRSTVT
PROCEDURE INSERT(P,a);
IF NOT F[P,a] THEN
BEGIN
F[P,a] := TRUE
PUSH[P,a] ONTO STACK
END
//主程序
BEGIN
FOR 每一个非终结符P和终结符a
DO F[P,a] := FALSE;
FOR 每个形如 P->a… 或 P->Qa…的产生式
DO INSERT(P,a)
WHILE STACK非空 DO
BEGIN
把STACK的顶项记为(Q,a)弹出去
FOR 每个形如 P->Q…的产生式 DO
INSERT(P,a)
END OF WHILE;
END
//LASTVT
PROCEDURE INSERT(P,a);
IF NOT F[P,a] THEN
BEGIN
F[P,a] := TRUE
PUSH[P,a] ONTO STACK
END
//主程序
BEGIN
FOR 每一个非终结符P和终结符a
DO F[P,a] := FALSE;
FOR 每个形如 P->…a 或 P->…aQ的产生式
DO INSERT(P,a)
WHILE STACK非空 DO
BEGIN
把STACK的顶项记为(Q,a)弹出去
FOR 每个形如 P->…Q的产生式 DO
INSERT(P,a)
END OF WHILE;
END
//优先关系表
FOR 每个产生式 P->X1X2…Xn DO
FOR i := 1 TO n-1 DO
BEGIN
IF Xi和X(i+1)均为终结符
THEN 置 Xi优先级等于X(i+1);
IF i<=n-2 且Xi和X(i+2)都为终结符,但X(i+1)为非终结符
THEN 置 Xi优先级等于X(i+2);
IF Xi为终结符而X(i+1)为非终结符
THEN FOR FIRSTVT(X(i+1))中的每个a DO 置 Xi优先级低于b;
IF Xi为非终结符而X(i+1)为终结符
THEN FOR LASTVT(Xi)中的每个a DO 置 a优先级高于X(i+1);
END
//算符优先
k = 1; S[k] = ‘#’;
REPEAT
把下一个输入符号读进a中;
IF S[k] 属于 VT THEN j = k ELSE j = k-1;
While S[j] > a DO
BEGIN
REPEAT
Q = S[j];
IF S[j-1] 属于 VT THEN j = j-1 ELSE j = j-2
UNTIL S[j] < Q;
把S[j+1]…S[k]归约为某个N;
k = j+1;
S[k] = N;
END OF WHILE;
IF S[j] < a OR S[j] = a THEN
BEGIN k = k+1; S[k] = a END
ELSE ERROR /调用出错诊察程序/
UNTIL a = ‘#’
(4)算法流程图
(5)输入输出源程序
#include<fstream>
#include<cstring>
#include<string>
#include<fstream>
#include<sstream>
#include<iostream>
#include<map>
#include<bits/stdc++.h>
#define Max 9//MAX根据优先关系表得出
using namespace std;
map<string,int> order;//定义终结符在优先表中的序号
stack<string> symbols,save;//定义两个栈,其中symbols为符号栈,save是用来存储临时变量的
vector<string> Terminator;//使用vector迭代器减少空间的使用,用于数组,操作方便,存放终结符的符号
vector<string>::iterator judge;//使用vector迭代器判断是否为终结符
string sequence[1000];//待输入的符号串
string str;//从文本中读入的字符串
string sym;//当前的符号
map<string,string> Word;//使用map数据结构实现key-value对应
std::map<string,string>::iterator it;//用来遍历key-value对应关系的迭代器
string pointer; //指针
void map_init();//key-value(单词-编码)对应关系进行初始化
void lexicalAnalysis();//词法分析过程
int flag=0;
int number=0;
ifstream file("./2.txt");
int priorityTable[Max][Max]={{0,0,1,1,1,1,1,2,2},//算符优先表,其中0表示“=”,1表示“<”,2表示“>",3表示空
{0,0,1,1,1,1,1,2,2},//这张表是在写程序之前根据文法计算出来的,在实现报告中显示
{2,2,0,0,1,1,1,2,2},
{2,2,0,0,1,1,1,2,2},
{2,2,2,2,0,3,3,2,2},
{2,2,2,2,3,0,3,2,2},
{1,1,1,1,1,1,1,0,2},
{2,2,2,2,3,3,0,2,2},
{1,1,1,1,1,1,1,1,0}};
void init(){//初始化定义终结符的序列
order["plus"]=0;
order["minus"]=1;
order["times"]=2;
order["slash"]=3;
order["ident"]=4;
order["number"]=5;
order["lparen"]=6;
order["rparen"]=7;
order["#"]=8;
for(int i=0;i<9;i++){
Terminator.push_back("plus");
Terminator.push_back("minus");
Terminator.push_back("times");
Terminator.push_back("slash");
Terminator.push_back("ident");
Terminator.push_back("number");
Terminator.push_back("lparen");
Terminator.push_back("rparen");
Terminator.push_back("#");
}
}
int judgePriority(string a,string b){//从算符优先表中判断两个string符号的优先级,以便在算符优先分析函数中对不同关系做出不同的算法分析
int row,column;//行列对应的符号,与sequence对应
row=order[a];
column=order[b];
if(priorityTable[row][column]==0){//当前关系为“=”
return 0;
}else if(priorityTable[row][column]==1){//当前关系为“<”
return 1;
}else if(priorityTable[row][column]==2){//当前关系为“>”
return 2;
}else return 3;//当前关系为空
}
int advance(){//表示指针的移动
//getline(infile,str);
number++;
int found;
cin>>str;
if(number>7){//用于文末终结符的结束 错误判断
flag++;
if(flag==1){
sym="#";
return 1;
}
else if(flag>1){
return 0;
}
}
found=str.find(',',0);//找“,”的位置
sym=str.substr(1,found-1);//指定位置复制子字符串,比如(lparen,() --pointer为lparen
return 1;
}
int SearchTerminator(string c){//判断是不是终结符
judge=find(Terminator.begin(),Terminator.end(),c);//用find函数从终结符开始到结束逐个查找是否有c
if(judge == Terminator.end()){
return 0;//末尾是“#”,指的是分析结束
}else{
return 1;
}
}
int OperatorAnalysis(){//算符优先分析,由p93得
int k=1,j=0;//初始:深度为1
string b,q;
symbols.push("#");//符号栈压入"#"
while(advance()){//把输入串中下一个字符读进sym中;
if(SearchTerminator(symbols.top())){//如果栈顶的字符为终结符
j=k;
q=symbols.top();//将q指向栈顶
}else{//不是非终结符
j=k-1;//从当前栈,指针向下移动一个
b=symbols.top();//将b指向栈顶
symbols.pop();
save.push(b);//放在b中等待规约
q=symbols.top();//栈顶为指针向下移动一个
}
while(judgePriority(q,sym)==2){//优先关系while(symbols[j]>a)
if(!SearchTerminator(symbols.top())){//非终结符等待归约
b=symbols.top();
symbols.pop();
save.push(b);
}
do{//if symbols[j-1]为终结符 j--;否则j:=j-2;
b=symbols.top();
symbols.pop();
save.push(b);
j--;
if(SearchTerminator(symbols.top())){
q=symbols.top();
}else{
save.push(symbols.top());
symbols.pop();
q=symbols.top();
j--;
}
}while(judgePriority(q,b)!=1);//直到symbols[j]<q;
for(int i=0;i<k-j;i++){//save归约串
sequence[i]=save.top();
save.pop();
}
if(sequence[0]=="term"){//判断是否为表达式
if(!((k-j)%2)){
cout<<"No,it is wrong.";//表达式归约出错
return 0;
}
for(int i=0;i<k-j;i++){
if(!(i%2)){
if(sequence[i]!="term"){
cout<<"No,it is wrong.";//表达式归约出错
return 0;
}
}else{
if(sequence[i]!="plus"||sequence[i]!="minus"){
cout<<"No,it is wrong.";//表达式归约出错
return 0;
}
}
k=j+1;
symbols.push("expression");
}
}else if(sequence[0]=="factor"){//判断是否为项
if(!((k-j)%2)){
cout<<"No,it is wrong.";//项规约出错
return 0;
}
for(int i=0;i<k-j;i++){
if(i%2){
if(sequence[i]!="factor"){
cout<<"No,it is wrong.";
return 0;
}
}else{
if(sequence[i]!="times"||sequence[i]!="slash"){
cout<<"No,it is wrong.";
return 0;
}
}
}
k=j+1;
symbols.push("term");
}else if(sequence[0]=="lparen"){//判断是否为因子
if(sequence[0]=="lparen"){
if((k-j)!=3){
cout<<"No,it is wrong.";//因子规约错误
return 0;
}
if(sequence[1]!="expression"||sequence[2]!="rparen"){
cout<<"No,it is wrong.";
return 0;
}
}
k=j+1;
if(symbols.top()!="times"||symbols.top()!="slash"||sym!="times"||sym!="slash"){
symbols.push("term");
}
else symbols.push("factor");
}else if(sequence[0]=="number"||sequence[0]=="ident"){//标识符 整数 + - * /
k=j+1;
if(symbols.top()!="times"||symbols.top()!="slash"||sym!="times"||sym!="slash"){//加法运算符、乘法运算符
symbols.push("term");
}
else symbols.push("factor");
}else{
cout<<"No,it is wrong.";//字符规约出错
return 0;
}
}
if(judgePriority(q,sym)==1||judgePriority(q,sym)==0){//symbols[j]</=sym
k++;
symbols.push(sym);
}
}
if(k==3){//深度为3时
for(int i=0;i<3;i++){//判断是否为"#"以此判断表达式是否正确
b=symbols.top();
symbols.pop();
if(i==0&&b!="#"){
cout<<"No,it is wrong.";//表达式出错
return 0;
}else if(i==1&&b!="expression"){
cout<<"No,it is wrong.";//表达式出错
return 0;
}else if(i==2&&b!="#"){
cout<<"No,it is wrong.";//表达式出错
return 0;
}
}
return 1;
}
else{
cout<<"No,it is wrong.";//表达式出错
return 0;
}
}
void map_init(){//key-value(单词-编码)对应关系进行初始化
Word["begin"]="beginsym";
Word["call"]="callsym";
Word["const"]="constsym";
Word["do"]="dosym";
Word["end"]="endsym";
Word["if"]="ifsym";
Word["odd"]="oddsym";
Word["procedure"]="proceduresym";
Word["read"]="readsym";
Word["then"]="thensym";
Word["var"]="varsym";
Word["while"]="whilesym";
Word["write"]="writesym";
Word["+"]="plus";
Word["-"]="minus";
Word["*"]="times";
Word["/"]="slash";
Word["="]="eql";
Word["<>"]="neq";
Word["<"]="lss";
Word["<="]="leq";
Word[">"]="gtr";
Word[">="]="geq";
Word[":="]="becomes";
Word["("]="lparen";
Word[")"]="rparen";
Word[","]="comma";
Word[";"]="semicolon";
Word["."]="period";
Word["#"]="#";
}
void lexicalAnalysis(){
string word;//识别单词
string str;//识别字符
ifstream infile("./1.txt");
ofstream outfile("./2.txt");
ostringstream buf;
char ch;
while(buf&&infile.get(ch)) buf.put(ch);//将文件中的字符读出来
str=buf.str();
for(std::size_t i=0;i<str.size();i++){//对整个字符串进行遍历
while(str[i]==' '||str[i]=='\n') i++;//若最开始为空格或换行符,则将指针的位置往后移
if(isalpha(str[i])){//对标识符和基本字进行识别,调用库函数isalpha()
word=str[i++];
while(isalpha(str[i])||isdigit(str[i])){
word+=str[i++];
}
it=Word.find(word);//返回word在Word中的迭代器
if(it!=Word.end()){//判断是不是基本字,若为基本字则进行输出
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
}
else{//否则为标识符直接输出
outfile<<"(ident"<<","<<word<<")"<<endl;
}
i--;
}
else if(isdigit(str[i])){//判断是不是常数,调用库函数isdigit()
word=str[i++];
while(isdigit(str[i])){
word+=str[i++];
}
if(isalpha(str[i])){
outfile<<"error!"<<endl;
break;
}
else{
outfile<<"(number"<<","<<word<<")"<<endl;
}
i--;
}else if(str[i]=='<'){//对<,<=,<>分别进行判断
word=str[i++];
if(str[i]=='>'){
word+=str[i];
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
}else if(str[i]=='='){
word+=str[i];
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
}else if(str[i]!=' '||!isdigit(str[i])||!isalpha(str[i])){
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
i--;
}else{
outfile<<"error!"<<endl;
break;
}
}else if(str[i]=='>'){//对>,>=分别进行判断
word=str[i++];
if(str[i]=='='){
word+=str[i];
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
}else if(str[i]!=' '||!isdigit(str[i])||!isalpha(str[i])){
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
i--;
}else{
outfile<<"error!"<<endl;
break;
}
}else if(str[i]==':'){//对:=进行判断
word=str[i++];
if(str[i]=='='){
word+=str[i];
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
}else{
outfile<<"error!"<<endl;
break;
}
}else{//对其他的基本字依次进行判断
word=str[i];
it=Word.find(word);
if(it!=Word.end()){
outfile<<"("<<Word[word]<<","<<word<<")"<<endl;
}else{
break;
}
}
}
infile.close();
outfile.close();
}
int main(){
//map_init();//key-value(单词-编码)对应关系进行初始化
//lexicalAnalysis();
init();//初始化终结符序列
if(OperatorAnalysis()){
cout<<"Yes,it is correct."<<endl;//如果算符优先法分析return 1 ,则分析正确
}
return 0;
}