实验1:词法分析程序

实验1:词法分析程序
实验1:词法分析程序

实验1 词法分析程序

一、实验目的与要求

1.复习正规文法、正规式、有限自动机之间的相互转换的原理及技术;

2.学会使用Visual C++等高级语言编程实现上述转换,并能合理显示结果;

3.以C++的一个真子集为案例,具体分析词法分析程序的设计步骤、基本架构及代码

编制,并通过一定实例验证其可行性,以加深对词法分析原理的理解;

4.通过本次实验,使学生理解模块化程序设计的思想,从而从全局角度领会一个完整

软件的设计精髓,为后续试验的顺利完成奠定坚实的基础。

二、实验仪器及设备

1.微型电子计算机80台

2.配置Windows 2000及以上版本操作系统

3.安装Visual C++6.0/V isual C#2000/Delphi6.0等以上版本的开发环境

三、实验内容及步骤

(一)正规文法与有限自动机的相互转换

1.正规文法?有限自动机

已知某正规文法G[S]如下:

S→aA

S→bB

S→ε

A→aB

A→bA

B→aS

B→bA

B→ε

请编程实现:

(1)将G[S]转换为NFA;

(2)将上述得到的NFA确定化为DFA;

(3)再将DFA最简化为MFA。

2.有限自动机?正规文法

已知某有限自动机NFA M=(Q,∑,f,q0,Z)如下:

状态集:Q={1,2,3,4,5,6,7,8,9}

字母表:∑={a,b}

转移函数:

f(1,a)=5

f(1,ε)=2

f(1,a)=4

f(5,ε)=6

f(2,b)=3

f(4,ε)=7

f(6,ε)=2

f(6,b)=9

f(3,ε)=8

f(8,a)=9

f(7,b)=9

初态:q0=1

终态集:Z={6,7,9}

请编程实现:

(1)首先将此NFA确定化为DFA;

(2)再将得到的DFA最简化为MFA;

(3)最后,将MFA转化为正规文法(左线性或右线性均可)。

(二)编程实现MiniC++的词法分析

这里的MiniC++为C++语言的一个真子集,其语言系统与C++类似。基本组成如下:(1)关键字有18个,分别为:void、int、char、bool、float、double、if、else、switch、case、default、break、continue、do、while、for、return以及struct等。

(2)标识符:定义为“以字母或下划线开头,由字母、数字或下划线组成的符号串。”

(3)常量:包括数字(暂时仅考虑无符号整数)、字符串、字符常量等。

(4)分隔符:( ) [ ] { } . , : ; ~ ? # 等。

(5)运算符:=、==、>、>=、<、<=、!、!=、&&、&=、||、|=、+、++、- 、=、->、/、/=、%、%=等。

要求同学们完成下述工作:

1.基于上述描述,根据你的理解,尝试写出该MiniC++的文法G[S];

2.再根据你所写的文法G[S],绘制出MiniC++的NFA状态图,并进而推导出最简化的MFA;

3.根据你推导出的MFA,编写MiniC++的词法分析程序,并用下面的源代码测试你所设计的程序。

将程序输出的单词流与理论分析的结果比较分析,若有差错,请找出原因,并进一步修改你的程序,直至得到正确的结果。

四、思考题

1.将实验内容(一)中的文法G[S]转化为正规式;

2.将实验内容(一)中的有限自动机M转化为正规式。

五、实验报告

1.实验报告撰写在统一配发的

.....上,下次上课时交给老师。

.....纸质报告册

2.实验报告格式如下。

六、参考代码

(一)CGrammar类

1.头文件Grammar.h

#pragma warning(disable:4786)//屏蔽“由于string和vector等STL模版产生很长的标识符而引起的警告信息”

#include

#include

#include

#include

#include //使用vector容器

#include

using namespace std;

struct CreateFormula // 产生式

{

string LeftPart; // 左部

string RightPart; // 右部

};

class CGrammar

{

public:

CGrammar();

~CGrammar();

//外部操作函数:

void SetGrammar(char *Filename);// 根据文件内容设置文法产生式

void DelDirectLeftRecursion(int i);// 消除“直接”左递归

bool DelIndirectLeftRecursion(); // 消除“间接”左递归

bool HavingRedundance(); // 含有无用产生式

void MiniGrammar(); // 文法化简

void DisplayGrammar(); // 显示文法

void DisplayGrammar(char *Filename);// 将文法产生式保存在txt文件

//返回文法属性的函数:

int GetGrammarType(); // 返回文法类型

bool GetHavingLeftRecursion(); // 是否存在左递归

vectorGetVN(); // 返回非终结符

vectorGetVT(); // 返回终结符

vectorGetCF(); // 返回产生式数据

string GetStartSign(); // 返回开始符号

public:

//基本函数:

bool FindSign(string str,vector&VnVt); // 查找某符号串str是否属于VnVt

vector GetSingleSign(string str); //分解字符串为单个符号(包括形式为E'的符号)

private:

void CountVn_and_Vt(); // 求非终结符和终结符集

bool EnableDelLeftRecursion(); // 能否删除左递归

void ExistDirectLeftRecursion(); // 含有“直接”左递归

void ExistIndirectLeftRecursion();// 含有“间接”左递归

bool IS_0_Grammar(); // 识别0型文法

bool IS_1_Grammar(); // 识别1型文法

bool IS_2_Grammar(); // 识别2型文法

bool IS_3_Grammar(); // 识别3型文法

void GrammarType(); // 判断文法类型

string CreateNewVn(string vn); // 构造新的非终结符

protected:

// 文法结构

vectorNoEndSign; // 非终结符集

vectorEndSign; // 终结符集

vectorGR_Array;// 产生式集合

string StartSign; // 开始符号

int GrammarTypeNum; // 文法类型号码

private:

int GR_Number; // 产生式的总数

bool IsDirectLeftRecursion; // 含有直接左递归的标识

bool IsIndirectLeftRecursion; // 含有间接左递归的标识

};

2.实现文件Grammar.cpp

#include "Grammar.h"

#define KONG "ε"

CGrammar::CGrammar()

{

GR_Array.reserve(1000); //很重要,预先划分一块内存给vector使用

GrammarTypeNum=-1;

IsDirectLeftRecursion=false;

IsIndirectLeftRecursion=false;

}

CGrammar::~CGrammar()

{

}

//从txt文件中获取文法的产生式集合:

//规则:不用简写;用“→”连接;用“ε”表示空串;每行一条产生式。

void CGrammar::SetGrammar(char *Filename)

{

ifstream fin(Filename,ios::in);

char line[1024]={0};

int pos;

CreateFormula temp;

if(!GR_Array.empty())

GR_Array.clear(); //清空

while(fin.getline(line, sizeof(line)))

{

//下面的一段代码用于删除产生式中的所有空格:

char templine[200]="";

int i=0,j=0;

while(line[i])

{

if(line[i]!=' '&&line[i]!=''&&line[i]!=' ')

{

templine[j]=line[i];

j++;

}

i++;

}

templine[j+1]='\0';

//操作结束

string s(templine);

if((pos=s.find("→"))>=100)

continue;

temp.LeftPart=s.substr(0,pos);

temp.RightPart =s.substr(pos+2,s.length()-pos);//"→"是汉字符号,所以“+2”

GR_Array.push_back(temp);

}

GR_Number=GR_Array.size();

fin.clear();

fin.close();

//初始化操作:

CountVn_and_Vt(); // 求非终结符集、终结符集

GrammarType(); // 判断文法的分类类型

ExistDirectLeftRecursion(); // 判断是否存在直接左递归

ExistIndirectLeftRecursion(); // 判断是否存在间接左递归

}

// 将文法产生式保存在txt文件

void CGrammar::DisplayGrammar(char *Filename)

{

ofstream fout(Filename,ios::out);

for(int i=0;i

fout<

fout.clear();

fout.close();

}

// 查找某符号串str是否属于某个容器VnVt

bool CGrammar::FindSign(string str,vector&VnVt)

{

for(int i=0;i

if(!(VnVt[i].compare(str)))

return true;

return false;

}

//求非终结符集Vn、终结符集Vt

void CGrammar::CountVn_and_Vt()

{

if(!NoEndSign.empty())

NoEndSign.clear();

if(!EndSign.empty())

EndSign.clear();

for(int i=0;i

{

//观察产生式的左部

vectorstrtemp=GetSingleSign(GR_Array[i].LeftPart);//分解左部为单个符号

for(int j=0;j

{

string tempvn=strtemp[j];

if(tempvn[0]>='A'&&tempvn[0]<='Z')//若为大写字母则是非终结符

{

if(!FindSign(tempvn,NoEndSign))//若是新的非终结符,则添加进去

NoEndSign.push_back(tempvn);

}

else //否则,则为终结符

{

if(!FindSign(tempvn,EndSign))//若是新的终结符,则添加进去

EndSign.push_back(tempvn);

}

}

//观察产生式的右部

if((GR_Array[i]https://www.360docs.net/doc/d716242251.html,pare("ε")))//若不是X→ε形式,则

{

//分解右部为单个符号

vectorstrtemp1=GetSingleSign(GR_Array[i].RightPart);

for(int k=0;k

{

string tempvt=strtemp1[k];

if(!(tempvt[0]>='A'&&tempvt[0]<='Z'))//若不是大写字母,则是终结符

{

if(!FindSign(tempvt,EndSign)) //若是新的终结符,则添加进去

EndSign.push_back(tempvt);

}

else //否则,则为非终结符

{

if(!FindSign(tempvt,NoEndSign)) //若是新的终结符,则添加进去

NoEndSign.push_back(tempvt);

}

}

}

}

if(!NoEndSign.empty())

StartSign=NoEndSign[0]; // 文法开始符号默认为遇到的第一个非终结符

}

//识别0型文法(所有产生式的左部含有非终结符):

bool CGrammar::IS_0_Grammar()

{

int CapitalNumInLeftPart; //某产生式左部的非终结符的数目,即大写字母数int count=0;

for(int i=0;i

{

CapitalNumInLeftPart=0;

for(int j=0;j

{

string temp3=GR_Array[i].LeftPart.substr(j,1);

if(FindSign(temp3,NoEndSign))

CapitalNumInLeftPart++;

}

if(CapitalNumInLeftPart<=0)

return false; //表示当前产生式的左部没有非终结符,则直接终止}

return true; //是0 型文法

}

//识别1型文法(即上下文有关文法,所有产生式的左部长度<=右部长度):

bool CGrammar::IS_1_Grammar()

{

for(int i=0;i

if(GR_Array[i].LeftPart.length()>GR_Array[i].RightPart.length()) //若左部大于右部,return false;

return true;

}

//识别2型文法(即上下文无关文法:左部长度=1,且左部为非终结符Vn)

bool CGrammar::IS_2_Grammar()

{

for(int i=0;i

{

if(GR_Array[i].LeftPart.length()!=1)//若左部长度不为1

return false;

string temp4=GR_Array[i].LeftPart.substr(0,1);

if(!FindSign(temp4,NoEndSign))

return false;

}

return true;

}

//识别3型文法(即正规文法,分为右线性、左线性)

bool CGrammar::IS_3_Grammar()

{

int Flag1=0,Flag2=0; //分别代表右线性A→aB、左线性A→Ba 的个数

for(int i=0;i

{

if(GR_Array[i].RightPart.length()==1) //右部字符个数等于1,

{

string temp1=GR_Array[i].RightPart.substr(0,1);//取右部字符

if(!FindSign(temp1,NoEndSign)&&!FindSign(temp1,EndSign))// 若右部既不是终结符

//,即不满足A→a形式,也不是非终结符,既不满足A→B形式,则

return false;

}

else if(GR_Array[i].RightPart.length()==2) //判断是否右线性A→aB或左线性A→Ba形式

{

if((GR_Array[i]https://www.360docs.net/doc/d716242251.html,pare("ε")))//若不是X→ε形式,则

{

string temp2=GR_Array[i].RightPart.substr(0,1);

string temp3=GR_Array[i].RightPart.substr(1,1);

if(FindSign(temp2,EndSign)// 若右部首字符是终结符

&&FindSign(temp3,NoEndSign)) // 次首字符为非终结符,即A→aB

Flag1++;

else if(FindSign(temp2,NoEndSign)// 若右部首字符是非终结符

&&FindSign(temp3,EndSign)) // 次首字符为终结符,即A→Ba

Flag2++;

else

return false;

}

}

else

return false;

}

if(Flag1>0&&Flag2>0) //同时含有右线性、左线性

return false;

else

return true;

}

//判断文法的类型

void CGrammar::GrammarType()

{

GrammarTypeNum=-1;

if(IS_0_Grammar())

{

GrammarTypeNum=0;

if(IS_1_Grammar())

{

GrammarTypeNum=1;

if(IS_2_Grammar())

{

GrammarTypeNum=2;

if(IS_3_Grammar())

GrammarTypeNum=3;

}

}

}

}

// 判断是否含有“直接”左递归

void CGrammar::ExistDirectLeftRecursion()

{

for(int i=0;i

{

string temp1=GR_Array[i].LeftPart.substr(0,1);

if(FindSign(temp1,NoEndSign)&& //若左部首字符为指定非终结符NoEndSign[i],GR_Array[i].LeftPart[0]==GR_Array[i].RightPart[0]) // ,且存在直接左递归{

IsDirectLeftRecursion=true;

return;

}

}

IsDirectLeftRecursion=false;

}

// 判断是否含有“间接”左递归

void CGrammar::ExistIndirectLeftRecursion()

{

vector::iterator Iter1,Iter2;

vectorGR_ArrayTemp;//备份用:

GR_ArrayTemp.reserve(1000);

GR_ArrayTemp=GR_Array; //此处仅是判断是否存在左递归,

//所有不能对原产生式进行破坏性操作,而只能对备份数据操作。

for(int i=0;i

{

for(int j=0;j

{

for(Iter1=GR_ArrayTemp.begin();Iter1!=GR_ArrayTemp.end();Iter1++)

{

string temp1=(*Iter1).LeftPart.substr(0,1);

string temp2=(*Iter1).RightPart.substr(0,1);

if(!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[i])//左部首字符为Pi,

&&!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[j]))//右部首字符为Pj,即形如Pi→Pjγ

//注:这里,γ是终结符,i、j是下标。

{

// 寻找所有形为:Pj→...的产生式:

for(Iter2=GR_ArrayTemp.begin();Iter2!=GR_ArrayTemp.end();Iter2++)

{

string temp3=(*Iter2).LeftPart.substr(0,1);

if(!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[j]))//若某产生式的左部为Pj,则替换,

{

CreateFormula temp;

temp=(*Iter1);

temp.RightPart.replace(0,1,(*Iter2).RightPart);

GR_ArrayTemp.push_back(temp);

}

}

//当替换完成后,需要删除第k条产生式((*Iter1)),

Iter1=GR_ArrayTemp.erase(Iter1);

}

}

}

}

for(Iter1=GR_ArrayTemp.begin();Iter1!=GR_ArrayTemp.end();Iter1++)

{

string temp1=(*Iter1).LeftPart.substr(0,1);

if(FindSign(temp1,NoEndSign)&& //若左部首字符为指定非终结符NoEndSign[i],(*Iter1).LeftPart[0]==(*Iter1).RightPart[0]) // ,且存在直接左递归

{

IsIndirectLeftRecursion=true;

return ;

}

}

IsIndirectLeftRecursion=false;

}

// 能否删除左递归

bool CGrammar::EnableDelLeftRecursion()

{

int Sum=GR_Array.size();

for(int i=0;i

{

if(GR_Array[i].LeftPart[0]==GR_Array[i].RightPart[0]// 若“左部首字符=右部首字符,”

&&GR_Array[i].RightPart.length()==1) // “且右部长度=1”,即“满足P→P回路”。

{

// 则再检查是否存在与P→P有相同左部首字符、且右部首字符=ε的产生式,即P→ε形式

for(int j=0;j

if(GR_Array[j].LeftPart[0]==GR_Array[i].LeftPart[0]//相同的左部首字符,

&&GR_Array[j].RightPart[0]=='ε') // 且右部首字符=ε

return false;//若存在这样的情况,则无法消除左递归。退出!

}

}

return true;

}

//构造新的非终结符,例如“E”变换为“E'”:

string CGrammar::CreateNewVn(string NoEndSigntemp)

{

return NoEndSigntemp+'\'';

}

//消除直接“左”递归

void CGrammar::DelDirectLeftRecursion(int i)

{

string c;

vector::iterator Iter1,Iter2; // 迭代器1

for(Iter1=GR_Array.begin();Iter1!=GR_Array.end();Iter1++)

{

string temp1=(*Iter1).LeftPart.substr(0,1);

if(!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[i])&& //若左部首字符为指定非终结符NoEndSign[i],(*Iter1).LeftPart[0]==(*Iter1).RightPart[0]) // ,且存在直接左递归{

//(1):修改产生式P→a为P→aP'

c=CreateNewVn(NoEndSign[i]);

for(Iter2=GR_Array.begin();Iter2!=GR_Array.end();Iter2++)

{

string temp2=(*Iter2).LeftPart.substr(0,1);

if(!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[i])&& //对于左部首字符同样=指定非终结符NoEndSign[i],

(*Iter2).LeftPart[0]!=(*Iter2).RightPart[0])//但左部首字符不等于右部首字符,即不是左递归,例如P→a

(*Iter2).RightPart+=c;//在右部尾端添补新的非终结符号,即:P→a变为P→aP' }

//(2):对递归式(*Iter1)进行改造:

(*Iter1).LeftPart=c; //①左部变为新的非终结符号c,即P'

(*Iter1).RightPart=(*Iter1).RightPart.erase(0,1); //②右部删除首字符,即“去除”原递归字符

(*Iter1).RightPart+=c; //③在右部尾端添加c,即P'

// 例如:P→Pa变为P'→aP',即变为右递归。

//(3):为文法增加一条空“ε”产生式:

CreateFormula temp;

temp.LeftPart=(*Iter1).LeftPart;

temp.RightPart="ε";

GR_Array.push_back(temp);

//(4):将P'加入非终结符集:

if(!FindSign(c,NoEndSign))

NoEndSign.push_back(c);

}

}

}

//消除间接“左”递归

bool CGrammar::DelIndirectLeftRecursion()

{

//首先判断能否消除左递归

if(!EnableDelLeftRecursion())

return false;

vector::iterator Iter1,Iter2;

for(int i=0;i

{

for(int j=0;j

{

for(Iter1=GR_Array.begin();Iter1!=GR_Array.end();Iter1++)

{

string temp1=(*Iter1).LeftPart.substr(0,1);

string temp2=(*Iter1).RightPart.substr(0,1);

if(!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[i])//左部首字符为Pi,

&&!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[j]))//右部首字符为Pj,即形如Pi→Pjγ

//注:这里,γ是终结符,i、j是下标。

{

// 寻找所有形为:Pj→...的产生式:

for(Iter2=GR_Array.begin();Iter2!=GR_Array.end();Iter2++)

{

string temp3=(*Iter2).LeftPart.substr(0,1);

if(!https://www.360docs.net/doc/d716242251.html,pare(NoEndSign[j]))//若某产生式的左部为Pj,则替换,

{

CreateFormula temp;

temp=(*Iter1);

temp.RightPart.replace(0,1,(*Iter2).RightPart);

GR_Array.push_back(temp);

}

}

//当替换完成后,需要删除第k条产生式((*Iter1)),

Iter1=GR_Array.erase(Iter1);

}

}

}

DelDirectLeftRecursion(i); //消除直接左递归

}

GR_Number=GR_Array.size();

return true;

}

//判断是否含有多余产生式(规则)

// 多余规则(即无用产生式)满足下述条件:

//(1)每一个非终结符号A(S除外)必须在某句型中(规则右端)出现,

// 即若某条规则左部的非终结符A不在任何其他规则右部出现,

// 那么所有的推导始终不会用到此规则,也就是说A是“不可到达”的。

// (2)非终结符A必须推出终结符串t来。否则在推导句子的过程中,

// 一旦使用了该规则,将推不出任何终结符号串,称为“不可终止”的。

bool CGrammar::HavingRedundance()

{

vectorNoEndSignTemp;//保存能由开始符号到达的非终结符的集合

vector::iterator Iter1,Iter3; // 迭代器

vector::iterator Iter2; // 迭代器

//第(1)种情况:删除“不可达”产生式

NoEndSignTemp.reserve(1000);//为NoEndSignTemp与分配存储空间,否则可能出错。

//(A)求解能由开始符号到达的非终结符的集合:

NoEndSignTemp.push_back(GR_Array[0].LeftPart);

for(Iter1=NoEndSignTemp.begin();Iter1!=NoEndSignTemp.end();Iter1++)

for(Iter2=GR_Array.begin();Iter2!=GR_Array.end();Iter2++)

{

if(NoEndSign==NoEndSignTemp)

return false;

if(!((*Iter2)https://www.360docs.net/doc/d716242251.html,pare(*Iter1)))

{

for(Iter3=NoEndSign.begin();Iter3!=NoEndSign.end();Iter3++)

{

int L=(*Iter2).RightPart.length();

if((*Iter2).RightPart.find(*Iter3)

{

//,且该非终结符*Iter3不在NoEndSignTemp中,则

if(!FindSign(*Iter3,NoEndSignTemp))

NoEndSignTemp.push_back(*Iter3);// 添加到NoEndSignTemp中

}

}

}

}

//(B)删除不能由开始符号到达的非终结符引导的产生式:

for(Iter3=NoEndSign.begin();Iter3!=NoEndSign.end();Iter3++)

if(!FindSign(*Iter3,NoEndSignTemp)) //若非终结符*Iter3不在NoEndSignTemp中,则return true;

return false;

}

//文法化简,即删除无用产生式。

void CGrammar::MiniGrammar()

{

vectorNoEndSignTemp;//保存能由开始符号到达的非终结符的集合

vector::iterator Iter1,Iter3; // 迭代器

vector::iterator Iter2; // 迭代器

//第(1)种情况:删除“不可达”产生式

NoEndSignTemp.reserve(1000);//为NoEndSignTemp与分配存储空间,否则可能出错。

//(A)求解能由开始符号到达的非终结符的集合:

NoEndSignTemp.push_back(GR_Array[0].LeftPart);

for(Iter1=NoEndSignTemp.begin();Iter1!=NoEndSignTemp.end();Iter1++)

for(Iter2=GR_Array.begin();Iter2!=GR_Array.end();Iter2++)

{

if(NoEndSign==NoEndSignTemp)

return;

if(!((*Iter2)https://www.360docs.net/doc/d716242251.html,pare(*Iter1)))

{

for(Iter3=NoEndSign.begin();Iter3!=NoEndSign.end();Iter3++)

{

int L=(*Iter2).RightPart.length();

if((*Iter2).RightPart.find(*Iter3)

{

//,且该非终结符*Iter3不在NoEndSignTemp中,则

if(!FindSign(*Iter3,NoEndSignTemp))

NoEndSignTemp.push_back(*Iter3);// 添加到NoEndSignTemp中

}

}

}

}

//(B)删除不能由开始符号到达的非终结符引导的产生式:

for(Iter3=NoEndSign.begin();Iter3!=NoEndSign.end();Iter3++)

{

if(!FindSign(*Iter3,NoEndSignTemp)) //若非终结符*Iter3不在NoEndSignTemp中,

{

//删除所有左部为*Iter3的产生式:

for(Iter2=GR_Array.begin();Iter2!=GR_Array.end();Iter2++)

if(!((*Iter2)https://www.360docs.net/doc/d716242251.html,pare(*Iter3)))

Iter2=GR_Array.erase(Iter2);

}

}

}

//分解字符串为单个符号(包括形式为E'的符号)

vector CGrammar::GetSingleSign(string str)

{

vector temp1;

for(int i=0;i

{

string temp2=str.substr(i,1);

if(i

temp2+=str[++i];

temp1.push_back(temp2);

}

return temp1;

}

//显示文法(四元组形式)

void CGrammar::DisplayGrammar()

{

int sum=NoEndSign.size(),i;

//显示开始符号

cout<<"G["<

//显示非终结符

cout<

for(i=1;i

{

cout<<",";

cout<

}

cout<<"},{";

//显示终结符

sum=EndSign.size();

cout<

for(i=1;i

{

cout<<",";

cout<

}

cout<<"},P,"<

//显示产生式集合:

vector::iterator Iter1;

for(Iter1=GR_Array.begin();Iter1!=GR_Array.end();Iter1++)

cout<<" "<

<

}

//返回文法类型

int CGrammar::GetGrammarType()

{

return GrammarTypeNum;

}

//返回是否存在左递归的标识

bool CGrammar::GetHavingLeftRecursion()

{

return IsDirectLeftRecursion||IsIndirectLeftRecursion; }

//返回非终结符

vectorCGrammar::GetVN()

{

return NoEndSign;

}

//返回终结符

vectorCGrammar::GetVT()

{

return EndSign;

}

// 返回产生式数据

vectorCGrammar::GetCF()

{

return GR_Array;

}

// 返回开始符号

string CGrammar::GetStartSign()

{

return StartSign;

}

(二)CRGrammar类

1.头文件RGrammar.h

#include "Grammar.h"

#define PRO_MAX_LEN 20480 // 源程序最大长度

// 为便于描述,这里我们将有关单词归纳为如下五类:#define KEY 1 // 关键字

#define OPERA T 2 // 运算符

#define DIVIDE 3 // 界符

#define ID 4 // 标识符

#define CONST 5 // 常量

struct SignProperty // 属性字

{

int SignKind; // 单词种别

string SignV alue; // 单词值

};

struct tp // 临时用

{

string tp_start,tp_end;

};

struct MoveFunction // 状态转移函数

{

string Startstate; // 当前状态

string Endstate; // 目标状态

string InputSign; // 输入符号

};

struct FA // 有限自动机结构,五元组形式

{

vectorQState; // 状态集

vectorSigema; // 字母表

string q0; // 初始状态

vectorZState; // 终态集

vectorFAf;// 转移函数

};

struct NFAStateMovetable // NFA确定化表

{

vectorNFAStart;// 当前状态集

string NFAinputsign; // 输入符号

vectorNFAEnd; // 目标状态集

};

#include "Grammar.h"

class CRGrammar:public CGrammar

{

public:

CRGrammar();

virtual ~CRGrammar();

public:

void MiniCLex(char *Filename);// 小型C++语言的词法分析(演示程序)

void FA2RG(); // 有限自动机(FA)转换为正规文法(右线性)

void SetFA(char *Filename); // 从txt文件中获取FA信息

void DFA2MFA(); // DFA化简

void NFA2DFA(); // NFA确定化

void DisplayNFA(); // 显示NFA

void DisplayDFA(); // 显示DFA

void DisplayMFA(); // 显示MFA

void RG2NFA(); // 正规文法(RG)转换为有限自动机(NFA)private:

void DiplayFA(FA fa); // 显示有限自动机

void CreateMovefunction(); // 构造NFA的转移函数

bool IsRightlineGrammar(); // 判断是否为右线性文法

void KongClosure(string I_state,vector&EClosure);// 求单符号的闭包

void I_KongClosure(vectorI_state,vector&EClosure);// 求符号集的闭包void Ia_KongClosure(vectorI_state,vector&EClosure,string sign);// 求符号

集I_state的a-闭包

bool StartEQEnd(vectorlist1,vectorlist2);// 判断两个串等价

bool HaveEndstate(vectorlist1,vectorlist2);// 判断list1中含有list2的元素vectorFA1(char *line); // 从一串字符中提取FA信息

void AClosure(string I_state,vector&EClosure,string sign);// 求单符号的a-闭包void DelOtioseState(); // 删除多余状态

bool IsNFA(); // 判断一个FA是否为NFA

void CarveState(); // 合并等价状态

int stringToNum(const string& str); //string 转为int

bool MiniC_Pretreatment(); // 滤除注释等

bool MiniC_InputSource(char *Filename); // 输入源码

void MiniC_CreateErrortable(); // 构造错误码表

bool MiniC_Lexanalyse(); // 词法分析程序

int MiniC_InKeyTable(string str); // 在关键字表中查找str

void MiniC_CreateKeyTable(); // 构造关键字表

int MiniC_InsertSigntable(string str,vector&table);// 插入表

void MiniC_DisplayPropertytable(); // 显示属性值表

void MiniC_DisplaySigntable(); // 显示符号表

string MiniC_GetSignIntable(int No,vectortable);

private:

FA NFA; // 非确定有限自动机NFA

FA DFA; // 确定的有限自动机DFA

FA MFA; // 最小化的有限自动机MFA

string proBuffer; // 存储程序代码的全局缓冲区

vectorErrortable; // 错误代码表

vectorKeytable; // 关键字表

vectorSigntable; // 标识符表

vectorConsttable; // 常量表

vectorPropertytable; // 属性字表

};

2.实现文件RGrammar.cpp

#include "RGrammar.h"

// 正规文法(RG)转换为NFA

void CRGrammar::RG2NFA()

{

if(IsRightlineGrammar()) // 要求必须是右线性3型文法,

{

NFA.Sigema=GetVT(); //(1)设置字母表:∑

NFA.q0=GetStartSign(); //(2)设置初始状态:q0

NFA.QState=GetVN(); //(3)设置状态集:Q

string temp("Z");

NFA.QState.push_back(temp);

if(!NFA.ZState.empty())

NFA.ZState.clear(); //清空

NFA.ZState.push_back(temp); //(4)设置终态集:Z

CreateMovefunction(); //(5)设置FA的转移函数

}

}

// 构造NFA的转移函数

void CRGrammar::CreateMovefunction()

{

if(!IsRightlineGrammar())

return ;

if(!NFA.FAf.empty())

NFA.FAf.clear(); //清空

MoveFunction tempMove; // 临时转移函数

for(int i=0;i

{

string tempLeft=GR_Array[i].LeftPart.substr(0,1);//保存左部

if(!FindSign(tempLeft,NFA.QState))

continue;

if(!(GR_Array[i]https://www.360docs.net/doc/d716242251.html,pare("ε"))) // 若右部是A→ε形式,则

{

tempMove.Startstate =tempLeft;

tempMove.InputSign ="ε";

tempMove.Endstate ="Z";

NFA.FAf.push_back(tempMove); //保存状态转移函数

}

else

{

if(GR_Array[i].RightPart.length()==1) // 若右部字符个数等于1,

{

string temp1=GR_Array[i].RightPart.substr(0,1);//取右部字符

if(FindSign(temp1,NFA.Sigema))// 若右部字符是终结符,即满足A→a形式

{

tempMove.Startstate =tempLeft;

tempMove.InputSign =temp1;

tempMove.Endstate ="Z";

NFA.FAf.push_back(tempMove); // 保存状态转移函数

}

else if(FindSign(temp1,NFA.QState))// 若右部字符是非终结符,即A→B形式

{

tempMove.Startstate =tempLeft;

tempMove.InputSign ="ε";

tempMove.Endstate =temp1;

NFA.FAf.push_back(tempMove); //保存状态转移函数

}

}

else if(GR_Array[i].RightPart.length()==2) //判断是否为右线性A→aB

{

string temp2=GR_Array[i].RightPart.substr(0,1); // 取右部首字符

string temp3=GR_Array[i].RightPart.substr(1,1); // 取右部第2个字符

if(FindSign(temp2,NFA.Sigema)// 若右部首字符是终结符

&&FindSign(temp3,NFA.QState)) // 次字符为非终结符,即A→aB {

tempMove.Startstate =tempLeft;

tempMove.InputSign =temp2;

tempMove.Endstate =temp3;

NFA.FAf.push_back(tempMove); //保存状态转移函数

}

}

}

}

}

// 判断是否为右线性正规文法

bool CRGrammar::IsRightlineGrammar()

{

if(GrammarTypeNum!=3) // 首先必须是3型文法,如果不是,则return false;

for(int i=0;i

{

if((GR_Array[i]https://www.360docs.net/doc/d716242251.html,pare("ε")))// 若右部不是A→ε形式,则

{

if(GR_Array[i].RightPart.length()==1) // 若右部字符个数等于1,

{

string temp1=GR_Array[i].RightPart.substr(0,1);//取右部字符

// 若右部既不是终结符,即不满足A→a形式,也不是非终结符,既不满足A→B形式,则if(!FindSign(temp1,NoEndSign)&&!FindSign(temp1,EndSign))

return false;

}

else if(GR_Array[i].RightPart.length()==2) //判断是否为右线性A→aB

{

string temp2=GR_Array[i].RightPart.substr(0,1); // 取右部首字符

string temp3=GR_Array[i].RightPart.substr(1,1); // 取右部第2个字符

if(!FindSign(temp2,EndSign) // 若右部首字符是终结符

||!FindSign(temp3,NoEndSign))// 次字符为非终结符,即满足A→aB

return false;

}

else

return false;

}

}

return true;

}

// 显示FA:五元组形式

void CRGrammar::DiplayFA(FA fa)

{

cout<<"五元组如下:"<

int i;

cout<

for(i=0;i

cout<

cout<

for(i=0;i

cout<

cout<

for(i=0;i

cout<

<

cout<

cout<

for(i=0;i

cout<

cout<

}

void CRGrammar::DisplayNFA()

{

if(IsNFA())

{

cout<

DiplayFA(NFA);

}

}

void CRGrammar::DisplayDFA()

{

cout<

DiplayFA(DFA);

}

void CRGrammar::DisplayMFA()

{

cout<

DiplayFA(MFA);

}

//求单符号的ε-闭包

void CRGrammar::KongClosure(string I_state,vector&EClosure)

{

if(!FindSign(I_state,EClosure)) // 若I_state尚未添入闭包中,则EClosure.push_back(I_state); // 加入闭包。

for(int i=0;i

{

if(!(I_https://www.360docs.net/doc/d716242251.html,pare(NFA.FAf[i].Startstate)) // 找到当前状态为I_state,

&&(!(NFA.FAf[i]https://www.360docs.net/doc/d716242251.html,pare("ε")))) // 且输入符号为sign,则{

if(!FindSign(NFA.FAf[i].Endstate,EClosure)) // 若尚未添入闭包中,则

{

EClosure.push_back(NFA.FAf[i].Endstate); // 加入闭包

KongClosure(NFA.FAf[i].Endstate,EClosure); // 递归求解

}

}

}

}

//求符号集的ε-闭包

/*

参数EClosure:保存求得的闭包

参数I_state:所求对象,即是求I_state得闭包

实验一 词法分析

实验一词法分析 有如下算术运算文法: 1) E->E+T 2) E->E-T 3) E->T 4) T->T*F 5) T->T/F 6) T->F 7) F->(E) 8) F->I 9) I->十进制实数|十进制整数|十六进制实数| 十六进制整数|八进制实数|八进制整数 10) 十进制实数-> (0|(1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9) *).(0|1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9) * 11) 八进制实数-> 0(0|1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)* .(0|1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7) * 12) 十六进制实数-> 0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)* .(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3| 4|5|6|7|8|9|a|b|c|d|e|f) * 13) 十进制整数-> 0 | (1|2|3|4|5|6|7|8|9)(0|1|2|3|4|5|6|7|8|9) * 14) 八进制整数-> 0(0|1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7) * 15)十六进制整数-> 0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f) (0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f) * 单词分类: 运算符:+ - * / () 常数: 十进制实数 十进制整数 十六进制实数 十六进制整数 八进制实数 八进制整数 1. 实验目的 实现一个词法分析程序,将输入字符串流分解成 单词流供语法分析使用。 2. 实验要求 输入算术运算式,输出分解后的单词流,例如: 输入(0124.3+0x35a.4f)*12 输出:

实验一词法分析实验报告

实验一词法分析 一、实验目的 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 二、实验要求 使用一符一种的分法 关键字、运算符和分界符可以每一个均为一种 标识符和常数仍然一类一种 三、实验内容 功能描述: 1、待分析的简单语言的词法 (1)关键字: begin if then while do end (2)运算符和界符: := + –* / < <= <> > >= = ; ( ) # (3)其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义: ID=letter(letter| digit)* NUM=digit digit * (4)空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。 2、各种单词符号对应的种别码 图 1

程序结构描述: 图 2 四、实验结果 输入begin x:=9: if x>9 then x:=2*x+1/3; end # 后经词法分析输出如下序列:(begin 1)(x 10)(:17)(= 18)(9 11)(;26)(if 2)……如图3所示:

图3 输入private x:=9;if x>0 then x:=2*x+1/3; end#后经词法分析输出如下序列:(private 10)(x 10)(:17)(= 18)(9 11)(;26)(if 2)……如图4所示: 图4 显然,private是关键字,却被识别成了标示符,这是因为图1中没有定义private关键字的种别码,所以把private当成了标示符。 输入private x:=9;if x>0 then x:=2*x+1/3; @ end#后经词法分析输出如下序列:(private 10)(x 10)(:17)(= 18)(9 11)(;26)(if 2)……如图5所示

实验1 词法分析器

青岛理工大学 实 验 报 告 实验课程: 编译原理 实验日期: 2014 年 5月28 日 交报告日期:2014 年6月4日 成绩: 实验地点:现代教育技术中心101(计算机实验室) 计算机工程 学院,计算机科学与技术 专业, 班级:计算113 实验指导教师: 批阅教师: 一、实验目的 设计、编制并调试一个词法分析程序,加深对词法分析原理的理解。 二、实验要求 1. 待分析的简单语言的词法 1) 关键字: begin if then while do end 所有关键字都是小写。 2) 运算符和界符: : = + – * / < <= <> > >= = ; ( ) # 3) 其他单词是标识符(ID )和整型常数(NUM ),通过以下正规式定义: ID=letter (letter| digit )* NUM=digit digit * 4) 空格由空白、制表符和换行符组成。空格一般用来分隔ID 、NUM,运算符、界符和关 键字,词法分析阶段通常被忽略。

2.各种单词符号对应的种别码 3.词法分析程序的功能 输入:所给文法的源程序字符串。 输出:二元组(syn,token或sum)构成的序列。 其中:syn为单词种别码;token为存放的单词自身字符串;sum为常数。 例如:对源程序 begin x:=9; if x>0 then x:=2*x+1/3; end# 经词法分析后输出如下序列:(1,beigin) (10,x) (18,:=) (11,9) (26,;) (2,if)......

三、算法思想 1.主程序示意图 主程序示意图如下所示: 其中初值包括如下两个方面。 1)关键字表的初值。 关键字作为特殊标识符处理,把它们预先安排在关键字表,当扫描程序识别出标识符时,查关键字表。若查到匹配的单词,则该单词为关键字,否则为一般标识符。关键字表作为一个字符串数组,其描述如下: char *rwtab[22] = {"begin","if","else","then","while","do","for","switch","case", "until","break","goto","constant","return", "int","float","double","string","char","short","long","end"}; 2)程序中的主要变量为syn,token和sum。 2.扫描子程序的算法思想 首先设置3个变量: ①token用于存放构成单词符号的字符串; ②sum用于存放整型单词; ③syn用于存放单词符号的种别码。

实验一词法分析实验报告

实验一词法分析实验报告

实验一词法分析 一、实验目的 通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。 编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 二、实验要求 使用一符一种的分法 关键字、运算符和分界符可以每一个均为一种标识符和常数仍然一类一种 三、实验内容 功能描述: 1、待分析的简单语言的词法 (1)关键字:

begin if then while do end (2)运算符和界符: := + –* / < <= <> > > = = ; ( ) # (3)其他单词是标识符(ID)和整型常数(NUM),通过以下正规式定义: ID=letter(letter| digit)* NUM=digit digit * (4)空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM,运算符、界符和关键字,词法分析阶段通常被忽略。 2、各种单词符号对应的种别码 图 1

程序结构描述: 是 否 是 调用scanner() 字母 数 其他 运算符、 符号 界符等符号 否 是 图 2 四、实验结果 输入begin x:=9: if x>9 then x:=2*x+1/3; end # 后经词法分析输出如 变量忽略 是否输入返 拼数 syn=11返 对不同报拼字是否关syn 为对syn=10

实验1-3-《编译原理》词法分析程序设计方案

实验1-3 《编译原理》S语言词法分析程序设计方案 一、实验目的 了解词法分析程序的两种设计方法之一:根据状态转换图直接编程的方式; 二、实验内容 1.根据状态转换图直接编程 编写一个词法分析程序,它从左到右逐个字符的对源程序进行扫描,产生一个个的单词的二元式,形成二元式(记号)流文件输出。在此,词法分析程序作为单独的一遍,如下图所示。 具体任务有: (1)组织源程序的输入 (2)拼出单词并查找其类别编号,形成二元式输出,得到单词流文件 (3)删除注释、空格和无用符号 (4)发现并定位词法错误,需要输出错误的位置在源程序中的第几行。将错误信息输出到屏幕上。 (5)对于普通标识符和常量,分别建立标识符表和常量表(使用线性表存储),当遇到一个标识符或常量时,查找标识符表或常量表,若存在,则返回位置,否则返回0并且填写符号表或常量表。 标识符表结构:变量名,类型(整型、实型、字符型),分配的数据区地址 注:词法分析阶段只填写变量名,其它部分在语法分析、语义分析、代码生成等阶段逐步填入。 常量表结构:常量名,常量值 三、实验要求 1.能对任何S语言源程序进行分析 在运行词法分析程序时,应该用问答形式输入要被分析的S源语言程序的文件名,然后对该程序完成词法分析任务。 2.能检查并处理某些词法分析错误 词法分析程序能给出的错误信息包括:总的出错个数,每个错误所在的行号,错误的编号及错误信息。 本实验要求处理以下两种错误(编号分别为1,2): 1:非法字符:单词表中不存在的字符处理为非法字符,处理方式是删除该字符,给出错误信息,“某某字符非法”。 2:源程序文件结束而注释未结束。注释格式为:/* …… */ 四、保留字和特殊符号表

TEST语言 -语法分析,词法分析实验报告

编译原理实验报告 实验名称:分析调试语义分析程序 TEST抽象机模拟器完整程序 保证能用!!!!! 一、实验目的 通过分析调试TEST语言的语义分析和中间代码生成程序,加深对语法制导翻译思想的理解,掌握将语法分析所识别的语法范畴变换为中间代码的语义翻译方法。 二、实验设计 程序流程图

extern int TESTScan(FILE *fin,FILE *fout); FILE *fin,*fout; //用于指定输入输出文件的指针 int main() { char szFinName[300]; char szFoutName[300]; printf("请输入源程序文件名(包括路径):"); scanf("%s",szFinName); printf("请输入词法分析输出文件名(包括路径):"); scanf("%s",szFoutName); if( (fin = fopen(szFinName,"r")) == NULL) { printf("\n打开词法分析输入文件出错!\n"); return 0; } if( (fout = fopen(szFoutName,"w")) == NULL) { printf("\n创建词法分析输出文件出错!\n"); return 0; } int es = TESTScan(fin,fout); fclose(fin); fclose(fout); if(es > 0) printf("词法分析有错,编译停止!共有%d个错误!\n",es); else if(es == 0) { printf("词法分析成功!\n"); int es = 0;

实验一词法分析

实验一词法分析 1.实验要求 (1)从源程序文件中读取有效字符并将其转换成二元组内部表示形式输出。 (2)掌握词法分析的实现方法。 (3)实验时间4学时。 (4)实验完成后,要提交实验报告(包括源程序清单)。 2.实验内容 2.1主程序设计考虑: 主程序的说明部分为各种表格和变量安排空间(关键字和特殊符号表)。 id 和ci 数组分别存放标识符和常数;还有一些为造表填表设置的变量。 主程序的工作部分建议设计成便于调试的循环结构。每个循环处理一个单词;调用词法分析过程;输出每个单词的内部码(种别编码,属性值)。建议从文件中读取要分析的符号串。 2.2词法分析过程考虑 该过程根据输入单词的第一个有效字符(有时还需读第二个字符),判断单词种别,产生种别编码。对于标识符和常数,需分别与标识符表和常数表中已登记的元素相比较,如表中已有该元素,则记录其在表中的位置,如未出现过,将标识符按顺序填入数组id 中,将 三:主流程图如下:

四:实验思路 (1)我首先把这个单词的种类分成了五类,包括:关键字、标识符、常数、算符、界符。然后利用状态转换图进行单词的识别 (2)对于关键字、算符、界符。因为这些单词的个数有限。所以我单独给每个单词一个种别编码。能够做到每个单词的种别编码是不一样的。而对于常数和标识符,我先把它们分别单独的作为一类,然后定义一个二维数组,分别存放这个单词的名称和编码。而这个编码就是这个单词在这个二维数组中的位置;当遇到新的标识符或常数,就把这个单词放入到相应的数组中。 (3)然后构造一个状态转换图的程序。把每次得到的单词先暂时存放在temp 二维数组中。然后用这个临时的二维数组去确定这个单词是何种类别 五:实验代码 using System; using System.Collections.Generic;

实验一、词法分析器(含源代码)

词法分析器实验报告 一、实验目的及要求 本次实验通过用C语言设计、编制、调试一个词法分析子程序,识别单词,实现一个C语言词法分析器,经过此过程可以加深对编译器解析单词流的过程的了解。 运行环境: 硬件:windows xp 软件:visual c++6.0 二、实验步骤 1.查询资料,了解词法分析器的工作过程与原理。 2.分析题目,整理出基本设计思路。 3.实践编码,将设计思想转换用c语言编码实现,编译运行。 4.测试功能,多次设置包含不同字符,关键字的待解析文件,仔细察看运行结果,检测该分析器的分析结果是否正确。通过最终的测试发现问题,逐渐完善代码中设置的分析对象与关键字表,拓宽分析范围提高分析能力。 三、实验内容 本实验中将c语言单词符号分成了四类:关键字key(特别的将main说明为主函数)、普通标示符、常数和界符。将关键字初始化在一个字符型指针数组*key[]中,将界符分别由程序中的case列出。在词法分析过程中,关键字表和case列出的界符的内容是固定不变的(由程序中的初始化确定),因此,从源文件字符串中识别出现的关键字,界符只能从其中选取。标识符、常数是在分析过程中不断形成的。 对于一个具体源程序而言,在扫描字符串时识别出一个单词,若这个单词的类型是关键字、普通标示符、常数或界符中之一,那么就将此单词以文字说明的形式输出.每次调用词法分析程序,它均能自动继续扫描下去,形成下一个单词,直到整个源程序全部扫描完毕,从而形成相应的单词串。 输出形式例如:void $关键字

流程图、程序流程图:

程序: #include #include #include #include //定义关键字 char *Key[10]={"main","void","int","char","printf","scanf","else","if","return"}; char Word[20],ch; // 存储识别出的单词流 int IsAlpha(char c) { //判断是否为字母 if(((c<='z')&&(c>='a'))||((c<='Z')&&(c>='A'))) return 1; else return 0; } int IsNum(char c){ //判断是否为数字 if(c>='0'&&c<='9') return 1; else return 0; } int IsKey(char *Word){ //识别关键字函数 int m,i; for(i=0;i<9;i++){ if((m=strcmp(Word,Key[i]))==0) { if(i==0) return 2; return 1; } } return 0; } void scanner(FILE *fp){ //扫描函数 char Word[20]={'\0'}; char ch; int i,c; ch=fgetc(fp); //获取字符,指针fp并自动指向下一个字符 if(IsAlpha(ch)){ //判断该字符是否是字母 Word[0]=ch; ch=fgetc(fp);

词法分析器实验报告

词法分析器实验报告 词法分析器设计 一、实验目的: 对C语言的一个子集设计并实现一个简单的词法分析器,掌握利用状 态转换图设计词法分析器的基本方法。利用该词法分析器完成对源程 序字符串的词法分析。输出形式是源程序的单词符号二元式的代码, 并保存到文件中。 二、实验内容: 1. 设计原理 词法分析的任务:从左至右逐个字符地对源程序进行扫描,产生一个个单词符号。 理论基础:有限自动机、正规文法、正规式 词法分析器(Lexical Analyzer) 又称扫描器(Scanner):执行词法分析的程序 2. 词法分析器的功能和输出形式 功能:输入源程序、输出单词符号 程序语言的单词符号一般分为以下五种:关键字、标识符、常数、运算符,界符 3. 输出的单词符号的表示形式: 单词种别用整数编码,关键字一字一种,标识符统归为一种,常数一种,各种符号各一种。 4. 词法分析器的结构 单词符号 5. 状态转换图实现

三、程序设计 1.总体模块设计 /*用来存储目标文件名*/ string file_name; /*提取文本文件中的信息。*/ string GetText(); /*获得一个单词符号,从位置i开始查找。并且有一个引用参数j,用来返回这个单词最后一个字符在str的位置。*/ string GetWord(string str,int i,int& j); /*这个函数用来除去字符串中连续的空格和换行 int DeleteNull(string str,int i); /*判断i当前所指的字符是否为一个分界符,是的话返回真,反之假*/ bool IsBoundary(string str,int i); /*判断i当前所指的字符是否为一个运算符,是的话返回真,反之假*/ bool IsOperation(string str,int i);

词法分析的实验报告

《词法分析》实验报告

目录 目录错误!未定义书签。 1 实验目的错误!未定义书签。 2 实验内容错误!未定义书签。 TINY计算机语言描述错误!未定义书签。 实验要求错误!未定义书签。 3 此法分析器的程序实现错误!未定义书签。状态转换图错误!未定义书签。 程序源码错误!未定义书签。 实验运行效果截图错误!未定义书签。 4 实验体会错误!未定义书签。

实验目的 1、学会针对DFA转换图实现相应的高级语言源程序。 2、深刻领会状态转换图的含义,逐步理解有限自动机。 3、掌握手工生成词法分析器的方法,了解词法分析器的内部工作原理。 实验内容 TINY计算机语言描述 TINY计算机语言的编译程序的词法分析部分实现。 从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的内部表示(token)送给语法分析程序。 为了简化程序的编写,有具体的要求如下: 1、数仅仅是整数。 2、空白符仅仅是空格、回车符、制表符。 3、代码是自由格式。 4、注释应放在花括号之内,并且不允许嵌套 TINY语言的单词 要求实现编译器的以下功能 1、按规则拼单词,并转换成二元式形式 2、删除注释行 3、删除空白符(空格、回车符、制表符) 4、列表打印源程序,按照源程序的行打印,在每行的前面加上行号,并且打印出每行包含的记号的二元形式 5、发现并定位错误 词法分析进行具体的要求 1、记号的二元式形式中种类采用枚举方法定义;其中保留字和特殊字符是每个都一个种类,标示符自己是一类,数字是一类;单词的属性就是表示的字符串值。 2、词法分析的具体功能实现是一个函数GetToken(),每次调用都对剩余的字符串分析得到一个单词或记号识别其种类,收集该记号的符号串属性,当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识别出记号的属性值。这样配合语法分析程序的分析需要的记号及其属性,生成一个语法树。

编译原理实验报告(词法分析器语法分析器)

编译原理实验报告

实验一 一、实验名称:词法分析器的设计 二、实验目的:1,词法分析器能够识别简单语言的单词符号 2,识别出并输出简单语言的基本字.标示符.无符号整数.运算符.和界符。 三、实验要求:给出一个简单语言单词符号的种别编码词法分析器 四、实验原理: 1、词法分析程序的算法思想 算法的基本任务是从字符串表示的源程序中识别出具有独立意义的单词符号,其基本思想是根据扫描到单词符号的第一个字符的种类,拼出相应的单词符号。 2、程序流程图 (1 (2)扫描子程序

3

五、实验内容: 1、实验分析 编写程序时,先定义几个全局变量a[]、token[](均为字符串数组),c,s( char型),i,j,k(int型),a[]用来存放输入的字符串,token[]另一个则用来帮助识别单词符号,s用来表示正在分析的字符。字符串输入之后,逐个分析输入字符,判断其是否‘#’,若是表示字符串输入分析完毕,结束分析程序,若否则通过int digit(char c)、int letter(char c)判断其是数字,字符还是算术符,分别为用以判断数字或字符的情况,算术符的判断可以在switch语句中进行,还要通过函数int lookup(char token[])来判断标识符和保留字。 2 实验词法分析器源程序: #include #include #include int i,j,k; char c,s,a[20],token[20]={'0'}; int letter(char s){ if((s>=97)&&(s<=122)) return(1); else return(0); } int digit(char s){ if((s>=48)&&(s<=57)) return(1); else return(0); } void get(){ s=a[i]; i=i+1; } void retract(){ i=i-1; } int lookup(char token[20]){ if(strcmp(token,"while")==0) return(1); else if(strcmp(token,"if")==0) return(2); else if(strcmp(token,"else")==0) return(3); else if(strcmp(token,"switch")==0) return(4); else if(strcmp(token,"case")==0) return(5); else return(0); } void main() { printf("please input string :\n"); i=0; do{i=i+1; scanf("%c",&a[i]);

词法分析实验报告

编译原理实验一 姓名:朱彦荣 学号: 专业:软件工程2 实验题目:词法分析 完成语言:C/C++ 上级系统:VC++6.0 日期:2015/11/7 词法分析 设计题目:手工设计c语言的词法分析器 (可以是c语言的子集) 设计内容: 处理c语言源程序,过滤掉无用符号,判断源程序中单词的合法性,并分解出正确的单词,以二元组形式存放在文件中。 设计目的: 了解高级语言单词的分类,了解状态图以及如何表示并识别单词规则,掌握状态图到识别程序的编程。 结果要求:课程设计报告。 完成日期:第十五周提交报告 一.分析 要想手工设计词法分析器,实现C语言子集的识别,就要明白什么是词法

主要是对源程序进行编译预处理(去除注释、无用的回车换行找到包含的文件等)之后,对整个源程序进行分解,分解成一个个单词,这些单词有且只有五类,分别是标识符、保留字、常数、运算符、界符。以便为下面的语法分析和语义分析做准备。可以说词法分析面向的对象是单个的字符,目的是把它们组成有效的单词(字符串);而语法的分析则是利用词法分析的结果作为输入来分析是否符合语法规则并且进行语法制导下的语义分析,最后产生四元组(中间代码),进行优化(可有可无)之后最终生成目标代码。可见词法分析是所有后续工作的基础,如果这一步出错,比如明明是‘<=’却被拆分成‘<’和‘=’就会对下文造成不可挽回的影响。因此,在进行词法分析的时候一定要定义好这五种符号的集合。下面是我构造的一个C语言子集。 第一类:标识符letter(letter | digit)* 无穷集 第二类:常数(digit)+ 无穷集 第三类:保留字(32) auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while 第四类:界符‘/*’、‘//’、() { } [ ] " " ' 等 第五类:运算符<、<=、>、>=、=、+、-、*、/、^、等 对所有可数符号进行编码: <$,0> ... <+,33> <-,34> <*,35> <<,37> <<=,38> <>,39> <>=,40>

编译原理实验一词法分析实验报告

专题 1_ 词法分析程序构造原理与实现 李若森 13281132计科1301 一、程序功能描述 [功能]: 完成下述正则文法所描述的C语言子集单词符号的词法分析程序。 [要求]: (1)给出各单词符号的类别编码。 (2)能发现输入串的错误。 (3)将分析所得二元序列输出到中间文件中。 [文法]: <标识符>→c|c<余留标识符> <余留标识符>→d|c <无符号数>→d<余留无符号数>|.<小数部分>|d <余留无符号数>→d<余留无符号数>|.<十进小数>|(E|e)<指数部分>|.|d <十进小数>→(E|e)<指数部分>|d<十进小数>|d <小数部分>→d<十进小数>|d <指数部分>→d<余留指数>|(+|-)<整指数>|d <整指数>→d<余留整指数>|d <余留整指数>→d<余留整指数>|d <算数运算符>→+|-|*|/|++|-- <关系运算符>→>|<|==|>=|<=|!= <逻辑运算符>→!|&&|\|\| <位操作运算符>→>>|<< <赋值运算符>→=|+=|-=|*=|/=|%= <特殊运算符>→,|\(|\)|{|} <分隔符>→; 保留字: void int float double if else for do while [说明]: (1)该语言对大小写不敏感 (2)c代表字母a-z&&A-Z,d代表数字0-9。 (3)?/*..*/?以及?//?为程序注释部分。 (4)文法中‘\’为转义字符

二、主要数据结构描述 pair: 用pair来存储单个二元组。其中第一个元素为类型号,第二个为 元素的值。当类型号小于40时代表程序分界符,第二个元素不存储有效信息,用 ?-?代替;类型号为40时是标识符,第二个元素存储标识符字符串;类型号为41 时代表实数,第二个元素存储的是该实数的二进制值。 vector<>: vector是C++中的动态数组,用来存储每一行的二元组。 三、程序结构描述 设计方法: 状态转换图:(DFA M)

Removed_实验一:词法分析器编制实验37

实验一:词法分析器编制实验 一教学重点与实现的关键技术 1.1词法分析概述 人们理解一篇文章(或解析一个程序)起码是在单词级别上来思考的。同样,编译程序也是 在单词的级别上来分析和翻译源程序的。词法分析的任务是:从左至右逐个字符地对源程序进行扫描,产生一个个的单词符号(token),把作为字符串的源程序改造成单词符号串的中间程序。因此,词法分析是编译的基础。 执行词法分析的程序称为词法分析器。构造词法分析器的方法分为手工编制和自动生成(如用著名的词法分析器的自动生成工具Lex自动为某种语言的编译构造词法分析器)两种,本实验要求学生利用所学习掌握的知识手工编制一个小型的词法分析器。 1.2词法分析器的设计要求 1.2.1词法分析器的功能和输出形式 词法分析器的功能是输入源程序,输出单词符号。单词符号是一个程序语言的基本语法符号。程序语言的单词符号一般可分为下列五种。 (1)关键字 是由程序语言定义的具有固定意义的标志符。有时称这些标志符为保留字或基本字。例如,Pascal中的begin,end,if,while都是保留字。这些字通常不用作一般标 志符。 (2)标识符 用来表示各种名字,如变量名、数组名、过程名等等。 (3)常数 常数的类型一般有整型、实型、布尔型、文字型等等。例如, 100,3.14159,TRUE,‘Sample’。 (4)运算符 如+、-、*、/等等 (5)界符 如逗号、分号、括号、/*,*/等等。 一个程序语言的关键字、运算符和界符都是确定的,一般只有几十个或上百个。而对于标识符或常数的使用通常都不加什么限制。 词法分析器所输出的单词符号常常表示成如下的二元式: (单词种别,单词符号的属性值) 单词种别通常用整数编码。一个语言的单词符号如何分种,分成几种,怎么编码,是一个技术性的问题。它主要取决于处理上的方便。标识符一般统归 为一种。常数则宜按类型(整、实、布尔等)分种。关键字可将其全体视为一种, 也可以一字一种。采用一字一种的分法实际处理起来较为方便。运算符可采用一 符一种的分法,但也可以把具有一定共性的运算符视为一种。至于界符一般用一 符一种的分法。 如果一个种别只含一个单词符号,那么,对于这个单词符号,种别编码就完全代表它自身了。若一个种别含有多个单词符号,那么,对于它的每个单词 符号,除了给出种别编码之外,还应给出有关单词符号的属性信息。 单词符号的属性是指单词符号的特性或特征。属性值则是反映特性或特征的值。例如,对于某个标识符,常将存放它的有关信息的符号表项的指针作为其属 性值;对于某个常数,则将存放它的常数表项的指针作为其属性值。 在这里,我们给出一种编码方法(以FORTRAN语言为例): 单词符号编码举例 单词符号种别 编码 内部 值 助记符 DIM1$DIM

词法分析的实验报告

《词法分析》 实验报告 目录 目录 0 1 实验目的 (1) 2 实验内容 (1) 2、1 TINY计算机语言描述 (1) 2、2 实验要求 (1) 3 此法分析器的程序实现 (2) 3、1 状态转换图 (2) 3、2 程序源码 (3) 3、3 实验运行效果截图 (8) 4 实验体会 (8)

1实验目的 1、学会针对DFA转换图实现相应的高级语言源程序。 2、深刻领会状态转换图的含义,逐步理解有限自动机。 3、掌握手工生成词法分析器的方法,了解词法分析器的内部工作原理。 2实验内容 2.1TINY计算机语言描述 TINY计算机语言的编译程序的词法分析部分实现。 从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的内部表示(token)送给语法分析程序。 为了简化程序的编写,有具体的要求如下: 1、数仅仅就是整数。 2、空白符仅仅就是空格、回车符、制表符。 3、代码就是自由格式。 4、注释应放在花括号之内,并且不允许嵌套 TINY语言的单词 2.2实验要求 要求实现编译器的以下功能 1、按规则拼单词,并转换成二元式形式 2、删除注释行 3、删除空白符(空格、回车符、制表符)

4、列表打印源程序,按照源程序的行打印,在每行的前面加上行号,并且打印出每行包含的记号的二元形式 5、发现并定位错误 词法分析进行具体的要求 1、记号的二元式形式中种类采用枚举方法定义;其中保留字与特殊字符就是每个都一个种类,标示符自己就是一类,数字就是一类;单词的属性就就是表示的字符串值。 2、词法分析的具体功能实现就是一个函数GetToken(),每次调用都对剩余的字符串分析得到一个单词或记号识别其种类,收集该记号的符号串属性,当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识别出记号的属性值。这样配合语法分析程序的分析需要的记号及其属性,生成一个语法树。 3、标示符与保留字的词法构成相同,为了更好的实现,把语言的保留字建立一个表格存储,这样可以把保留字的识别放在标示符之后,用识别出的标示符对比该表格,如果存在该表格中则就是保留字,否则就是一般标示符。 3此法分析器的程序实现 3.1状态转换图 图1 TINY语言的确定有限自动机(DFA)

实验1 词法分析程序的设计与开发

编译原理实验报告 一、实验目的 ? 深入理解有限自动机及其应用 ? 掌握词法分析程序的开发。 ? 掌握根据语言的词法规则构造识别其单词的有限自动机的方法 ? 深入理解词法分析程序自动生成原理 二、实验要求 ? 掌握各类单词的形式描述 ?用直接转向法实现有限自动机的代码编写。 ? 独立完成PL0语言的词法分析器。 ? 掌握词法分析程序自动生成工具LEX 的使用。 三、实验原理 词法分析是编译过程的第一阶段。它的任务就是对输入的字符串形式的源程序按顺序进行扫描,根据源程序的词法规则识别具有独立意义的单词(符号),并输出与其等价的Token 序列。 有限自动机是描述程序设计语言单词构成的工具,而状态转换图是有限自动机的比较直观的描述方法。我们使用确定的有限状态自动机,简记为DFA 。 PL/0的语言的词法分析器将要完成以下工作: (1) 跳过分隔符(如空格,回车,制表符); (2) 识别诸如begin ,end ,if ,while 等保留字; (3) 识别非保留字的一般标识符,此标识符值(字符序列)赋给全局量id ,而全局量sym 赋值为SYM_IDENTIFIER 。 (4) 识别数字序列,当前值赋给全局量NUM ,sym 则置为SYM_NUMBER ; (5) 识别:=,<=,>=之类的特殊符号,全局量sym 则分别被赋值为SYM_BECOMES ,SYM_LEQ ,SYM_GEQ 等。 课程名称: 编译原理 班级: 计算1614 实验成绩: 指导教师: 付永钢 姓名: 施心萍 实验项目名称: 实验一 词法分析程序设计与开发 学号: 201621121097 上机实践日期:

南昌大学编译原理实验报告-词法分析器

南昌大学实验报告一 学生姓名:学号:专业班级:网络工程091 实验类型:□验证█综合□设计□创新实验日期:2012-4-12 实验成绩: 实验1 词法分析程序的设计 一、实验目的 掌握计算机语言的词法分析程序的开发方法。 二、实验内容 编制一个能够分析三种整数、标识符、主要运算符和主要关键字的词法分析程序。 三、实验要求 1、根据以下的正规式,编制正规文法,画出状态图; 标识符<字母>(<字母>|<数字字符>)* 十进制整数(0 | (1|2|3|4|5|6|7|8|9))(0|1|2|3|4|5|6|7|8|9)* 如有余力,则进一步分析八进制和十六进制整数,其正规式如下: 八进制整数0(1|2|3|4|5|6|7)(0|1|2|3|4|5|6|7)* 十六进制整数0x(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)(0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f)* 运算符和界符+ - * / > < = <= >= ( ) ;{ } 关键字main if then else while do int (可根据需要添加) 2、根据状态图,设计词法分析函数int scan( ),完成以下功能: 1)从文本文件中读入测试源代码,根据状态转换图,分析出一个单词, 2)以二元式形式输出单词<单词种类,单词属性> 其中单词种类用整数表示: 0:标识符 1:十进制整数 2:八进制整数 3:十六进制整数 运算符和界符,关键字采用一字一符,不编码 其中单词属性表示如下: 标识符,整数由于采用一类一符,属性用单词表示 运算符和界符,关键字采用一字一符,属性为空 3、编写测试程序,反复调用函数scan( ),输出单词种别和属性。 四、实验环境 PC微机 DOS操作系统或Windows 操作系统 Turbo C 程序集成环境或Visual C++ 程序集成环境 五、实验步骤

实验一(词法分析)

实验一词法分析 1. 实验目的 1、 学会针对DFA转换图实现相应的高级语言源程序。 2、 深刻领会状态转换图的含义,逐步理解有限自动机。 3、掌握手工生成词法分析器的方法,了解词法分析器的内部 工作原理。 2. 实验内容 计算机程序设计语言的编译程序的词法分析部分实现。给出算法的流程图及有穷状态自动机的模型(可以用矩阵或者状态图表示)从左到右扫描每行该语言源程序的符号,拼成单词,换成统一的内部表示(token)送给语法分析程序。 为了简化程序的编写,有具体的要求如下: (1) 数仅仅是整数。 (2) 空白符仅仅是空格、回车符、制表符。 (3) 代码是自由格式。 (4) 注释应放在花括号之内,并且不允许嵌套 3. 实验要求 要求实现编译器的以下功能: (1) 按规则拼单词,并转换成二元式形式 (2) 删除注释行 (3) 删除空白符 (空格、回车符、制表符) (4) 列表打印源程序,按照源程序的行打印,在每行的前面加上行号,并且打印出每行包含的记号的二元形式(5) 发现并定位错误 词法分析进行具体的要求: (1) 记号的二元式形式中种类采用枚举方法定义;其中保留字和特殊字符是每个都一个种类,标示符自己是一类, 数字是一类;单词的属性就是表示的字符串值。 (2)词法分析程序当识别一个单词完毕,采用返回值的形式返回符号的种类,同时采用程序变量的形式提供当前识 别出记号的属性值。 (3)标示符和保留字的词法构成相同,为了更好的实现,把语言的保留字建立一个表格存储,这样可以把保留字的

识别放在标示符之后,用识别出的标示符对比该表格, 如果存在该表格中则是保留字,否则是一般标示符。 (选做) 4.实验结果 (1)测试用例 (2)实验结果的屏幕截图 五.实验总结

词法分析实验报告(实验一)

编译原理词法分析实验报告 软工082班 兰洁 200831104044 一、实验内容 二、实验目的 三、实验预期 四、程序规定 五、实验原理 ●程序流程图 ●判别浮点功能扩展流程图 ●状态转换图 六、程序代码与浮点判别功能扩展 七、测试用例 ●扩展功能测试用例; ●普通功能测试用例 八、输出结果 九、实验心得

一、实验内容: 词法分析: 1、识别简单语言的单词符号; 2、识别关键字、标识符、数字、运算符等。并扩展浮点识别功能。 二、实验目的 调试词法分析程序,加深对词法分析原理的理解,掌握编写简单词法分析程序的一般步骤。 三、实验预期结果: 经过调试源代码程序,程序能够成功运行编译,对输入的简单字符串,能够别关键字、标识符、数字、运算符等,并且给出单词符号的对应编码。 四、程序规定: 1、关键字:"function","if","then","while","do","endfunc"; 2、算术运算符:”+”,”-”,”*”,”/”,”=”; 3、关系运算符:"<" ">" "<=" ">=" "==" "!="; 4、界符:"(" ")" ";" "#"; 5、标识符规定以字母开头,字母均为小写; 6、空格和换行符跳过; 7、单词对应编码: 十、实验原理: 输入串--------------------〉词法分析程序————————〉单词符号串 输入:字符串以#结束。 输出:单词的二元组(syn,token/sum)

程序流程图 分析浮点数功能扩展部分流程图:

shuzi()函数

状态转换图 六、程序代码: 备注:红色字体部分为程序功能的功能扩展,使程序能够分析浮点数! 我把浮点数的syn设置为80!

语法分析器实验报告

语法分析器的设计实验报告 一、实验内容 语法分析程序用LL(1)语法分析方法。首先输入定义好的文法书写文件(所用的文法可以用LL(1)分析),先求出所输入的文法的每个非终结符是否能推出空,再分别计算非终结符号的FIRST集合,每个非终结符号的FOLLOW集合,以及每个规则的SELECT集合,并判断任意一个非终结符号的任意两个规则的SELECT 集的交集是不是都为空,如果是,则输入文法符合LL(1)文法,可以进行分析。对于文法: G[E]: E->E+T|T T->T*F|F F->i|(E) 分析句子i+i*i是否符合文法。 二、基本思想 1、语法分析器实现 语法分析是编译过程的核心部分,它的主要任务是按照程序的语法规则,从由词法分析输出的源程序符号串中识别出各类语法成分,同时进行词法检查,为语义分析和代码生成作准备。这里采用自顶向下的LL(1)分析方法。 语法分析程序的流程图如图5-4所示。 语法分析程序流程图 该程序可分为如下几步: (1)读入文法 (2)判断正误 (3)若无误,判断是否为LL(1)文法 (4)若是,构造分析表; (5)由句型判别算法判断输入符号串是为该文法的句型。 三、核心思想 该分析程序有15部分组成: (1)首先定义各种需要用到的常量和变量;

(2)判断一个字符是否在指定字符串中; (3)读入一个文法; (4)将单个符号或符号串并入另一符号串; (5)求所有能直接推出&的符号; (6)求某一符号能否推出‘& ’; (7)判断读入的文法是否正确; (8)求单个符号的FIRST; (9)求各产生式右部的FIRST; (10)求各产生式左部的FOLLOW; (11)判断读入文法是否为一个LL(1)文法; (12)构造分析表M; (13)句型判别算法; (14)一个用户调用函数; (15)主函数; 下面是其中几部分程序段的算法思想: 1、求能推出空的非终结符集 Ⅰ、实例中求直接推出空的empty集的算法描述如下: void emp(char c){ 参数c为空符号 char temp[10];定义临时数组 int i; for(i=0;i<=count-1;i++)从文法的第一个产生式开始查找 { if 产生式右部第一个符号是空符号并且右部长度为1, then将该条产生式左部符号保存在临时数组temp中 将临时数组中的元素合并到记录可推出&符号的数组empty中。 } Ⅱ、求某一符号能否推出'&' int _emp(char c) { //若能推出&,返回1;否则,返回0 int i,j,k,result=1,mark=0; char temp[20]; temp[0]=c; temp[1]='\0'; 存放到一个临时数组empt里,标识此字符已查找其是否可推出空字 如果c在可直接推出空字的empty[]中,返回1 for(i=0;;i++) { if(i==count) return(0); 找一个左部为c的产生式 j=strlen(right[i]); //j为c所在产生式右部的长度 if 右部长度为1且右部第一个字符在empty[]中. then返回1(A->B,B可推出空) if 右部长度为1但第一个字符为终结符,then 返回0(A->a,a为终结符) else

相关文档
最新文档