**
任务说明
**
1. 实验目的
通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析
所识别的语法范畴变换为某种中间代码的语义翻译方法。
掌握目前普遍采用的语义分析方法──语法制导翻译技术。
给出 PL/0 文法规范,要求在语法分析程序中添加语义处理,对于语法正确的表达式,输出其中间代码;对于语法正确的算术表达式,输出其计算值。
2. 实验准备
微机安装好 C 语言,或 C++,或 Visual C++.
3.实验内容
已给 PL/0 语言文法,在实验二或实验三的表达式语法分析程序里,添加语义处理部分,输出表达式的中间代码,用四元式序列表示。
4. 实验要求
语义分析对象重点考虑经过语法分析后已是正确的语法范畴,本实验重点是语义子程序。
在实验二或实验三“语法分析器”的里面添加 PL/0 语言“表达式”部分的语义处理,输出表达式的中间代码,计算表达式的语义值。
中间代码用四元式序列表示。
5. 输入输出
(1) PL/0 算术表达式的语义计算:
输入:
PL/0 算术表达式,例如:2+35 作为输入。
输出:《编译原理》实验指导书
13 of 14
17
(2)PL/0 表达式的中间代码表示
输入:
PL/0 表达式,例如: a(b+c)。
输出:
(+,b,c,t1)
(*,a,t1,t2)
具体实现
设计思想
扩充的巴科斯范式
<表达式> ::= [+|-]<项>{<加法运算符> <项>}
<项> ::= <因子>{<乘法运算符> <因子>}
<因子> ::= <标识符>|<无符号整数>| ‘(’<表达式>‘)’
<加法运算符> ::= +|-
<乘法运算符> ::= *|/
普通的巴科斯范式
为表示方便:
表达式E、项X、因子Y、标识符b,无符号整数z,加法运算符A,乘法运算符C改进后的产生式和语义动作以及变量函数说明:
(1)设定每个非终结符有一个综合属性place,表示存放值的变量名。变量名和值都会保存在符号表中。
(2)newtemp函数:产生一个新的变量名,并返回。
(3)emit函数:产生一个四元式,并保存在四元式列表中。
说明:语义规则中的函数设计和代码实现中的会有些出入,不过功能是一样的。
·适合一遍扫描的翻译模式:
由于本次实验使用自下而上的LR分析方法,所以不用消除左递归。
FIRST和FOLLOW集合
FIRST(S’)={b,z,(,-} FOLLOW(S’)={#}
FIRST(E)={b,z,(,-} FOLLOW(E)={#,),+,-}
FIRST(X)={b,z,(} FOLLOW(X)={
,/,+,-,#,)}
FIRST(Y)={b,z,(} FOLLOW(Y)={
,/,+,-,#,)}
LR分析表
因为改进后的文法跟实验三稍有不同,所以LR分析表也有些不同。
算法流程
该流程是在实验3的LR分析流程上稍作修改的,词法分析过程和实验1相同,图中不再展开说明,另外加入了一些关于语义分析的过程。
代码
//实验1词法分析+实验3LR分析+实验4语义分析和中间代码生成
#include <iostream>
#include <string>
#include <stack>
#include <map>
#include <sstream>
using namespace std;
/* 变量声明 */
/****词法分析***/
//分析的单词,s1为编码,s2为单词符号(词法分析器的输出每个单词的格式)
struct strs
{
string s1,s2;
};
string str_out="";//词法分析完成后输出,也是语法分析的输入
/****语法分析***/
int account=1;//记录需语法分析的单词的个数,再加上#
int flag=0; //记录语法分析状态 flag=1语法分析正确,即对应lr分析表中acc状态
int p=-1;//指针作用,表示当前扫描单词的下标
strs analystr;//当前分析的单词
stack <int> State; //状态栈
stack <string> in; //符号栈
int error=0;//错误处理,对应lr分析表中空白位置,当遇到空白位置时说明分析错误,error置为1
/****语义分析***/
int countt=0;//变量名的个数
int countquad=0;//四元式个数
//为记录X,Y,E的place属性值,也为它们申请变量,保存在符号表中
int countX=0; //语法分析时遇到的X个数
int countY=0; //语法分析时遇到的Y个数
int countE=0; //语法分析时遇到的E个数
map<string,string>words;//符号表
map<string,int>res;//当分析算术表达式时,记录每个四元式result值
//四元式结构
struct four
{
string op;//操作符
string o1;//操作数1
string o2;//操作数2
string result;//记录结果变量名,真实的结果数据在res表中
};
four four[50];//四元式列表
/* 函数 */
//扫描下一个单词
void Advance(strs *S)
{
p++;
analystr = S[p];
}
//产生新的变量名
string newtemp(string s)
{
stringstream ss;
if(s=="t")
{
countt++;
ss<<s<<countt;//拼接
}
else if(s=="X")
{
countX++;
ss<<s<<countX;//拼接
}
else if(s=="Y")
{
countY++;
ss<<s<<countY;//拼接
}
else
{
countE++;
ss<<s<<countE;//拼接
}
return ss.str();
}
//产生四元式并保存到四元式列表中
void emit(string op,string o1,string o2,string res)
{
four[countquad].op=op;
four[countquad].o1=o1;
four[countquad].o2=o2;
four[countquad].result=res;
countquad++;
}
//词法分析:用于识别是基本字或标识符
void Letter(string str){
/*-------------------识别基本字--------------------*/
if(str=="begin")
{
account++;str_out = str_out +"(beginsym,begin)"+' ';}
else if(str=="call")
{
account++;str_out = str_out +"(callsym,call)"+' ';}
else if(str=="const")
{
account++;str_out = str_out +"(constsym,const)"+' ';}
else if(str=="do")
{
account++;str_out = str_out +"(dosym,do)"+' ';}
else if(str=="end")
{
account++;str_out = str_out +"(endsym,end)"+' ';}
else if(str=="if")
{
account++;str_out = str_out +"(ifsym,if)"+' ';}
else if(str=="odd")
{
account++;str_out = str_out +"(oddsym,odd)"+' ';}
else if(str=="procedure")
{
account++;str_out = str_out +"(proceduresym,procedure)"+' ';}
else if(str=="read")
{
account++;str_out = str_out +"(readsym,read)"+' ';}
else if(str=="then")
{
account++;str_out = str_out +"(thensym,then)"+' ';}
else if(str=="while")
{
account++;str_out = str_out +"(whilesym,while)"+' ';}
else if(str=="var")
{
account++;str_out = str_out +"(varsym,var)"+' ';}
else if(str=="write")
{
account++;str_out = str_out +"(writesym,write)"+' ';}
/*-------------------end---------------------------*/
/*-------------------识别标识符--------------------*/
else{
account++;str_out = str_out+"(ident,"+str+")"+' ';
}
/*-------------------end---------------------------*/
}
//语法LR分析:对应goto表,执行归约后,根据当前状态和符号确定哪一个状态应该进入State中
void Analy_goto()
{
int state=State.top(); //取当前栈顶状态
string si=in.top(); //当前符号栈的栈顶符号
char sii=si[0]; //string->char转换
switch(state)
{
//根据goto表,压入状态的代码
case 0:
if(sii=='E') State.push(1);
else if(sii=='X') State.push(3);
else if(sii=='Y') State.push(4);
else error=1;
break;
case 2:
if(sii=='X') State.push(10);
else if(sii=='Y') State.push(11);
else error=1;
break;
case 5:
if(sii=='E') State.push(14);
else if(sii=='X') State.push(3);
else if(sii=='Y') State.push(11);
else error=1;
break;
case 8:
if(sii=='X') State.push(15);
else if(sii=='Y') State.push(11);
else error=1;
break;
case 9:
if(sii=='X') State.push(16);
else if(sii&#