编译实验(二)语法/语义分析

通过词法分析,我们成功得到了一个完整的token 文件以及符号表,接下来要做的就是语法/语义分析。我们采用的分析方法是算符优先算法,实现这一个算法的前提是文法必须是算符优先文法,因此我们首先要做的事就是构造算符优先文法,文法结构如下:

1、构造文法并且初始化其各个属性。

class Grammar

{

public:

int getid(){ return id ; }

char  * getINP(){ return INP; }

char *getOUP(){ return OUP; }

bool isbelong(string str) ; //是否属于终结符集合

vector<string> getisEnd(){ return isEnd; }

private:

int id; //文法序号

char * INP;  //产生式左部分

char * OUP;//产生式右部分数组

vector<string> notEnd;  //非终结符集合

vector<string> isEnd; //终结符集合

};


本次实验所用的算符文法 grammar 数组集合:

Grammar  grammar[] = {
	Grammar(0, "W", "# P #"),
	Grammar(1, "P", "program id L"),     //程序,标识符,程序体
	Grammar(2, "L", "S ; L")  ,//S语句  , L 语句表, A赋值语句,B 布尔表达式,E算术表达式
	Grammar(2, "L", "S"),
	Grammar(3, "S", "if B then S"),
	Grammar(3, "S", "if B then L else S"),
	Grammar(3, "S", "while B do S"),
	Grammar(3, "S", "begin L end"),
	Grammar(3, "S", "var D"),
	Grammar(3, "S", "?"),       //s->空 //10个
	Grammar(3, "S", "A"),
	Grammar(4, "D", "id : K ;"),        //D 声明语句  , id标识符 ,K数据类型 
	Grammar(5, "K", "integer"),
	Grammar(5, "K", "bool"),
	Grammar(5, "K", "real"),   //156
	Grammar(6, "A", "id := E"),
	Grammar(7, "E", "E + T"),
	Grammar(7, "E", "T"),
	Grammar(7, "E", "- E"),
	Grammar(8, "B", "B or N"),    //20
	Grammar(8, "B", "N"),
	Grammar(8, "B", "not B"),      //R 布尔运算符
	Grammar(9, "T", "T * F"),
	Grammar(9, "T", "F"),
	Grammar(10, "F", "( E )"),    //25
	Grammar(10, "F", "id"),
	Grammar(11, "N", "N and M"),
	Grammar(11, "N", "M"),
	Grammar(12, "M", "( B )"),
	Grammar(12, "M", "id < id"),//30
	Grammar(12, "M", "id > id"),
	Grammar(12, "M", "id <> id"),
	Grammar(12, "M", "id <= id"),
	Grammar(12, "M", "id >= id"),
	Grammar(12, "M", "id = id"),
};


例如Grammar(1, "P", "program id L"),表示P->program id L 产生式,程序中标识符、整形、浮点型和常数在产生式统一体现为”id”,文法序号用来区分产生式左部的非终结符,此文法中,非终结字符全用大写字母表示,终结字符全用小写字母表示,这样区分是否为终结符只需要判断字符串转化成大写后是否等于本身即可。

2、将所有文法根据产生式左部统一起来

完成了文法的构建,接下来要把产生式统一起来,结构如下:

class flGrammar

{

private:

vector<string> isEnd;

string notEnd;    //左产生式

int id;

public:

vector<string> fristvt;      

vector<string> lastvt;

flGrammar(){};

int getid(){ return id; }

string getNotEnd(){ return notEnd; }

flGrammar(int id){ flInitGrammar(id); };

    void flInitGrammar(int id);

bool isexistFirstvt(string str) //是否在fristvt和lastvt

{

for (int i = 0; i < fristvt.size(); i++)

if (str == fristvt[i])

return true;

return false;

}

bool isexistLastvt(string str);

};


本次要做的主要任务为求得Fristvt和Lastvt的第一步,也就是把产生式第一个终结符加入到Fistvt,如果第一个是非终结符把第二个字符串姐加入到Fristvt,Lastvt同理。

通过初始化工作,得到一个整合起来的文法,例如 S->if B then S,S->begin L end,整合起来后得到notend=”S” ,isend={if then begin end},fristvt={if begin},lastvt={then end},其中flGrammar的属性id也就是文法序号。

 

3、构造文法表,完善Fristvt和lastvt

class flTable

{

public:

flTable();

~flTable();

void print(){

for (int i = 0; i < Grammar_Maxid; i++)

flgrammar[i]->print();

}

void finishGra();    //完成Fristvt & Lastvt

void printTable();

bool Match(vector<token>,string &);

void printBuffer();

void printCreateTreeStack();

void printSignStack();

Tree * getGraTree(){ return GrammarTree; }

private:

flGrammar *flgrammar[Grammar_Maxid];

vector<vecEnd> end;   //终结符聚合

int **t_relation;

void initEnd();

bool isexistEnd(vecEnd str)

{

for (int i = 0; i < end.size(); i++)

if (str.end == end[i].end)

return true;

return false;

}

int findend(string str)  ; //根据字符串查询终结符在end的中的索引 

vector <token> signStack;   //符号栈

vector <token> Buffer;//输入缓冲区

string getSignStackname(token t)

{

 

if (typeIsId(t.type))

return "id";

return t.name;

}

Tree *GrammarTree = NULL;

};


其中flGrammar *flgrammar[Grammar_Maxid];也就是关于所有文法的集合,之前已经把产生式第一个终结符加入到Fristvt,把产生式最后一个终结符加入到Lastvt中。现在要完善Fristvt和Lastvt。从尾至头的遍历garmmar文法数组集合,把形如P->Q……这样非终结符Q打头的Fristvt集加入到P的Fristvt集里,形如P->……Q这样非终结符结尾的Q的Lastvt集加入到P的lastvt,在加入的过程中首先要判断要加入的终结符是否已经在了集合中。这就是bool  flGrammar:: isexistFirstvt(string str) 的作用,如果已经在集合中就不用再加入进去了。

4、初始化终结符集合,完成算符优先表

void flTable::initEnd();终结符集合比较简单,只需要遍历文法的终结符集合即可。初始化算符优先表,把P->…Qa… 这样的Lastvt(Q)中所有元素> a ,P->…aQ…这样的a<Fristvt(Q)中所有元素,P->…ab…P->…aQb这样的另a=b。其中用0123分别代表无关系、小于关系、等于关系、大于关系。

细化算符优先表int **t_relation;步骤

1、遍历grammar文法集合,从第0个文法开始,将其产生式右部分进行字符串分割,得到一个该产生式右部字符集合,遍历该字符集合,从第i=0个字符开始判断是不是终结符,如果是,再判断第i+1个字符是不是非终结符,如果是,第i个终结符小于第i+1个非终结符的Fristvt集合,如果第i+1是终结符,则第i个等于第i+1个。

2、如果第i个是非终结符则第i个的非终结符的Lastvt集合大于第i+1个字符。

3、再次遍历一遍文法,如果第i=0个字符为终结符且第i+1个为非终结符且第i+2个为终结符,则第i个终结符等于第i+2个终结符。

注意:终结符在算符优先表的位置也就是在vector<vecEnd> end;   //终结符集合中的位置,所以有 int flTable:: findend(string str)的作用就是查找终结符在终结符集合中的索引。

5、初始化符号栈,输入缓冲区,根据算符优先表完成规约

    先自定义一个有 “#”的token对象,将#压入栈中和缓冲区中,再将词法分析中产生的token集合逆序推入缓冲区。我们知道,在规约整个过程中都是栈与缓冲区的终结符之间的关系比较,与非终结符无关。所以规约时,只需要把栈中元素推出去即可,无需再把非终结符压入栈中。

当栈顶元素与缓冲区顶元素比较,如果小于或等于,把缓冲区顶元素移进到栈中。如果大于则栈顶指针top下移,top和top+1元素进行比较直到比较到小于时,将top之后的元素全部从栈中推出。栈顶元素与缓冲区顶元素无关系时,则指明语法出错。当且仅当关系为等于号且缓冲区顶部元素为#时表明成功规约。至此语法分析结束。

 

6、语义分析:完成语义动作表以及四元式数据结构

四元式结构如下:其中op代表第一元素的字符形式,code代表第一元素代码形式。ConvertOpToCode()方法是根据op给code赋值。完成后,定义一个四元式集合,语义分析就是完成代码关于四元式集合的生成。

struct GenStruct

{

int label;    //四元式序号

string op;

int code;     //第一元素

string addr1="0"; //第二元素

string addr2="0"; //第三元素

string result="0";//第四元素

int out_port = 0;  //记录该四元式是否为一个基本块的入口,是则为1,否则为0。

void ConvertOpToCode(string);

GenStruct(int l){ label = l; }

GenStruct(int l,string op,string a1,string a2,string res){

label = l, this->op=op, ConvertOpToCode(op), addr1 = a1, addr2 = a2, result = res;

}

};


语义动作表代码在语法分析中规约的时候完成:

void GrammarAction(TreeNode  *pNew, vector<TreeNode> pushend,int index) //语义动作 index代表其文法索引, pushend代表各个结点  pNew 根结点
{
	string value1;
	string backpatchfalse;
	switch (index)
	{
#pragma region ...
	case 0:
		pNew->nextlist = pushend[1].nextlist;
		break;
	case 1:
		pNew->nextlist = pushend[0].nextlist;
		break;
	case 2:
		pNew->nextlist = pushend[0].nextlist;
pNew->fristnextquad = pushend[2].fristnextquad;
		break;
	case 3:
		pNew->nextlist = pushend[0].nextlist;
pNew->fristnextquad = pushend[2].fristnextquad;
		break;
		//S
	case 4: //if B then S
		backpatch(pushend[pushend.size() - 2].truelist, pushend[pushend.size() - 2].nextquad);
		pNew->nextlist = merge(pushend[0].nextlist, pushend[pushend.size() - 2].falselist);
		pNew->fristnextquad = pushend[pushend.size() - 2]].fristnextquad;
		break;
	case 5://if B then L else S,then 要跳转到 else后面
		
		int2str(stoi(pushend[2].nextquad)+1,backpatchfalse);
		backpatch(pushend[pushend.size() - 2].truelist, pushend[pushend.size() - 2].nextquad);
		backpatch(pushend[pushend.size() - 2].falselist, backpatchfalse);
		//backpatch(pushend[pushend.size() - 2].falselist, pushend[0].fristnextquad);
		pNew->nextlist = merge(pushend[0].nextlist, pushend[pushend.size() - 2].falselist);
		CreateGen("j", "0", "0", pushend[0].nextquad);
		pNew->nextquad = getNextquad();
		//把getnextquad的产生式插入到pushend[2].nextquad ,把13加入到7后面8
		InsertGentoIndex(getNextquad(), pushend[2].nextquad);
pNew->fristnextquad = pushend[pushend.size() - 2]].fristnextquad;
		break;
	case 6://s->while B do S
	/*	backpatch(pushend[0].nextlist,pushend[2].fristnextquad);*/
		backpatch(pushend[2].truelist, pushend[2].nextquad);
		pNew->nextlist = pushend[2].falselist;
		CreateGen("j", "0", "0", pushend[2].fristnextquad);
		pNew->nextquad = getNextquad();
		backpatch(pushend[2].falselist, getNextquad());
		break;
	case 7://begin  S end
		pNew->nextlist = pushend[1].nextlist;
		break;
	case 8://var D
		break;
	case 9:// ?
		break;
	case 10://S->A
		break;


	case 11: //D:->id:K
		break;
	case 12://k->integer
		break;
	case 13://k->integer
		break;
	case 14://k->integer
		break;
	case 15://A-> id:=E
		CreateGen(pushend[1].data.name, pushend[0].value, "0", pushend[2].data.name);
		pNew->nextquad = getNextquad();
		break;
	case 16://E->E+T
		 value1 = NewTempStruct();
		CreateGen("+", pushend[2].value, pushend[0].value, value1);
		pNew->value = value1;
		pNew->nextquad = getNextquad();
		break;
	case 17:
		pNew->value = pushend[0].value;
		break;
#pragma endregion
	case 18: //E->-E
		value1 = NewTempStruct();
		CreateGen("-","0", pushend[0].value, value1);
		pNew->nextquad = getNextquad();
		pNew->value = value1;
		break;
	case 19://B-> B or N
		backpatch(pushend[pushend.size() - 1].falselist, pushend[pushend.size() - 1].nextquad);
		pNew->truelist = merge(pushend[pushend.size() - 1].truelist, pushend[0].truelist);
		pNew->falselist = pushend[0].falselist;
		pNew->fristnextquad = pushend[2].fristnextquad;
		break;
	case 20://B->N
		pNew->falselist = pushend[0].falselist;
		pNew->truelist = pushend[0].truelist;
		pNew->value = pushend[0].value;
		break;
	case 21://B->not B
		pNew->falselist = pushend[0].truelist;
		pNew->truelist = pushend[0].falselist;
		pNew->value = pushend[0].value;
		break;
	case 22: //T->T*F
		value1 = NewTempStruct();
		CreateGen("*", pushend[2].value, pushend[0].value, value1);
		pNew->value = value1;
		pNew->nextquad = getNextquad();
		break;
	case 23://T->F
		pNew->falselist = pushend[0].falselist;
		pNew->truelist = pushend[0].truelist;
		pNew->value = pushend[0].value;
		break;
	case 24://F->(E)
		pNew->falselist = pushend[1].falselist;
		pNew->truelist = pushend[1].truelist;
		pNew->value = pushend[1].value;
		break;
	case 25://F->id
		pNew->value = pushend[0].data.name;
		break;
	case 26://N->N and M
		backpatch(pushend[pushend.size() - 1].truelist, pushend[pushend.size() - 1].nextquad);
		pNew->falselist = merge(pushend[pushend.size() - 1].falselist, pushend[0].falselist);
		pNew->truelist = pushend[0].truelist;
		pNew->fristnextquad = pushend[2].fristnextquad;
		break;
	case 27:
		pNew->falselist = pushend[0].falselist;
		pNew->truelist = pushend[0].truelist;
		pNew->value = pushend[0].value;
		break;
	case 28: //M->(B)
		pNew->falselist = pushend[1].falselist;
		pNew->truelist = pushend[1].truelist;
		pNew->value = pushend[1].value;
		break;
	case 29: //M->id <id
	case 30: //>
	case 31: //<>
	case 32: //<=
	case 33://>=
	case 34://=
		string s1;
		int2str(stoi(getNextquad())+ 1, s1);
		pNew->truelist.push_back(getNextquad());
		pNew->falselist.push_back(s1);
		CreateGen("j"+pushend[1].data.name,pushend[2].data.name,pushend[0].data.name,"0");
		CreateGen("j","0", "0", "0");
		pNew->nextquad = getNextquad();
		break;
	}
}

其中merge作用是将两集合合并并返回,backpatch作用是回填,把新值回填到指定四元式集合的第四元素中。CreateGen()产生四元式并加入到四元式集合中,void InsertGentoIndex(string sel,string desti)将四元式中转移到四元式集合中的指定位置,其中sel代表所在位置,destin代表要转移到的位置。

注意:在转移过程中要把在destin和sel之间的所有四元式的label加1,判断并且跳转语句的四元式的第四元素是否在在destin和sel之间内,如果在将该四元式的第四元素+1,最后删除原本的四元式。语义动作表是在规约前完成,找出规约时所用的产生式,根据产生式的索引完成指定的动作。可以根据规约时的终结符集合和产生式中终结符集合是否降等来判断所用的产生式是哪一条。


7、语法分析:定义(语法)树的数据结构

因为语法所用的规约只把终结符推出栈,与非终结符无关。但是这样就无法进行自下而上的属性赋值,也就无法完成语义动作。用建立树的方式,在规约的时候把要规约的符号作为孩子,而查询出的规约的产生式左部的非终结符作为根节点。规约时完成对语法树的建立偶同时完成语法动作(也就是四元式产生).

struct TreeNode

{

TreeNode()

{

fristnextquad = getNextquad();

nextquad = getNextquad();

value = data.name;

sibling = NULL;

child = NULL;

}

DataType data;

TreeNode * sibling; //右兄弟结点

TreeNode * child;   //左子结点

string nextquad;	//完成规约时的四元式地址

string fristnextquad;   //完成规约前的四元式地址

string value;  //代表的值,产生式第四元素如T1,T2

vector<string>nextlist;

vector<string>truelist; //真出口集

vector<string>falselist; //出口集

};


其中getNextquad()为四元式集合长度,也就是下一条要产生四元式的地址,DataTypetoken。完成了数据结构的定义,在规约时,就把每一个要规约的元素建立一个结点(兄弟结点),找到规约对应的产生式,根结点也就是该产生式的左部非终结符。也就是说在语法规约的时候,完成对树的建立,完成规约时相应的语法动作,最终完成规约,建立好语法树。干脆。重新再建立一个符号栈,不过符号栈中元素不在是token而是TreeNode,和原本的符号栈的区别就在于把非终结符也加入到了里面,而不是与非终结符无关



  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
附录c 编译程序实验 实验目的:用c语言对一个简单语言的子集编制一个一遍扫描的编译程序,以加深对编译原理的理解,掌握编译程序的实现方法和技术。 语法分析 C2.1 实验目的 编制一个递归下降分析程序,实现对词法分析程序所提供的单词序列的语法检查和结构分析. C2.2 实验要求 利用C语言编制递归下降分析程序,并对简单语言进行语法分析. C2.2.1待分析的简单语言的语法 实验目的 通过上机实习,加深对语法制导翻译原理的理解,掌握将语法分析所识别的语法成分变换为中间代码的语义翻译方法. 实验要求 采用递归下降语法制导翻译法,对算术表达式、赋值语句进行语义分析并生成四元式序列。 实验的输入和输出 输入是语法分析提供的正确的单词串,输出为三地址指令形式的四元式序列。 例如:对于语句串 begin a:=2+3*4;x:=(a+b)/c end# 输出的三地址指令如下: (1) t1=3*4 (2) t2=2+t1 (3) a=t2 (4) t3=a+b (5) t4=t3/c (6) x=t4 算法思想 1设置语义过程 (1) emit(char *result,char *arg1,char *op,char *ag2) 该函数功能是生成一个三地址语句送到四元式表中。 四元式表的结构如下: struct {char result[8]; char ag1[8]; char op[8]; char ag2[8]; }quad[20]; (2)char *newtemp() 该函数回送一个新的临时变量名,临时变量名产生的顺序为T1,T2,…. Char *newtemp(void) { char *p; char m[8]; p=(char *)malloc(8); k++; itoa(k,m,10); strcpy(p+1,m); p[0]=’t’; return(p); } (2)主程序示意图如图c.10所示。 (2) 函数lrparser在原来语法分析的基础上插入相应的语义动作:将输入串翻译成四元式序列。在实验中我们只对表达式、赋值语句进行翻译。 语义分析程序的C语言程序框架 int lrparser() { int schain=0; kk=0; if(syn=1) { 读下一个单词符号; schain=yucu; /调用语句串分析函数进行分析/ if(syn=6) { 读下一个单词符号; if(syn=0 && (kk==0)) 输出(“success”); } else { if(kk!=1 ) 输出 ‘缺end’ 错误;kk=1;} else{输出’begin’错误;kk=1;} } return(schain); int yucu() { int schain=0; schain=statement();/调用语句分析函数进行分析/ while(syn=26) {读下一个单词符号; schain=statement(); /调用语句分析函数进行分析/ } return(schain); } int statement() { char tt[8],eplace[8]; int schain=0; {switch(syn) {case 10: strcpy(tt,token); scanner(); if(syn=18) {读下一个单词符号; strcpy(eplace,expression()); emit(tt,eplace,””,””); schain=0; } else {输出’缺少赋值号’的错误;kk=1; } return(schain); break; } } char *expression(void) {char *tp,*ep2,*eplace,*tt; tp=(char *)malloc(12);/分配空间/ ep2=(char *)malloc(12); eplace=(char *)malloc(12); tt =(char )malloc(12); strcpy(eplace,term ());/调用term分析产生表达式计算的第一项eplace/ while(syn=13 or 14) { 操作符 tt= ‘+’或者‘—’; 读下一个单词符号; strcpy(ep2,term());/调用term分析产生表达式计算的第项ep2/ strcpy(tp,newtemp());/调用newtemp产生临时变量tp存储计算结果/ emit(tp,eplace,tt,ep2);/生成四元式送入四元式表/ strcpy(eplace,tp); } return(eplace); } char *term(void)/仿照函数expression编写/ char *factor(void) {char *fplace; fplace=(char *)malloc(12); strcpy(fplace, “ ”); if(syn=10) {strcpy(fplace,,token);/将标识符token的值赋给fplace/ 读下一个单词符号; } else if(syn=11) {itoa(sum,fplace,10); 读下一个单词符号; } else if (syn=27) {读下一个单词符号; fplace=expression();/调用expression分析返回表达式的值/ if(syn=28) 读下一个单词符号; else{输出‘}’错误;kk=1; } } else{输出‘(’错误;kk=1; } return(fplace); }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值